diff options
-rw-r--r-- | bta/hd/bta_hd_act.c | 1329 | ||||
-rw-r--r-- | bta/hd/bta_hd_api.c | 495 | ||||
-rw-r--r-- | bta/hd/bta_hd_int.h | 417 | ||||
-rw-r--r-- | bta/hd/bta_hd_main.c | 587 | ||||
-rw-r--r-- | bta/include/bta_hd_api.h | 558 | ||||
-rw-r--r-- | btif/include/btif_hd.h | 112 | ||||
-rw-r--r-- | btif/src/btif_hd.c | 1820 | ||||
-rw-r--r-- | stack/hid/hidd_api.c | 599 | ||||
-rw-r--r-- | stack/hid/hidd_conn.c | 1043 | ||||
-rw-r--r-- | stack/hid/hidd_int.h | 94 | ||||
-rw-r--r-- | stack/include/hidd_api.h | 236 |
11 files changed, 7290 insertions, 0 deletions
diff --git a/bta/hd/bta_hd_act.c b/bta/hd/bta_hd_act.c new file mode 100644 index 000000000..8ec26981e --- /dev/null +++ b/bta/hd/bta_hd_act.c @@ -0,0 +1,1329 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID host action functions. + * + ******************************************************************************/ + +#include "bt_target.h" + +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +#include <string.h> + +#include "bta_sys.h" +#include "btm_api.h" +#include "l2c_api.h" +#include "bta_hh_int.h" +#include "bta_hh_co.h" +#include "utl.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + + +/***************************************************************************** +** Local Function prototypes +*****************************************************************************/ +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 +static char* bta_hh_get_w4_event(UINT16 event); +static char * bta_hh_hid_event_name(UINT16 event); +#endif + +/***************************************************************************** +** Action Functions +*****************************************************************************/ +/******************************************************************************* +** +** Function bta_hh_api_enable +** +** Description Perform necessary operations to enable HID host. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_api_enable(tBTA_HH_DATA *p_data) +{ + tBTA_HH_STATUS status = BTA_HH_ERR; + UINT8 xx; + + /* initialize BTE HID */ + HID_HostInit(); + + memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB)); + + HID_HostSetSecurityLevel("", p_data->api_enable.sec_mask); + + /* Register with L2CAP */ + if ( HID_HostRegister (bta_hh_cback) == HID_SUCCESS) + { + /* store parameters */ + bta_hh_cb.p_cback = p_data->api_enable.p_cback; + + status = BTA_HH_OK; + /* initialize device CB */ + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++) + { + bta_hh_cb.kdev[xx].state = BTA_HH_IDLE_ST; + bta_hh_cb.kdev[xx].hid_handle = BTA_HH_INVALID_HANDLE; + bta_hh_cb.kdev[xx].index = xx; + } + + /* initialize control block map */ + for (xx = 0; xx < BTA_HH_MAX_KNOWN; xx ++) + 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); +} +/******************************************************************************* +** +** Function bta_hh_api_disable +** +** Description Perform necessary operations to disable HID host. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_api_disable(void) +{ + UINT8 xx; + + /* service is not enabled */ + if (bta_hh_cb.p_cback == NULL) + return; + + /* no live connection, signal DISC_CMPL_EVT directly */ + if (!bta_hh_cb.cnt_num) + { + bta_hh_disc_cmpl(); + } + else /* otherwise, disconnect all live connections */ + { + bta_hh_cb.w4_disable = TRUE; + + for(xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++) + { + /* send API_CLOSE event to every connected device */ + if ( bta_hh_cb.kdev[xx].state == BTA_HH_CONN_ST ) + { + /* disconnect all connected devices */ + bta_hh_sm_execute(&bta_hh_cb.kdev[xx], + BTA_HH_API_CLOSE_EVT, + NULL); + } + } + } + + return; +} + +/******************************************************************************* +** +** Function bta_hh_disc_cmpl +** +** Description All connections have been closed, disable service. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_disc_cmpl(void) +{ + tBTA_HH_STATUS status = BTA_HH_OK; + + /* Deregister with lower layer */ + 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); +} + +/******************************************************************************* +** +** Function bta_hh_sdp_cback +** +** Description SDP callback function. +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_sdp_cback(UINT16 result, UINT16 attr_mask, + tHID_DEV_SDP_INFO *sdp_rec ) +{ + tBTA_HH_DEV_CB *p_cb = bta_hh_cb.p_cur; + UINT8 hdl; + tBTA_HH_STATUS status = BTA_HH_ERR_SDP; + + /* make sure sdp succeeded and hh has not been disabled */ + if ((result == SDP_SUCCESS) && (p_cb != NULL)) + { + /* security is required for the connection, add attr_mask bit*/ + if (p_cb->sec_mask) + attr_mask |= HID_SEC_REQUIRED; + +#if BTA_HH_DEBUG + 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 */ + if (bta_hh_tod_spt(p_cb, sdp_rec->sub_class)) + { + /* if not added before */ + if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE) + { + /* add device/update attr_mask information */ + if(HID_HostAddDev (p_cb->addr, attr_mask, &hdl) == HID_SUCCESS) + { + status = BTA_HH_OK; + /* update cb_index[] map */ + bta_hh_cb.cb_index[hdl] = p_cb->index; + } + else + { + p_cb->app_id = 0; + } + } + else + { + hdl = p_cb->hid_handle; + } + /* else : incoming connection after SDP should update the SDP information as well */ + + if (p_cb->app_id != 0) + { + /* update cb information with attr_mask, dscp_info etc. */ + bta_hh_add_device_to_list(p_cb, hdl, attr_mask, + &sdp_rec->dscp_info, + sdp_rec->sub_class, + sdp_rec->ssr_max_latency, + sdp_rec->ssr_min_tout, + p_cb->app_id); + + p_cb->dscp_info.ctry_code = sdp_rec->ctry_code; + + status = BTA_HH_OK; + } + + } + else /* type of device is not supported */ + status = BTA_HH_ERR_TOD_UNSPT; + } + + /* 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); + + return; +} +/******************************************************************************* +** +** Function bta_hh_di_sdp_cback +** +** Description SDP DI callback function. +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_di_sdp_cback(UINT16 result) +{ + tBTA_HH_DEV_CB *p_cb = bta_hh_cb.p_cur; + tBTA_HH_STATUS status = BTA_HH_ERR_SDP; + tSDP_DI_GET_RECORD di_rec; + tHID_STATUS ret; +#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 + * connection to go through and update the DI record to invalid DI entry.*/ + if (((result == SDP_SUCCESS) || (result == SDP_NO_RECS_MATCH)) && (p_cb != NULL)) + { + if(result == SDP_SUCCESS && SDP_GetNumDiRecords(bta_hh_cb.p_disc_db) != 0) + { + /* always update information with primary DI record */ + if (SDP_GetDiRecord(1, &di_rec, bta_hh_cb.p_disc_db) == SDP_SUCCESS) + { + bta_hh_update_di_info(p_cb, di_rec.rec.vendor, di_rec.rec.product, di_rec.rec.version, 0); + } + + } + else /* no DI recrod available */ + { + bta_hh_update_di_info(p_cb, BTA_HH_VENDOR_ID_INVALID, 0, 0, 0); + } + + if ((ret = HID_HostGetSDPRecord(p_cb->addr, + bta_hh_cb.p_disc_db, + p_bta_hh_cfg->sdp_db_size, + bta_hh_sdp_cback)) == HID_SUCCESS) + { + status = BTA_HH_OK; + } + else + { +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1 ("bta_hh_di_sdp_cback: HID_HostGetSDPRecord failed: Status 0x%2x", + ret); +#endif + } + } + + + if (status != BTA_HH_OK) + { + 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); + } + return; + +} + + +/******************************************************************************* +** +** Function bta_hh_start_sdp +** +** Description Start SDP service search, and obtain necessary SDP records. +** Only one SDP service search request is allowed at the same +** time. For every BTA_HhOpen API call, do SDP first unless SDP +** has been done previously. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_STATUS status = BTA_HH_ERR_SDP; + UINT8 hdl; + + p_cb->sec_mask = p_data->api_conn.sec_mask; + 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) + { + status = BTA_HH_OK; +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG0("bta_hh_start_sdp:: skip SDP for known devices"); +#endif + if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE) + { + if (HID_HostAddDev (p_cb->addr, p_cb->attr_mask, &hdl) \ + == HID_SUCCESS) + { + /* update device CB with newly register device handle */ + bta_hh_add_device_to_list(p_cb, hdl, p_cb->attr_mask, NULL, + p_cb->sub_class, + p_cb->dscp_info.ssr_max_latency, + p_cb->dscp_info.ssr_min_tout, + p_cb->app_id); + /* update cb_index[] map */ + bta_hh_cb.cb_index[hdl] = p_cb->index; + } + else + status = BTA_HH_ERR_NO_RES; + } + bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); + + return; + } + /* GetSDPRecord. at one time only one SDP precedure can be active */ + else if (!bta_hh_cb.p_disc_db) + { + bta_hh_cb.p_disc_db = (tSDP_DISCOVERY_DB *) GKI_getbuf(p_bta_hh_cfg->sdp_db_size); + + if (bta_hh_cb.p_disc_db == NULL) + { + status = BTA_HH_ERR_NO_RES; + } + else + { + bta_hh_cb.p_cur = p_cb; + /* do DI discovery first */ + if (SDP_DiDiscover(p_data->api_conn.bd_addr, + bta_hh_cb.p_disc_db, + p_bta_hh_cfg->sdp_db_size, + bta_hh_di_sdp_cback) != SDP_SUCCESS) + { +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1 ("bta_hh_start_sdp: SDP_DiDiscover failed: \ + Status 0x%2X",status); +#endif + status = BTA_HH_ERR_SDP; + utl_freebuf((void **)&bta_hh_cb.p_disc_db); + } + else + status = BTA_HH_OK; + } + } + + if (status != BTA_HH_OK) + bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); + + return; + +} +/******************************************************************************* +** +** Function bta_hh_sdp_cmpl +** +** Description When SDP completed, initiate a connection or report error depend +** on SDP result. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn_dat; + tBTA_HH_STATUS status = p_data->status; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1 ("bta_hh_sdp_cmpl: status 0x%2X",p_data->status); +#endif + + /* initialize call back data */ + memset((void *)&conn_dat, 0, sizeof(tBTA_HH_CONN)); + conn_dat.handle = p_cb->hid_handle; + bdcpy(conn_dat.bda, p_cb->addr); + + /* if SDP compl success */ + if ( status == BTA_HH_OK) + { + /* not incoming connection doing SDP, initiate a HID connection */ + if (!p_cb->incoming_conn) + { + tHID_STATUS ret; + /* set security level */ + HID_HostSetSecurityLevel("", p_cb->sec_mask); + + /* open HID connection */ + if ((ret = HID_HostOpenDev (p_cb->hid_handle)) != HID_SUCCESS) + { +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1 ("bta_hh_sdp_cmpl: HID_HostOpenDev failed: \ + Status 0x%2X",ret); +#endif + /* open fail, remove device from management device list */ + HID_HostRemoveDev( p_cb->hid_handle); + status = BTA_HH_ERR; + } + else + { + status = BTA_HH_OK; + } + } + else /* incoming connection SDP finish */ + { + bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL); + } + } + + 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 */ + bta_hh_sm_execute(p_cb, BTA_HH_API_CLOSE_EVT, NULL); + + /* if this is an outgoing connection to an unknown device, clean up cb */ + if (p_cb->app_id == 0 && !p_cb->incoming_conn) + { + /* clean up device control block */ + bta_hh_clean_up_kdev(p_cb); + } +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + } + return; +} + +/******************************************************************************* +** +** Function bta_hh_api_disc_act +** +** Description HID Host initiate a disconnection. +** +** +** Returns void +** +*******************************************************************************/ +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; + +#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); + + if (status) + (* bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, (tBTA_HH *)&disc_dat); + } + + return; + +} +/******************************************************************************* +** +** Function bta_hh_open_cmpl_act +** +** Description HID host connection completed +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn ; + UINT8 dev_handle = p_data ? (UINT8)p_data->hid_cback.hdr.layer_specific : \ + p_cb->hid_handle; + + memset((void *)&conn, 0, sizeof (tBTA_HH_CONN)); + conn.handle = dev_handle; + bdcpy(conn.bda, p_cb->addr); + + /* increase connection number */ + bta_hh_cb.cnt_num ++; + + /* initialize device driver */ + 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 (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, + 0, + 0, NULL)) != HID_SUCCESS) + { + /* HID connection is up, while SET_PROTO fail */ + conn.status = BTA_HH_ERR_PROTO; + (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn); + } + else + { + conn.status = BTA_HH_OK; + p_cb->w4_evt = BTA_HH_OPEN_EVT; + } + } + else + (* 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; + +} +/******************************************************************************* +** +** Function bta_hh_open_act +** +** Description HID host receive HID_OPEN_EVT . +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_API_CONN conn_data; + + 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 + + /* SDP has been done */ + if (p_cb->app_id != 0) + { + bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, p_data); + } + else + /* app_id == 0 indicates an incoming conenction request arrives without SDP + 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); + bta_hh_start_sdp(p_cb, (tBTA_HH_DATA *)&conn_data); + } + + return; +} + + +/******************************************************************************* +** +** Function bta_hh_data_act +** +** Description HID Host process a data report +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_data_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA * p_data) +{ + BT_HDR *pdata = p_data->hid_cback.p_data; + UINT8 *p_rpt = (UINT8 *)(pdata + 1) + pdata->offset; + + bta_hh_co_data((UINT8)p_data->hid_cback.hdr.layer_specific, p_rpt, pdata->len, + p_cb->mode, p_cb->sub_class, p_cb->dscp_info.ctry_code, p_cb->addr, p_cb->app_id); + + utl_freebuf((void **)&pdata); +} + + +/******************************************************************************* +** +** Function bta_hh_handsk_act +** +** Description HID Host process a handshake acknoledgement. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA * p_data) +{ + tBTA_HH_CBDATA cback_data ; + tBTA_HH_HSDATA hs_data; + tBTA_HH_CONN conn ; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG2("HANDSHAKE received for: event = %s data= %d", + bta_hh_get_w4_event(p_cb->w4_evt), p_data->hid_cback.data); +#endif + + memset(&hs_data, 0, sizeof(tBTA_HH_HSDATA)); + memset(&cback_data, 0, sizeof(tBTA_HH_CBDATA)); + + switch (p_cb->w4_evt) + { + /* GET_ transsaction, handshake indicate unsupported request */ + case BTA_HH_GET_PROTO_EVT: + hs_data.rsp_data.proto_mode = BTA_HH_PROTO_UNKNOWN; + /* fall through */ + case BTA_HH_GET_RPT_EVT: + case BTA_HH_GET_IDLE_EVT : + hs_data.handle = p_cb->hid_handle; + /* if handshake gives an OK code for these transaction, fill in UNSUPT */ + if ((hs_data.status = bta_hh_get_trans_status(p_data->hid_cback.data)) == BTA_HH_OK) + hs_data.status = BTA_HH_HS_TRANS_NOT_SPT; + + (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&hs_data); + p_cb->w4_evt = 0; + break; + + /* acknoledgement from HID device for SET_ transaction */ + case BTA_HH_SET_RPT_EVT: + case BTA_HH_SET_PROTO_EVT: + case BTA_HH_SET_IDLE_EVT : + cback_data.handle = p_cb->hid_handle; + cback_data.status = bta_hh_get_trans_status(p_data->hid_cback.data); + (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&cback_data); + p_cb->w4_evt = 0; + break; + + /* SET_PROTOCOL when open connection */ + case BTA_HH_OPEN_EVT: + conn.status =p_data->hid_cback.data ? BTA_HH_ERR_PROTO: BTA_HH_OK; + conn.handle = p_cb->hid_handle; + bdcpy(conn.bda, p_cb->addr); + (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&conn); +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + p_cb->w4_evt = 0; + break; + + default: + /* unknow transaction handshake response */ + APPL_TRACE_DEBUG0("unknown transaction type"); + break; + } + + /* transaction achknoledgement received, inform PM for mode change */ + bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->addr); + return; +} +/******************************************************************************* +** +** Function bta_hh_ctrl_dat_act +** +** Description HID Host process a data report from control channel. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_ctrl_dat_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA * p_data) +{ + BT_HDR *pdata = p_data->hid_cback.p_data; + UINT8 *data = (UINT8 *)(pdata + 1) + pdata->offset; + tBTA_HH_HSDATA hs_data; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1("Ctrl DATA received w4: event[%s]", + bta_hh_get_w4_event(p_cb->w4_evt)); +#endif + hs_data.status = BTA_HH_OK; + hs_data.handle = p_cb->hid_handle; + + switch (p_cb->w4_evt) + { + case BTA_HH_GET_IDLE_EVT: + hs_data.rsp_data.idle_rate = *data; + break; + case BTA_HH_GET_RPT_EVT: + hs_data.rsp_data.p_rpt_data = pdata; + break; + case BTA_HH_GET_PROTO_EVT: + /* match up BTE/BTA report/boot mode def*/ + hs_data.rsp_data.proto_mode = ((*data) == HID_PAR_PROTOCOL_REPORT)? \ + BTA_HH_PROTO_RPT_MODE : BTA_HH_PROTO_BOOT_MODE; +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1("GET_PROTOCOL Mode = [%s]", + (hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE)? "Report" : "Boot"); +#endif + break; + /* should not expect control DATA for SET_ transaction */ + case BTA_HH_SET_PROTO_EVT: + /* fall through */ + case BTA_HH_SET_RPT_EVT: + /* fall through */ + case BTA_HH_SET_IDLE_EVT : + /* fall through */ + default: +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1("invalid transaction type for DATA payload: 4_evt[%s]", + bta_hh_get_w4_event(p_cb->w4_evt)); +#endif + break; + } + + /* inform PM for mode change */ + bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); + bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->addr); + + (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&hs_data); + + p_cb->w4_evt = 0; + utl_freebuf((void **)&pdata); + +} + +/******************************************************************************* +** +** 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 +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_close_act (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn_dat ; + tBTA_HH_CBDATA disc_dat = {BTA_HH_OK, 0}; + UINT32 reason = p_data->hid_cback.data; /* Reason for closing (32-bit) */ + + /* if HID_HDEV_EVT_VC_UNPLUG was received, report BTA_HH_VC_UNPLUG_EVT */ + UINT16 event = p_cb->vp ? BTA_HH_VC_UNPLUG_EVT : BTA_HH_CLOSE_EVT; + + disc_dat.handle = p_cb->hid_handle; + disc_dat.status = p_data->hid_cback.data; + + /* Check reason for closing */ + if ((reason & (HID_L2CAP_CONN_FAIL|HID_L2CAP_REQ_FAIL)) || /* Failure to initialize connection (page timeout or l2cap error) */ + (reason == HID_ERR_AUTH_FAILED) || /* Authenication error (while initiating) */ + (reason == HID_ERR_L2CAP_FAILED)) /* Failure creating l2cap connection */ + { + /* Failure in opening connection */ + 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 + return; + } + /* otherwise report CLOSE/VC_UNPLUG event */ + else + { + /* finaliza device driver */ + bta_hh_co_close(p_cb->hid_handle, p_cb->app_id); + /* inform role manager */ + bta_sys_conn_close( BTA_ID_HH ,p_cb->app_id, p_cb->addr); + /* update total conn number */ + bta_hh_cb.cnt_num --; + + if (disc_dat.status) + disc_dat.status = BTA_HH_ERR; + + (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&disc_dat); + + /* if virtually unplug, remove device */ + if (p_cb->vp ) + { + HID_HostRemoveDev( p_cb->hid_handle); + bta_hh_clean_up_kdev(p_cb); + } + +#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(); + } + + return; +} + +/******************************************************************************* +** +** Function bta_hh_get_dscp_act +** +** Description Get device report descriptor +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_get_dscp_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + UNUSED(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); +} + +/******************************************************************************* +** +** Function bta_hh_maint_dev_act +** +** Description HID Host maintain device list. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_MAINT_DEV *p_dev_info = &p_data->api_maintdev; + tBTA_HH_DEV_INFO dev_info ; + UINT8 dev_handle; + + dev_info.status = BTA_HH_ERR; + dev_info.handle = BTA_HH_INVALID_HANDLE; + + switch (p_dev_info->sub_event) + { + case BTA_HH_ADD_DEV_EVT: /* add a device */ + bdcpy(dev_info.bda, p_dev_info->bda); + /* 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, + &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); + /* update cb_index[] map */ + bta_hh_cb.cb_index[dev_handle] = p_cb->index; + } + } + else /* device already been added */ + { + dev_info.handle = p_cb->hid_handle; + dev_info.status = BTA_HH_OK; + } +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + + break; + case BTA_HH_RMV_DEV_EVT: /* remove device */ + 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) + { + dev_info.status = BTA_HH_OK; + + /* remove from known device list in BTA */ + bta_hh_clean_up_kdev(p_cb); + } + } + break; + + default: + APPL_TRACE_DEBUG0("invalid command"); + break; + } + + (* bta_hh_cb.p_cback)(p_dev_info->sub_event, (tBTA_HH *)&dev_info); +} +/******************************************************************************* +** +** Function bta_hh_write_dev_act +** +** Description Write device action. can be SET/GET/DATA transaction. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CBDATA cbdata = {BTA_HH_OK, 0}; + 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 */ + if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL) + { + p_data->api_sndcmd.param = ( p_data->api_sndcmd.param == BTA_HH_PROTO_RPT_MODE) ?\ + HID_PAR_PROTOCOL_REPORT :HID_PAR_PROTOCOL_BOOT_MODE; + } + + if (HID_HostWriteDev (p_cb->hid_handle, + p_data->api_sndcmd.t_type, + p_data->api_sndcmd.param, + p_data->api_sndcmd.data, + p_data->api_sndcmd.rpt_id, + p_data->api_sndcmd.p_data) != HID_SUCCESS) + { + APPL_TRACE_ERROR0("HID_HostWriteDev Error "); + cbdata.status = BTA_HH_ERR; + + if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL && + p_data->api_sndcmd.t_type != HID_TRANS_DATA) + (* bta_hh_cb.p_cback)(event, (tBTA_HH *)&cbdata); + else if (p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) + (* bta_hh_cb.p_cback)(BTA_HH_VC_UNPLUG_EVT, (tBTA_HH *)&cbdata); + } + else + { + + switch(p_data->api_sndcmd.t_type) + { + case HID_TRANS_SET_PROTOCOL: + /* fall through */ + case HID_TRANS_GET_REPORT: + /* fall through */ + case HID_TRANS_SET_REPORT: + /* fall through */ + case HID_TRANS_GET_PROTOCOL: + /* fall through */ + case HID_TRANS_GET_IDLE: + /* fall through */ + case HID_TRANS_SET_IDLE:/* set w4_handsk event name for callback function use */ + p_cb->w4_evt = event; + break; + case HID_TRANS_DATA: /* output report */ + /* fall through */ + 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_VIRTUAL_CABLE_UNPLUG) + p_cb->vp = TRUE; + + break; + /* currently not expected */ + case HID_TRANS_DATAC: + default: + APPL_TRACE_DEBUG1("bta_hh_write_dev_act:: cmd type = %d", + p_data->api_sndcmd.t_type); + break; + } + + /* if not control type transaction, notify PM for energy control */ + if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) + { + /* inform PM for mode change */ + bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); + bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->addr); + } + else if (p_data->api_sndcmd.param == BTA_HH_CTRL_SUSPEND) + { + bta_sys_sco_close(BTA_ID_HH, p_cb->app_id, p_cb->addr); + } + else if (p_data->api_sndcmd.param == BTA_HH_CTRL_EXIT_SUSPEND) + { + bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); + } + } + + } + return; +} + +/***************************************************************************** +** Static Function +*****************************************************************************/ +/******************************************************************************* +** +** Function bta_hh_cback +** +** Description BTA HH callback function. +** +** +** Returns void +** +*******************************************************************************/ +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; + UINT8 xx = 0; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1("bta_hh_cback::HID_event [%s]", bta_hh_hid_event_name(event)); +#endif + + switch (event) + { + case HID_HDEV_EVT_OPEN: + sm_event = BTA_HH_INT_OPEN_EVT; + break; + case HID_HDEV_EVT_CLOSE: + sm_event = BTA_HH_INT_CLOSE_EVT; + break; + case HID_HDEV_EVT_INTR_DATA: + sm_event = BTA_HH_INT_DATA_EVT; + break; + case HID_HDEV_EVT_HANDSHAKE: + sm_event = BTA_HH_INT_HANDSK_EVT; + break; + case HID_HDEV_EVT_CTRL_DATA: + sm_event = BTA_HH_INT_CTRL_DATA; + break; + case HID_HDEV_EVT_RETRYING: + break; + case HID_HDEV_EVT_INTR_DATC: + case HID_HDEV_EVT_CTRL_DATC: + /* Unhandled events: Free buffer for DATAC */ + utl_freebuf((void **)&pdata); + break; + case HID_HDEV_EVT_VC_UNPLUG: + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) + { + if (bta_hh_cb.kdev[xx].hid_handle == dev_handle) + { + bta_hh_cb.kdev[xx].vp = TRUE; + break; + } + } + break; + } + + if (sm_event != BTA_HH_INVALID_EVT && + (p_buf = (tBTA_HH_CBACK_DATA *)GKI_getbuf(sizeof(tBTA_HH_CBACK_DATA) + + sizeof(BT_HDR))) != NULL) + { + 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); + } + +} +/******************************************************************************* +** +** Function bta_hh_get_trans_status +** +** Description translate a handshake result code into BTA HH +** status code +** +*******************************************************************************/ +static tBTA_HH_STATUS bta_hh_get_trans_status(UINT32 result) +{ + switch(result) + { + case HID_PAR_HANDSHAKE_RSP_SUCCESS : /* (0) */ + return BTA_HH_OK; + case HID_PAR_HANDSHAKE_RSP_NOT_READY : /* (1) */ + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID: /* (2) */ + case HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ : /* (3) */ + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM : /* (4) */ + return (tBTA_HH_STATUS)result; + case HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN : /* (14) */ + case HID_PAR_HANDSHAKE_RSP_ERR_FATAL : /* (15) */ + default: + return BTA_HH_HS_ERROR; + break; + } +} +/***************************************************************************** +** Debug Functions +*****************************************************************************/ + +#if (defined BTA_HH_DEBUG && BTA_HH_DEBUG == TRUE) +static char* bta_hh_get_w4_event(UINT16 event) +{ + switch (event) + { + case BTA_HH_GET_RPT_EVT: + return "BTA_HH_GET_RPT_EVT"; + case BTA_HH_SET_RPT_EVT: + return "BTA_HH_SET_RPT_EVT"; + case BTA_HH_GET_PROTO_EVT: + return "BTA_HH_GET_PROTO_EVT"; + case BTA_HH_SET_PROTO_EVT: + return "BTA_HH_SET_PROTO_EVT"; + case BTA_HH_GET_IDLE_EVT: + return "BTA_HH_GET_IDLE_EVT"; + case BTA_HH_SET_IDLE_EVT: + return "BTA_HH_SET_IDLE_EVT"; + case BTA_HH_OPEN_EVT: + return "BTA_HH_OPEN_EVT"; + default: + return "Unknown event"; + } + +} + +static char * bta_hh_hid_event_name(UINT16 event) +{ + switch (event) + { + case HID_HDEV_EVT_OPEN: + return "HID_HDEV_EVT_OPEN"; + case HID_HDEV_EVT_CLOSE: + return "HID_HDEV_EVT_CLOSE"; + case HID_HDEV_EVT_RETRYING: + return "HID_HDEV_EVT_RETRYING"; + case HID_HDEV_EVT_INTR_DATA: + return "HID_HDEV_EVT_INTR_DATA"; + case HID_HDEV_EVT_INTR_DATC: + return "HID_HDEV_EVT_INTR_DATC"; + case HID_HDEV_EVT_CTRL_DATA: + return "HID_HDEV_EVT_CTRL_DATA"; + case HID_HDEV_EVT_CTRL_DATC: + return "HID_HDEV_EVT_CTRL_DATC"; + case HID_HDEV_EVT_HANDSHAKE: + return "HID_HDEV_EVT_HANDSHAKE"; + case HID_HDEV_EVT_VC_UNPLUG: + return "HID_HDEV_EVT_VC_UNPLUG"; + default: + return "Unknown HID event"; + } +} +#endif +#endif /* BTA_HH_INCLUDED */ + diff --git a/bta/hd/bta_hd_api.c b/bta/hd/bta_hd_api.c new file mode 100644 index 000000000..df41a5275 --- /dev/null +++ b/bta/hd/bta_hd_api.c @@ -0,0 +1,495 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID HOST API in the subsystem of BTA. + * + ******************************************************************************/ + +#include "bt_target.h" + +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "bta_hh_api.h" +#include "bta_hh_int.h" +#include "l2c_api.h" +#include "utl.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_hh_reg = +{ + bta_hh_hdl_event, + BTA_HhDisable +}; + +/******************************************************************************* +** +** Function BTA_HhEnable +** +** Description Enable the HID host. This function must be called before +** any other functions in the HID host API are called. When the +** enable operation is complete the callback function will be +** called with BTA_HH_ENABLE_EVT. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_HhEnable(tBTA_SEC sec_mask, tBTA_HH_CBACK *p_cback) +{ + tBTA_HH_API_ENABLE *p_buf; + + /* register with BTA system manager */ + GKI_sched_lock(); + bta_sys_register(BTA_ID_HH, &bta_hh_reg); + GKI_sched_unlock(); + + APPL_TRACE_ERROR0("Calling BTA_HhEnable"); + p_buf = (tBTA_HH_API_ENABLE *)GKI_getbuf((UINT16)sizeof(tBTA_HH_API_ENABLE)); + + if (p_buf != NULL) + { + memset(p_buf, 0, sizeof(tBTA_HH_API_ENABLE)); + + p_buf->hdr.event = BTA_HH_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + p_buf->sec_mask = sec_mask; + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HhDisable +** +** Description Disable the HID host. If the server is currently +** connected, the connection will be closed. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhDisable(void) +{ + BT_HDR *p_buf; + + bta_sys_deregister(BTA_ID_HH); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR))) != NULL) + { + p_buf->event = BTA_HH_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HhClose +** +** Description Disconnect a connection. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhClose(UINT8 dev_handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)sizeof(BT_HDR))) != NULL) + { + memset(p_buf, 0, sizeof(BT_HDR)); + p_buf->event = BTA_HH_API_CLOSE_EVT; + p_buf->layer_specific = (UINT16) dev_handle; + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HhOpen +** +** Description Connect to a device of specified BD address in specified +** protocol mode and security level. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhOpen(BD_ADDR dev_bda, tBTA_HH_PROTO_MODE mode, tBTA_SEC sec_mask) +{ + tBTA_HH_API_CONN *p_buf; + + p_buf = (tBTA_HH_API_CONN *)GKI_getbuf((UINT16)sizeof(tBTA_HH_API_CONN)); + + if (p_buf!= NULL) + { + memset((void *)p_buf, 0, sizeof(tBTA_HH_API_CONN)); + + p_buf->hdr.event = BTA_HH_API_OPEN_EVT; + p_buf->hdr.layer_specific = BTA_HH_INVALID_HANDLE; + p_buf->sec_mask = sec_mask; + p_buf->mode = mode; + bdcpy(p_buf->bd_addr, dev_bda); + + bta_sys_sendmsg((void *)p_buf); + } + else + { + APPL_TRACE_ERROR0("No resource to send HID host Connect request."); + } +} + +/******************************************************************************* +** +** Function bta_hh_snd_write_dev +** +*******************************************************************************/ +static void bta_hh_snd_write_dev(UINT8 dev_handle, UINT8 t_type, UINT8 param, + UINT16 data, UINT8 rpt_id, BT_HDR *p_data) +{ + tBTA_HH_CMD_DATA *p_buf; + UINT16 len = (UINT16) (sizeof(tBTA_HH_CMD_DATA) ); + + if ((p_buf = (tBTA_HH_CMD_DATA *)GKI_getbuf(len))!= NULL) + { + memset(p_buf, 0, sizeof(tBTA_HH_CMD_DATA)); + + p_buf->hdr.event = BTA_HH_API_WRITE_DEV_EVT; + p_buf->hdr.layer_specific = (UINT16) dev_handle; + p_buf->t_type = t_type; + p_buf->data = data; + p_buf->param = param; + p_buf->p_data = p_data; + p_buf->rpt_id = rpt_id; + + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* +** +** Function BTA_HhSetReport +** +** Description send SET_REPORT to device. +** +** Parameter dev_handle: device handle +** r_type: report type, could be BTA_HH_RPTT_OUTPUT or +** BTA_HH_RPTT_FEATURE. +** Returns void +** +*******************************************************************************/ +void BTA_HhSetReport(UINT8 dev_handle, tBTA_HH_RPT_TYPE r_type, BT_HDR *p_data) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_SET_REPORT, r_type, 0, 0, p_data); +} +/******************************************************************************* +** +** Function BTA_HhGetReport +** +** Description Send a GET_REPORT to HID device. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhGetReport(UINT8 dev_handle, tBTA_HH_RPT_TYPE r_type, UINT8 rpt_id, UINT16 buf_size) +{ + UINT8 param = (buf_size) ? (r_type | 0x08) : r_type; + + bta_hh_snd_write_dev(dev_handle, HID_TRANS_GET_REPORT, param, + buf_size, rpt_id, NULL); +} +/******************************************************************************* +** +** Function BTA_HhSetProtoMode +** +** Description This function set the protocol mode at specified HID handle +** +** Returns void +** +*******************************************************************************/ +void BTA_HhSetProtoMode(UINT8 dev_handle, tBTA_HH_PROTO_MODE p_type) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_SET_PROTOCOL, (UINT8)p_type, + 0, 0, NULL); +} +/******************************************************************************* +** +** Function BTA_HhGetProtoMode +** +** Description This function get protocol mode information. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhGetProtoMode(UINT8 dev_handle) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_GET_PROTOCOL, 0, 0, 0, NULL); +} +/******************************************************************************* +** +** Function BTA_HhSetIdle +** +** Description send SET_IDLE to device. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhSetIdle(UINT8 dev_handle, UINT16 idle_rate) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_SET_IDLE, 0, idle_rate, 0, NULL); +} + +/******************************************************************************* +** +** Function BTA_HhGetIdle +** +** Description Send a GET_IDLE from HID device. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhGetIdle(UINT8 dev_handle) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_GET_IDLE, 0, 0, 0, NULL); +} +/******************************************************************************* +** +** Function BTA_HhSendCtrl +** +** Description Send a control command to HID device. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhSendCtrl(UINT8 dev_handle, tBTA_HH_TRANS_CTRL_TYPE c_type) +{ + bta_hh_snd_write_dev(dev_handle, HID_TRANS_CONTROL, (UINT8)c_type, 0, 0, NULL); +} +/******************************************************************************* +** +** Function BTA_HhSendData +** +** Description This function send DATA transaction to HID device. +** +** Parameter dev_handle: device handle +** dev_bda: remote device address +** p_data: data to be sent in the DATA transaction; or +** the data to be write into the Output Report of a LE HID +** device. The report is identified the report ID which is +** the value of the byte (UINT8 *)(p_buf + 1) + p_buf->offset. +** p_data->layer_specific needs to be set to the report type, +** it can be OUTPUT report, or FEATURE report. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhSendData(UINT8 dev_handle, BD_ADDR dev_bda, BT_HDR *p_data) +{ + UNUSED(dev_bda); +#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); +} + +/******************************************************************************* +** +** Function BTA_HhGetDscpInfo +** +** Description Get HID device report descriptor +** +** Returns void +** +*******************************************************************************/ +void BTA_HhGetDscpInfo(UINT8 dev_handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)sizeof(BT_HDR))) != NULL) + { + memset(p_buf, 0, sizeof(BT_HDR)); + p_buf->event = BTA_HH_API_GET_DSCP_EVT; + p_buf->layer_specific = (UINT16) dev_handle; + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_HhAddDev +** +** Description Add a virtually cabled device into HID-Host device list +** to manage and assign a device handle for future API call, +** host applciation call this API at start-up to initialize its +** virtually cabled devices. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhAddDev(BD_ADDR bda, tBTA_HH_ATTR_MASK attr_mask, UINT8 sub_class, + UINT8 app_id, tBTA_HH_DEV_DSCP_INFO dscp_info) +{ + tBTA_HH_MAINT_DEV *p_buf; + UINT16 len = sizeof(tBTA_HH_MAINT_DEV) + dscp_info.descriptor.dl_len; + + p_buf = (tBTA_HH_MAINT_DEV *)GKI_getbuf(len); + + if (p_buf != NULL) + { + memset(p_buf, 0, sizeof(tBTA_HH_MAINT_DEV)); + + p_buf->hdr.event = BTA_HH_API_MAINT_DEV_EVT; + p_buf->sub_event = BTA_HH_ADD_DEV_EVT; + p_buf->hdr.layer_specific = BTA_HH_INVALID_HANDLE; + + p_buf->attr_mask = (UINT16) attr_mask; + p_buf->sub_class = sub_class; + p_buf->app_id = app_id; + bdcpy(p_buf->bda, bda); + + memcpy(&p_buf->dscp_info, &dscp_info, sizeof(tBTA_HH_DEV_DSCP_INFO)); + if ( dscp_info.descriptor.dl_len != 0 && dscp_info.descriptor.dsc_list) + { + p_buf->dscp_info.descriptor.dl_len = dscp_info.descriptor.dl_len; + p_buf->dscp_info.descriptor.dsc_list = (UINT8 *)(p_buf + 1); + memcpy(p_buf->dscp_info.descriptor.dsc_list, dscp_info.descriptor.dsc_list, dscp_info.descriptor.dl_len); + } + else + { + p_buf->dscp_info.descriptor.dsc_list = NULL; + p_buf->dscp_info.descriptor.dl_len = 0; + } + + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* +** +** Function BTA_HhRemoveDev +** +** Description Remove a device from the HID host devices list. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhRemoveDev(UINT8 dev_handle ) +{ + tBTA_HH_MAINT_DEV *p_buf; + + p_buf = (tBTA_HH_MAINT_DEV *)GKI_getbuf((UINT16)sizeof(tBTA_HH_MAINT_DEV)); + + if (p_buf != NULL) + { + memset(p_buf, 0, sizeof(tBTA_HH_MAINT_DEV)); + + p_buf->hdr.event = BTA_HH_API_MAINT_DEV_EVT; + p_buf->sub_event = BTA_HH_RMV_DEV_EVT; + p_buf->hdr.layer_specific = (UINT16) 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 */ +/*******************************************************************************/ + +/******************************************************************************* +** +** Function BTA_HhParseBootRpt +** +** Description This utility function parse a boot mode report. +** For keyboard report, report data will carry the keycode max +** up to 6 key press in one report. Application need to convert +** the keycode into keypress character according to keyboard +** language. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhParseBootRpt(tBTA_HH_BOOT_RPT *p_data, UINT8 *p_report, + UINT16 report_len) +{ + p_data->dev_type = BTA_HH_DEVT_UNKNOWN; + + if (p_report) + { + /* first byte is report ID */ + switch (p_report[0]) + { + case BTA_HH_KEYBD_RPT_ID: /* key board report ID */ + p_data->dev_type = p_report[0]; + bta_hh_parse_keybd_rpt(p_data, p_report + 1, (UINT16)(report_len -1)); + break; + + case BTA_HH_MOUSE_RPT_ID: /* mouse report ID */ + p_data->dev_type = p_report[0]; + bta_hh_parse_mice_rpt(p_data, p_report + 1, (UINT16)(report_len - 1)); + break; + + default: + APPL_TRACE_DEBUG1("Unknown boot report: %d", p_report[0]);; + break; + } + } + + return; +} + +#endif /* BTA_HH_INCLUDED */ diff --git a/bta/hd/bta_hd_int.h b/bta/hd/bta_hd_int.h new file mode 100644 index 000000000..1ac40dac3 --- /dev/null +++ b/bta/hd/bta_hd_int.h @@ -0,0 +1,417 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains BTA HID Host internal definitions + * + ******************************************************************************/ + +#ifndef BTA_HH_INT_H +#define BTA_HH_INT_H + +#include "bta_sys.h" +#include "bd.h" +#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 +{ + BTA_HH_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_HH), + BTA_HH_API_CLOSE_EVT, + BTA_HH_INT_OPEN_EVT, + BTA_HH_INT_CLOSE_EVT, + BTA_HH_INT_DATA_EVT, + BTA_HH_INT_CTRL_DATA, + BTA_HH_INT_HANDSK_EVT, + BTA_HH_SDP_CMPL_EVT, + BTA_HH_API_WRITE_DEV_EVT, + 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, + BTA_HH_GATT_ENC_CMPL_EVT, +#endif + + /* not handled by execute state machine */ + BTA_HH_API_ENABLE_EVT, + BTA_HH_API_DISABLE_EVT, + BTA_HH_DISC_CMPL_EVT +}; +typedef UINT16 tBTA_HH_INT_EVT; /* HID host internal events */ + +#define BTA_HH_INVALID_EVT (BTA_HH_DISC_CMPL_EVT + 1) + +/* event used to map between BTE event and BTA event */ +#define BTA_HH_FST_TRANS_CB_EVT BTA_HH_GET_RPT_EVT +#define BTA_HH_FST_BTE_TRANS_EVT HID_TRANS_GET_REPORT + +/* sub event code used for device maintainence API call */ +#define BTA_HH_ADD_DEV 0 +#define BTA_HH_REMOVE_DEV 1 + +/* state machine states */ +enum +{ + BTA_HH_NULL_ST, + 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; + +/* data structure used to send a command/data to HID device */ +typedef struct +{ + BT_HDR hdr; + 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; + +/* data type for BTA_HH_API_ENABLE_EVT */ +typedef struct +{ + BT_HDR hdr; + UINT8 sec_mask; + UINT8 service_name[BTA_SERVICE_NAME_LEN+1]; + tBTA_HH_CBACK *p_cback; +} tBTA_HH_API_ENABLE; + +typedef struct +{ + BT_HDR hdr; + BD_ADDR bd_addr; + UINT8 sec_mask; + tBTA_HH_PROTO_MODE mode; +}tBTA_HH_API_CONN; + +/* internal event data from BTE HID callback */ +typedef struct +{ + BT_HDR hdr; + BD_ADDR addr; + UINT32 data; + BT_HDR *p_data; +}tBTA_HH_CBACK_DATA; + +typedef struct +{ + BT_HDR hdr; + BD_ADDR bda; + UINT16 attr_mask; + UINT16 sub_event; + UINT8 sub_class; + UINT8 app_id; + 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 +{ + BT_HDR hdr; + tBTA_HH_API_ENABLE api_enable; + tBTA_HH_API_CONN api_conn; + tBTA_HH_CMD_DATA api_sndcmd; + 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; + tBTA_GATTC_ENC_CMPL_CB le_enc_cmpl; +#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 20 +#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 +{ + tBTA_HH_DEV_DSCP_INFO dscp_info; /* report descriptor and DI information */ + BD_ADDR addr; /* BD-Addr of the HID device */ + UINT16 attr_mask; /* attribute mask */ + UINT16 w4_evt; /* W4_handshake event name */ + UINT8 index; /* index number referenced to handle index */ + UINT8 sub_class; /* Cod sub class */ + UINT8 sec_mask; /* security mask */ + UINT8 app_id; /* application ID for this connection */ + 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 + + BOOLEAN security_pending; +} tBTA_HH_DEV_CB; + +/* key board parsing control block */ +typedef struct +{ + BOOLEAN mod_key[4]; /* ctrl, shift(upper), Alt, GUI */ + BOOLEAN num_lock; + BOOLEAN caps_lock; + UINT8 last_report[BTA_HH_MAX_RPT_CHARS]; +} tBTA_HH_KB_CB; + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +typedef struct +{ + tBTA_HH_KB_CB kb_cb; /* key board control block, + suppose BTA will connect + to only one keyboard at + the same time */ + tBTA_HH_DEV_CB kdev[BTA_HH_MAX_DEVICE]; /* device control block */ + tBTA_HH_DEV_CB* p_cur; /* current device control + 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 */ + UINT8 cnt_num; /* connected device number */ + BOOLEAN w4_disable; /* w4 disable flag */ +} +tBTA_HH_CB; + +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_HH_CB bta_hh_cb; +#else +extern tBTA_HH_CB *bta_hh_cb_ptr; +#define bta_hh_cb (*bta_hh_cb_ptr) +#endif + +/* from bta_hh_cfg.c */ +extern tBTA_HH_CFG *p_bta_hh_cfg; + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ +extern BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg); +extern void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, + tBTA_HH_DATA *p_data); + +/* action functions */ +extern void bta_hh_api_disc_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_close_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_data_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA * p_data); +extern void bta_hh_ctrl_dat_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA * p_data); +extern void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +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); +extern void bta_hh_parse_keybd_rpt(tBTA_HH_BOOT_RPT *p_kb_data, + UINT8 *p_report, UINT16 report_len); +extern void bta_hh_parse_mice_rpt(tBTA_HH_BOOT_RPT *p_kb_data, + UINT8 *p_report, UINT16 report_len); +extern BOOLEAN bta_hh_tod_spt(tBTA_HH_DEV_CB *p_cb,UINT8 sub_class); +extern void bta_hh_clean_up_kdev(tBTA_HH_DEV_CB *p_cb); + +extern void bta_hh_add_device_to_list(tBTA_HH_DEV_CB *p_cb, UINT8 handle, + UINT16 attr_mask, + tHID_DEV_DSCP_INFO *p_dscp_info, + UINT8 sub_class, UINT16 max_latency, UINT16 min_tout, UINT8 app_id); +extern void bta_hh_update_di_info(tBTA_HH_DEV_CB *p_cb, UINT16 vendor_id, UINT16 product_id, + UINT16 version, UINT8 flag); +extern void bta_hh_cleanup_disable(tBTA_HH_STATUS status); + +extern UINT8 bta_hh_dev_handle_to_cb_idx(UINT8 dev_handle); + +/* action functions used outside state machine */ +extern void bta_hh_api_enable(tBTA_HH_DATA *p_data); +extern void bta_hh_api_disable(void); +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 BOOLEAN bta_hh_le_is_hh_gatt_if(tBTA_GATTC_IF client_if); +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); +extern void bta_hh_le_notify_enc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); + +#if BTA_HH_DEBUG +extern void bta_hh_trace_dev_db(void); +#endif + +#endif + diff --git a/bta/hd/bta_hd_main.c b/bta/hd/bta_hd_main.c new file mode 100644 index 000000000..c2554c1bb --- /dev/null +++ b/bta/hd/bta_hd_main.c @@ -0,0 +1,587 @@ +/****************************************************************************** + * + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID host main functions and state machine. + * + ******************************************************************************/ + +#include "bt_target.h" + +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + +#include <string.h> + +#include "bta_hh_api.h" +#include "bta_hh_int.h" +#include "gki.h" + +/***************************************************************************** +** Constants and types +*****************************************************************************/ + +/* state machine action enumeration list */ +enum +{ + BTA_HH_API_DISC_ACT, /* HID host process API close action */ + BTA_HH_OPEN_ACT, /* HID host process BTA_HH_EVT_OPEN */ + BTA_HH_CLOSE_ACT, /* HID host process BTA_HH_EVT_CLOSE */ + BTA_HH_DATA_ACT, /* HID host receive data report */ + BTA_HH_CTRL_DAT_ACT, + BTA_HH_HANDSK_ACT, + BTA_HH_START_SDP, /* HID host inquery */ + BTA_HH_SDP_CMPL, + BTA_HH_WRITE_DEV_ACT, + 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, + BTA_HH_GATT_ENC_CMPL, +#endif + BTA_HH_NUM_ACTIONS +}; + +#define BTA_HH_IGNORE BTA_HH_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tBTA_HH_ACTION)(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); + +/* action functions */ +const tBTA_HH_ACTION bta_hh_action[] = +{ + bta_hh_api_disc_act, + bta_hh_open_act, + bta_hh_close_act, + bta_hh_data_act, + bta_hh_ctrl_dat_act, + bta_hh_handsk_act, + bta_hh_start_sdp, + bta_hh_sdp_cmpl, + bta_hh_write_dev_act, + 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_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 + ,bta_hh_le_notify_enc_cmpl +#endif +}; + +/* state table information */ +#define BTA_HH_ACTION 0 /* position of action */ +#define BTA_HH_NEXT_STATE 1 /* position of next state */ +#define BTA_HH_NUM_COLS 2 /* number of columns */ + +/* state table for idle state */ +const UINT8 bta_hh_st_idle[][BTA_HH_NUM_COLS] = +{ +/* Event Action Next state */ +/* BTA_HH_API_OPEN_EVT */ {BTA_HH_START_SDP, 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_DATA_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, +/* BTA_HH_INT_CTRL_DATA */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, +/* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, +/* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, +/* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, +/* 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 } +/* BTA_HH_GATT_ENC_CMPL_EVT */ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +#endif + +}; + + +const UINT8 bta_hh_st_w4_conn[][BTA_HH_NUM_COLS] = +{ +/* Event Action Next state */ +/* 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_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_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 } +/* BTA_HH_GATT_ENC_CMPL_EVT */ ,{BTA_HH_IGNORE, BTA_HH_W4_CONN_ST } +#endif +}; + + +const UINT8 bta_hh_st_connected[][BTA_HH_NUM_COLS] = +{ +/* Event Action Next state */ +/* BTA_HH_API_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_CONN_ST }, +/* BTA_HH_API_CLOSE_EVT */ {BTA_HH_API_DISC_ACT, BTA_HH_CONN_ST }, +/* BTA_HH_INT_OPEN_EVT */ {BTA_HH_OPEN_ACT, BTA_HH_CONN_ST }, +/* BTA_HH_INT_CLOSE_EVT */ {BTA_HH_CLOSE_ACT, BTA_HH_IDLE_ST }, +/* BTA_HH_INT_DATA_EVT */ {BTA_HH_DATA_ACT, BTA_HH_CONN_ST }, +/* BTA_HH_INT_CTRL_DATA */ {BTA_HH_CTRL_DAT_ACT, BTA_HH_CONN_ST }, +/* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_HANDSK_ACT, BTA_HH_CONN_ST }, +/* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_CONN_ST }, +/* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_WRITE_DEV_ACT, BTA_HH_CONN_ST }, +/* 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 } +/* BTA_HH_GATT_ENC_CMPL_EVT */ ,{BTA_HH_IGNORE, 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 } +/* BTA_HH_GATT_ENC_CMPL_EVT */ ,{BTA_HH_GATT_ENC_CMPL, BTA_HH_W4_SEC } +}; +#endif + +/* type for state table */ +typedef const UINT8 (*tBTA_HH_ST_TBL)[BTA_HH_NUM_COLS]; + +/* state table */ +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 +}; + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_HH_CB bta_hh_cb; +#endif +/***************************************************************************** +** Static functions +*****************************************************************************/ +#if BTA_HH_DEBUG == TRUE +static char *bta_hh_evt_code(tBTA_HH_INT_EVT evt_code); +static char *bta_hh_state_code(tBTA_HH_STATE state_code); +#endif + +/******************************************************************************* +** +** Function bta_hh_sm_execute +** +** Description State machine event handling function for HID Host +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA * p_data) +{ + tBTA_HH_ST_TBL state_table; + UINT8 action; + tBTA_HH cback_data; + tBTA_HH_EVT cback_event = 0; +#if BTA_HH_DEBUG == TRUE + tBTA_HH_STATE in_state ; + UINT16 debug_event = event; +#endif + + memset(&cback_data, 0, sizeof(tBTA_HH)); + + /* handle exception, no valid control block was found */ + if (!p_cb) + { + /* BTA HH enabled already? otherwise ignore the event although it's bad*/ + if (bta_hh_cb.p_cback != NULL) + { + switch (event) + { + /* no control block available for new connection */ + case BTA_HH_API_OPEN_EVT: + cback_event = BTA_HH_OPEN_EVT; + /* build cback data */ + bdcpy(cback_data.conn.bda, ((tBTA_HH_API_CONN *)p_data)->bd_addr); + cback_data.conn.status = BTA_HH_ERR_DB_FULL; + cback_data.conn.handle = BTA_HH_INVALID_HANDLE; + break; + /* DB full, BTA_HhAddDev */ + case BTA_HH_API_MAINT_DEV_EVT: + cback_event = p_data->api_maintdev.sub_event; + + if (p_data->api_maintdev.sub_event == BTA_HH_ADD_DEV_EVT) + { + bdcpy(cback_data.dev_info.bda, p_data->api_maintdev.bda); + cback_data.dev_info.status = BTA_HH_ERR_DB_FULL; + cback_data.dev_info.handle = BTA_HH_INVALID_HANDLE; + } + else + { + cback_data.dev_info.status = BTA_HH_ERR_HDL; + cback_data.dev_info.handle = (UINT8)p_data->api_maintdev.hdr.layer_specific; + } + break; + 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) + { + cback_data.dev_status.status = BTA_HH_ERR_HDL; + cback_data.dev_status.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; + } + else if (p_data->api_sndcmd.t_type != HID_TRANS_DATA && + p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) + { + cback_data.hs_data.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; + cback_data.hs_data.status = BTA_HH_ERR_HDL; + /* hs_data.rsp_data will be all zero, which is not valid value */ + } + else if (p_data->api_sndcmd.t_type == HID_TRANS_CONTROL && + p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) + { + cback_data.status = BTA_HH_ERR_HDL; + cback_event = BTA_HH_VC_UNPLUG_EVT; + } + else + cback_event = 0; + break; + + case BTA_HH_API_CLOSE_EVT: + cback_event = BTA_HH_CLOSE_EVT; + + cback_data.dev_status.status = BTA_HH_ERR_HDL; + cback_data.dev_status.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; + break; + + default: + /* invalid handle, call bad API event */ + APPL_TRACE_ERROR1("wrong device handle: [%d]", p_data->hdr.layer_specific); + break; + } + if (cback_event) + (* bta_hh_cb.p_cback)(cback_event, &cback_data); + } + } + /* corresponding CB is found, go to state machine */ + else + { +#if BTA_HH_DEBUG == TRUE + in_state = p_cb->state; + APPL_TRACE_EVENT3("bta_hh_sm_execute: State 0x%02x [%s], Event [%s]", + in_state, bta_hh_state_code(in_state), + bta_hh_evt_code(debug_event)); +#endif + + if ((p_cb->state == BTA_HH_NULL_ST) || (p_cb->state >= BTA_HH_INVALID_ST)) + { + APPL_TRACE_ERROR2("bta_hh_sm_execute: Invalid state State = 0x%x, Event = %d", + p_cb->state,event); + return; + } + state_table = bta_hh_st_tbl[p_cb->state - 1]; + + event &= 0xff; + + p_cb->state = state_table[event][BTA_HH_NEXT_STATE] ; + + if ((action = state_table[event][BTA_HH_ACTION]) != BTA_HH_IGNORE) + { + (*bta_hh_action[action])(p_cb, p_data); + } + +#if BTA_HH_DEBUG == TRUE + if (in_state != p_cb->state) + { + APPL_TRACE_DEBUG3("HH State Change: [%s] -> [%s] after Event [%s]", + bta_hh_state_code(in_state), + bta_hh_state_code(p_cb->state), + bta_hh_evt_code(debug_event)); + } +#endif + } + + return; +} +/******************************************************************************* +** +** Function bta_hh_hdl_event +** +** Description HID host main event handling function. +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg) +{ + UINT8 index = BTA_HH_IDX_INVALID; + tBTA_HH_DEV_CB *p_cb = NULL; + + switch (p_msg->event) + { + case BTA_HH_API_ENABLE_EVT: + bta_hh_api_enable((tBTA_HH_DATA *) p_msg); + break; + + case BTA_HH_API_DISABLE_EVT: + bta_hh_api_disable(); + break; + + case BTA_HH_DISC_CMPL_EVT: /* disable complete */ + bta_hh_disc_cmpl(); + break; + + default: + /* all events processed in state machine need to find corresponding + CB before proceed */ + if (p_msg->event == BTA_HH_API_OPEN_EVT) + { + index = bta_hh_find_cb(((tBTA_HH_API_CONN *)p_msg)->bd_addr); + } + else if (p_msg->event == BTA_HH_API_MAINT_DEV_EVT) + { + /* if add device */ + if (((tBTA_HH_MAINT_DEV *)p_msg)->sub_event == BTA_HH_ADD_DEV_EVT) + { + index = bta_hh_find_cb(((tBTA_HH_MAINT_DEV *)p_msg)->bda); + } + else /* else remove device by handle */ + { + index = bta_hh_dev_handle_to_cb_idx((UINT8)p_msg->layer_specific); +// btla-specific ++ + /* If BT disable is done while the HID device is connected and Link_Key uses unauthenticated combination + * then we can get into a situation where remove_bonding is called with the index set to 0 (without getting + * cleaned up). Only when VIRTUAL_UNPLUG is called do we cleanup the index and make it MAX_KNOWN. + * So if REMOVE_DEVICE is called and in_use is FALSE then we should treat this as a NULL p_cb. Hence we + * force the index to be IDX_INVALID + */ + if ((index != BTA_HH_IDX_INVALID) && + (bta_hh_cb.kdev[index].in_use == FALSE)) { + index = BTA_HH_IDX_INVALID; + } +// 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); + + if (index != BTA_HH_IDX_INVALID) + p_cb = &bta_hh_cb.kdev[index]; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG2("bta_hh_hdl_event:: handle = %d dev_cb[%d] ", p_msg->layer_specific, index); +#endif + bta_hh_sm_execute(p_cb, p_msg->event, (tBTA_HH_DATA *) p_msg); + } + return (TRUE); +} + +/***************************************************************************** +** Debug Functions +*****************************************************************************/ +#if BTA_HH_DEBUG +/******************************************************************************* +** +** Function bta_hh_evt_code +** +** Description +** +** Returns void +** +*******************************************************************************/ +static char *bta_hh_evt_code(tBTA_HH_INT_EVT evt_code) +{ + switch(evt_code) + { + case BTA_HH_API_DISABLE_EVT: + return "BTA_HH_API_DISABLE_EVT"; + case BTA_HH_API_ENABLE_EVT: + return "BTA_HH_API_ENABLE_EVT"; + case BTA_HH_API_OPEN_EVT: + return "BTA_HH_API_OPEN_EVT"; + case BTA_HH_API_CLOSE_EVT: + return "BTA_HH_API_CLOSE_EVT"; + case BTA_HH_INT_OPEN_EVT: + return "BTA_HH_INT_OPEN_EVT"; + case BTA_HH_INT_CLOSE_EVT: + return "BTA_HH_INT_CLOSE_EVT"; + case BTA_HH_INT_HANDSK_EVT: + return "BTA_HH_INT_HANDSK_EVT"; + case BTA_HH_INT_DATA_EVT: + return "BTA_HH_INT_DATA_EVT"; + case BTA_HH_INT_CTRL_DATA: + return "BTA_HH_INT_CTRL_DATA"; + case BTA_HH_API_WRITE_DEV_EVT: + return "BTA_HH_API_WRITE_DEV_EVT"; + case BTA_HH_SDP_CMPL_EVT: + return "BTA_HH_SDP_CMPL_EVT"; + case BTA_HH_DISC_CMPL_EVT: + return "BTA_HH_DISC_CMPL_EVT"; + case BTA_HH_API_MAINT_DEV_EVT: + return "BTA_HH_API_MAINT_DEV_EVT"; + case BTA_HH_API_GET_DSCP_EVT: + 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"; + } +} + +/******************************************************************************* +** +** Function bta_hh_state_code +** +** Description get string representation of HID host state code. +** +** Returns void +** +*******************************************************************************/ +static char *bta_hh_state_code(tBTA_HH_STATE state_code) +{ + switch (state_code) + { + case BTA_HH_NULL_ST: + return"BTA_HH_NULL_ST"; + case BTA_HH_IDLE_ST: + return "BTA_HH_IDLE_ST"; + case BTA_HH_W4_CONN_ST: + 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"; + } +} + +#endif /* Debug Functions */ + +#endif /* BTA_HH_INCLUDED */ diff --git a/bta/include/bta_hd_api.h b/bta/include/bta_hd_api.h new file mode 100644 index 000000000..893cac5cc --- /dev/null +++ b/bta/include/bta_hd_api.h @@ -0,0 +1,558 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef BTA_HH_API_H +#define BTA_HH_API_H + +#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 TRUE +#endif + +#ifndef BTA_HH_SSR_MAX_LATENCY_DEF +#define BTA_HH_SSR_MAX_LATENCY_DEF 800 /* 500 ms*/ +#endif + +#ifndef BTA_HH_SSR_MIN_TOUT_DEF +#define BTA_HH_SSR_MIN_TOUT_DEF 2 +#endif + +/* BTA HID Host callback events */ +#define BTA_HH_ENABLE_EVT 0 /* HH enabled */ +#define BTA_HH_DISABLE_EVT 1 /* HH disabled */ +#define BTA_HH_OPEN_EVT 2 /* connection opened */ +#define BTA_HH_CLOSE_EVT 3 /* connection closed */ +#define BTA_HH_GET_RPT_EVT 4 /* BTA_HhGetReport callback */ +#define BTA_HH_SET_RPT_EVT 5 /* BTA_HhSetReport callback */ +#define BTA_HH_GET_PROTO_EVT 6 /* BTA_GetProtoMode callback */ +#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 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; + +/* application ID(none-zero) for each type of device */ +#define BTA_HH_APP_ID_MI 1 +#define BTA_HH_APP_ID_KB 2 +#define BTA_HH_APP_ID_RMC 3 +#define BTA_HH_APP_ID_3DSG 4 +#define BTA_HH_APP_ID_JOY 5 +#define BTA_HH_APP_ID_GPAD 6 +#define BTA_HH_APP_ID_LE 0xff + +/* defined the minimum offset */ +#define BTA_HH_MIN_OFFSET L2CAP_MIN_OFFSET+1 + +/* 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 + +#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 + +/* type of protocol mode */ +#define BTA_HH_PROTO_RPT_MODE (0x00) +#define BTA_HH_PROTO_BOOT_MODE (0x01) +#define BTA_HH_PROTO_UNKNOWN (0xff) +typedef UINT8 tBTA_HH_PROTO_MODE; + +enum +{ + BTA_HH_KEYBD_RPT_ID = 1, + BTA_HH_MOUSE_RPT_ID +}; +typedef UINT8 tBTA_HH_BOOT_RPT_ID; + +/* type of devices, bit mask */ +#define BTA_HH_DEVT_UNKNOWN 0x00 +#define BTA_HH_DEVT_JOS 0x01 /* joy stick */ +#define BTA_HH_DEVT_GPD 0x02 /* game pad */ +#define BTA_HH_DEVT_RMC 0x03 /* remote control */ +#define BTA_HH_DEVT_SED 0x04 /* sensing device */ +#define BTA_HH_DEVT_DGT 0x05 /* Digitizer tablet */ +#define BTA_HH_DEVT_CDR 0x06 /* card reader */ +#define BTA_HH_DEVT_KBD 0x10 /* keyboard */ +#define BTA_HH_DEVT_MIC 0x20 /* pointing device */ +#define BTA_HH_DEVT_COM 0x30 /* Combo keyboard/pointing */ +#define BTA_HH_DEVT_OTHER 0x80 +typedef UINT8 tBTA_HH_DEVT; + +enum +{ + BTA_HH_OK, + BTA_HH_HS_HID_NOT_READY, /* handshake error : device not ready */ + BTA_HH_HS_INVALID_RPT_ID, /* handshake error : invalid report ID */ + BTA_HH_HS_TRANS_NOT_SPT, /* handshake error : transaction not spt */ + BTA_HH_HS_INVALID_PARAM, /* handshake error : invalid paremter */ + BTA_HH_HS_ERROR, /* handshake error : unspecified HS error */ + BTA_HH_ERR, /* general BTA HH error */ + 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 */ + BTA_HH_ERR_NO_RES, /* out of system resources */ + BTA_HH_ERR_AUTH_FAILED, /* authentication fail */ + BTA_HH_ERR_HDL, + BTA_HH_ERR_SEC +}; +typedef UINT8 tBTA_HH_STATUS; + + +#define BTA_HH_VIRTUAL_CABLE HID_VIRTUAL_CABLE +#define BTA_HH_NORMALLY_CONNECTABLE HID_NORMALLY_CONNECTABLE +#define BTA_HH_RECONN_INIT HID_RECONN_INIT +#define BTA_HH_SDP_DISABLE HID_SDP_DISABLE +#define BTA_HH_BATTERY_POWER HID_BATTERY_POWER +#define BTA_HH_REMOTE_WAKE HID_REMOTE_WAKE +#define BTA_HH_SUP_TOUT_AVLBL HID_SUP_TOUT_AVLBL +#define BTA_HH_SEC_REQUIRED HID_SEC_REQUIRED +typedef UINT16 tBTA_HH_ATTR_MASK; + +/* supported type of device and corresponding application ID */ +typedef struct +{ + tBTA_HH_DEVT tod; /* type of device */ + UINT8 app_id; /* corresponding application ID */ +}tBTA_HH_SPT_TOD; + +/* configuration struct */ +typedef struct +{ + UINT8 max_devt_spt; /* max number of types of devices spt */ + tBTA_HH_SPT_TOD *p_devt_list; /* supported types of device list */ + UINT16 sdp_db_size; +}tBTA_HH_CFG; + +enum +{ + BTA_HH_RPTT_RESRV, /* reserved */ + BTA_HH_RPTT_INPUT, /* input report */ + BTA_HH_RPTT_OUTPUT, /* output report */ + BTA_HH_RPTT_FEATURE /* feature report */ +}; +typedef UINT8 tBTA_HH_RPT_TYPE; + +/* HID_CONTROL operation code used in BTA_HhSendCtrl() +*/ +enum +{ + BTA_HH_CTRL_NOP = 0 + HID_PAR_CONTROL_NOP ,/* mapping from BTE */ + BTA_HH_CTRL_HARD_RESET, /* hard reset */ + BTA_HH_CTRL_SOFT_RESET, /* soft reset */ + BTA_HH_CTRL_SUSPEND, /* enter suspend */ + BTA_HH_CTRL_EXIT_SUSPEND, /* exit suspend */ + BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG /* virtual unplug */ +}; +typedef UINT8 tBTA_HH_TRANS_CTRL_TYPE; + +typedef tHID_DEV_DSCP_INFO tBTA_HH_DEV_DESCR; + +#define BTA_HH_SSR_PARAM_INVALID HID_SSR_PARAM_INVALID + +/* id DI is not existing in remote device, vendor_id in tBTA_HH_DEV_DSCP_INFO will be set to 0xffff */ +#define BTA_HH_VENDOR_ID_INVALID 0xffff + + +/* report descriptor information */ +typedef struct +{ + UINT16 vendor_id; /* vendor ID */ + UINT16 product_id; /* product ID */ + UINT16 version; /* version */ + 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; + +/* callback event data for BTA_HH_OPEN_EVT */ +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; + +/* callback event data */ +typedef struct +{ + tBTA_HH_STATUS status; /* operation status */ + UINT8 handle; /* device handle */ +} tBTA_HH_CBDATA; + +enum +{ + BTA_HH_MOD_CTRL_KEY, + BTA_HH_MOD_SHFT_KEY, + BTA_HH_MOD_ALT_KEY, + BTA_HH_MOD_GUI_KEY, + BTA_HH_MOD_MAX_KEY +}; + +/* parsed boot mode keyboard report */ +typedef struct +{ + UINT8 this_char[6]; /* virtual key code */ + BOOLEAN mod_key[BTA_HH_MOD_MAX_KEY]; + /* ctrl, shift, Alt, GUI */ + /* modifier key: is Shift key pressed */ + /* modifier key: is Ctrl key pressed */ + /* modifier key: is Alt key pressed */ + /* modifier key: GUI up/down */ + BOOLEAN caps_lock; /* is caps locked */ + BOOLEAN num_lock; /* is Num key pressed */ +} tBTA_HH_KEYBD_RPT; + +/* parsed boot mode mouse report */ +typedef struct +{ + UINT8 mouse_button; /* mouse button is clicked */ + INT8 delta_x; /* displacement x */ + INT8 delta_y; /* displacement y */ +}tBTA_HH_MICE_RPT; + +/* parsed Boot report */ +typedef struct +{ + tBTA_HH_BOOT_RPT_ID dev_type; /* type of device report */ + union + { + tBTA_HH_KEYBD_RPT keybd_rpt; /* keyboard report */ + tBTA_HH_MICE_RPT mice_rpt; /* mouse report */ + } data_rpt; +} tBTA_HH_BOOT_RPT; + +/* handshake data */ +typedef struct +{ + tBTA_HH_STATUS status; /* handshake status */ + UINT8 handle; /* device handle */ + union + { + tBTA_HH_PROTO_MODE proto_mode; /* GET_PROTO_EVT :protocol mode */ + BT_HDR *p_rpt_data; /* GET_RPT_EVT : report data */ + UINT8 idle_rate; /* GET_IDLE_EVT : idle rate */ + } rsp_data; + +}tBTA_HH_HSDATA; + +/* union of data associated with HD callback */ +typedef union +{ + tBTA_HH_DEV_INFO dev_info; /* BTA_HH_ADD_DEV_EVT, BTA_HH_RMV_DEV_EVT */ + tBTA_HH_CONN conn; /* BTA_HH_OPEN_EVT */ + 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_UPDATE_SCPP_EVT */ + + tBTA_HH_STATUS status; /* BTA_HH_ENABLE_EVT */ + tBTA_HH_DEV_DSCP_INFO dscp_info; /* BTA_HH_GET_DSCP_EVT */ + tBTA_HH_HSDATA hs_data; /* GET_ transaction callback + BTA_HH_GET_RPT_EVT + BTA_HH_GET_PROTO_EVT + BTA_HH_GET_IDLE_EVT */ +} tBTA_HH; + +/* BTA HH callback function */ +typedef void (tBTA_HH_CBACK) (tBTA_HH_EVT event, tBTA_HH *p_data); + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function BTA_HhRegister +** +** Description This function enable HID host and registers HID-Host with +** lower layers. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhEnable(tBTA_SEC sec_mask, tBTA_HH_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_HhDeregister +** +** Description This function is called when the host is about power down. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhDisable(void); + +/******************************************************************************* +** +** Function BTA_HhOpen +** +** Description This function is called to start an inquiry and read SDP +** record of responding devices; connect to a device if only +** one active HID device is found. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhOpen (BD_ADDR dev_bda, tBTA_HH_PROTO_MODE mode, + tBTA_SEC sec_mask); + +/******************************************************************************* +** +** Function BTA_HhClose +** +** Description This function disconnects the device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhClose(UINT8 dev_handle); + +/******************************************************************************* +** +** Function BTA_HhSetProtoMode +** +** Description This function set the protocol mode at specified HID handle +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhSetProtoMode(UINT8 handle, tBTA_HH_PROTO_MODE t_type); + +/******************************************************************************* +** +** Function BTA_HhGetProtoMode +** +** Description This function get the protocol mode of a specified HID device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhGetProtoMode(UINT8 dev_handle); +/******************************************************************************* +** +** Function BTA_HhSetReport +** +** Description send SET_REPORT to device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhSetReport(UINT8 dev_handle, tBTA_HH_RPT_TYPE r_type, + BT_HDR *p_data); + +/******************************************************************************* +** +** Function BTA_HhGetReport +** +** Description Send a GET_REPORT to HID device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhGetReport(UINT8 dev_handle, tBTA_HH_RPT_TYPE r_type, + UINT8 rpt_id, UINT16 buf_size); +/******************************************************************************* +** +** Function BTA_HhSetIdle +** +** Description send SET_IDLE to device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhSetIdle(UINT8 dev_handle, UINT16 idle_rate); + +/******************************************************************************* +** +** Function BTA_HhGetIdle +** +** Description Send a GET_IDLE to HID device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhGetIdle(UINT8 dev_handle); + +/******************************************************************************* +** +** Function BTA_HhSendCtrl +** +** Description Send HID_CONTROL request to a HID device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhSendCtrl(UINT8 dev_handle, + tBTA_HH_TRANS_CTRL_TYPE c_type); + +/******************************************************************************* +** +** Function BTA_HhSetIdle +** +** Description send SET_IDLE to device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhSetIdle(UINT8 dev_handle, UINT16 idle_rate); + + +/******************************************************************************* +** +** Function BTA_HhGetIdle +** +** Description Send a GET_IDLE from HID device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhGetIdle(UINT8 dev_handle); + +/******************************************************************************* +** +** Function BTA_HhSendData +** +** Description Send DATA transaction to a HID device. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhSendData(UINT8 dev_handle, BD_ADDR dev_bda, BT_HDR *p_buf); + +/******************************************************************************* +** +** Function BTA_HhGetDscpInfo +** +** Description Get report descriptor of the device +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhGetDscpInfo(UINT8 dev_handle); + +/******************************************************************************* +** Function BTA_HhAddDev +** +** Description Add a virtually cabled device into HID-Host device list +** to manage and assign a device handle for future API call, +** host applciation call this API at start-up to initialize its +** virtually cabled devices. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhAddDev(BD_ADDR bda, tBTA_HH_ATTR_MASK attr_mask, + UINT8 sub_class, UINT8 app_id, + tBTA_HH_DEV_DSCP_INFO dscp_info); +/******************************************************************************* +** +** Function BTA_HhRemoveDev +** +** Description Remove a device from the HID host devices list. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhRemoveDev(UINT8 dev_handle ); + +/******************************************************************************* +** +** Parsing Utility Functions +** +*******************************************************************************/ +/******************************************************************************* +** +** Function BTA_HhParseBootRpt +** +** Description This utility function parse a boot mode report. +** +** Returns void +** +*******************************************************************************/ +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); + + + +#ifdef __cplusplus +} +#endif + +#endif /* BTA_HH_API_H */ diff --git a/btif/include/btif_hd.h b/btif/include/btif_hd.h new file mode 100644 index 000000000..d864ec43e --- /dev/null +++ b/btif/include/btif_hd.h @@ -0,0 +1,112 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef BTIF_HH_H +#define BTIF_HH_H + +#include <hardware/bluetooth.h> +#include <hardware/bt_hh.h> +#include <stdint.h> +#include "bta_hh_api.h" +#include "btu.h" + + +/******************************************************************************* +** Constants & Macros +********************************************************************************/ + +#define BTIF_HH_MAX_HID 8 +#define BTIF_HH_MAX_ADDED_DEV 32 + +#define BTIF_HH_MAX_KEYSTATES 3 +#define BTIF_HH_KEYSTATE_MASK_NUMLOCK 0x01 +#define BTIF_HH_KEYSTATE_MASK_CAPSLOCK 0x02 +#define BTIF_HH_KEYSTATE_MASK_SCROLLLOCK 0x04 + + +/******************************************************************************* +** Type definitions and return values +********************************************************************************/ + +typedef enum +{ + BTIF_HH_DISABLED = 0, + BTIF_HH_ENABLED, + BTIF_HH_DISABLING, + BTIF_HH_DEV_UNKNOWN, + BTIF_HH_DEV_CONNECTING, + BTIF_HH_DEV_CONNECTED, + BTIF_HH_DEV_DISCONNECTED +} BTIF_HH_STATUS; + +typedef struct +{ + bthh_connection_state_t dev_status; + UINT8 dev_handle; + bt_bdaddr_t bd_addr; + tBTA_HH_ATTR_MASK attr_mask; + UINT8 sub_class; + UINT8 app_id; + int fd; + BT_HDR *p_buf; + UINT32 hh_poll_thread_id; + UINT8 hh_keep_polling; + BOOLEAN vup_timer_active; + TIMER_LIST_ENT vup_timer; + BOOLEAN local_vup; // Indicated locally initiated VUP +} btif_hh_device_t; + +/* Control block to maintain properties of devices */ +typedef struct +{ + UINT8 dev_handle; + bt_bdaddr_t bd_addr; + tBTA_HH_ATTR_MASK attr_mask; +} btif_hh_added_device_t; + +/** + * BTIF-HH control block to maintain added devices and currently + * connected hid devices + */ +typedef struct +{ + BTIF_HH_STATUS status; + btif_hh_device_t devices[BTIF_HH_MAX_HID]; + UINT32 device_num; + btif_hh_added_device_t added_devices[BTIF_HH_MAX_ADDED_DEV]; + btif_hh_device_t *p_curr_dev; +} btif_hh_cb_t; + + +/******************************************************************************* +** Functions +********************************************************************************/ + +extern btif_hh_cb_t btif_hh_cb; + +extern btif_hh_device_t *btif_hh_find_connected_dev_by_handle(UINT8 handle); +extern void btif_hh_remove_device(bt_bdaddr_t bd_addr); +BOOLEAN btif_hh_add_added_dev(bt_bdaddr_t bda, tBTA_HH_ATTR_MASK attr_mask); +extern bt_status_t btif_hh_virtual_unplug(bt_bdaddr_t *bd_addr); +extern void btif_hh_disconnect(bt_bdaddr_t *bd_addr); +extern void btif_hh_setreport(btif_hh_device_t *p_dev, bthh_report_type_t r_type, + UINT16 size, UINT8* report); + +BOOLEAN btif_hh_add_added_dev(bt_bdaddr_t bd_addr, tBTA_HH_ATTR_MASK attr_mask); + +#endif diff --git a/btif/src/btif_hd.c b/btif/src/btif_hd.c new file mode 100644 index 000000000..e1b2f6906 --- /dev/null +++ b/btif/src/btif_hd.c @@ -0,0 +1,1820 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/************************************************************************************ + * + * Filename: btif_hh.c + * + * Description: HID Host Profile Bluetooth Interface + * + * + ***********************************************************************************/ +#include <hardware/bluetooth.h> +#include <hardware/bt_hh.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#define LOG_TAG "BTIF_HH" + +#include "bta_api.h" +#include "bta_hh_api.h" +#include "bd.h" +#include "btif_storage.h" + +#include "btif_common.h" +#include "btif_util.h" +#include "btif_hh.h" +#include "gki.h" +#include "l2c_api.h" + + +#define BTIF_HH_APP_ID_MI 0x01 +#define BTIF_HH_APP_ID_KB 0x02 + +#define COD_HID_KEYBOARD 0x0540 +#define COD_HID_POINTING 0x0580 +#define COD_HID_COMBO 0x05C0 +#define COD_HID_MAJOR 0x0500 + +#define KEYSTATE_FILEPATH "/data/misc/bluedroid/bt_hh_ks" //keep this in sync with HID host jni + +#define HID_REPORT_CAPSLOCK 0x39 +#define HID_REPORT_NUMLOCK 0x53 +#define HID_REPORT_SCROLLLOCK 0x47 + +//For Apple Magic Mouse +#define MAGICMOUSE_VENDOR_ID 0x05ac +#define MAGICMOUSE_PRODUCT_ID 0x030d + +#define LOGITECH_KB_MX5500_VENDOR_ID 0x046D +#define LOGITECH_KB_MX5500_PRODUCT_ID 0xB30B + +extern const int BT_UID; +extern const int BT_GID; +static int btif_hh_prev_keyevents=0; //The previous key events +static int btif_hh_keylockstates=0; //The current key state of each key + +#define BTIF_HH_ID_1 0 +#define BTIF_HH_DEV_DISCONNECTED 3 + +#define BTIF_TIMEOUT_VUP_SECS 3 + + +#ifndef BTUI_HH_SECURITY +#define BTUI_HH_SECURITY (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT) +#endif + +#ifndef BTUI_HH_MOUSE_SECURITY +#define BTUI_HH_MOUSE_SECURITY (BTA_SEC_NONE) +#endif + +/* HH request events */ +typedef enum +{ + BTIF_HH_CONNECT_REQ_EVT = 0, + BTIF_HH_DISCONNECT_REQ_EVT, + BTIF_HH_VUP_REQ_EVT +} btif_hh_req_evt_t; + + +/************************************************************************************ +** Constants & Macros +************************************************************************************/ +#define BTIF_HH_SERVICES (BTA_HID_SERVICE_MASK) + + + +/************************************************************************************ +** Local type definitions +************************************************************************************/ + +typedef struct hid_kb_list +{ + UINT16 product_id; + UINT16 version_id; + char* kb_name; +} tHID_KB_LIST; + +/************************************************************************************ +** Static variables +************************************************************************************/ +btif_hh_cb_t btif_hh_cb; + +static bthh_callbacks_t *bt_hh_callbacks = NULL; + +/* List of HID keyboards for which the NUMLOCK state needs to be + * turned ON by default. Add devices to this list to apply the + * NUMLOCK state toggle on fpr first connect.*/ +static tHID_KB_LIST hid_kb_numlock_on_list[] = +{ + {LOGITECH_KB_MX5500_PRODUCT_ID, + LOGITECH_KB_MX5500_VENDOR_ID, + "Logitech MX5500 Keyboard"} +}; + + +#define CHECK_BTHH_INIT() if (bt_hh_callbacks == NULL)\ + {\ + BTIF_TRACE_WARNING1("BTHH: %s: BTHH not initialized", __FUNCTION__);\ + return BT_STATUS_NOT_READY;\ + }\ + else\ + {\ + BTIF_TRACE_EVENT1("BTHH: %s", __FUNCTION__);\ + } + + + +/************************************************************************************ +** Static functions +************************************************************************************/ + +/************************************************************************************ +** Externs +************************************************************************************/ +extern void bta_hh_co_destroy(int fd); +extern void bta_hh_co_write(int fd, UINT8* rpt, UINT16 len); +extern bt_status_t btif_dm_remove_bond(const bt_bdaddr_t *bd_addr); +extern void bta_hh_co_send_hid_info(btif_hh_device_t *p_dev, char *dev_name, UINT16 vendor_id, + UINT16 product_id, UINT16 version, UINT8 ctry_code, + int dscp_len, UINT8 *p_dscp); +extern BOOLEAN check_cod(const bt_bdaddr_t *remote_bdaddr, uint32_t cod); +extern void btif_dm_cb_remove_bond(bt_bdaddr_t *bd_addr); +extern BOOLEAN check_cod_hid(const bt_bdaddr_t *remote_bdaddr, uint32_t cod); +extern int scru_ascii_2_hex(char *p_ascii, int len, UINT8 *p_hex); +extern void btif_dm_hh_open_failed(bt_bdaddr_t *bdaddr); + +/***************************************************************************** +** Local Function prototypes +*****************************************************************************/ +static void set_keylockstate(int keymask, BOOLEAN isSet); +static void toggle_os_keylockstates(int fd, int changedkeystates); +static void sync_lockstate_on_connect(btif_hh_device_t *p_dev); +//static void hh_update_keyboard_lockstates(btif_hh_device_t *p_dev); +void btif_hh_tmr_hdlr(TIMER_LIST_ENT *tle); + + +/************************************************************************************ +** Functions +************************************************************************************/ + +static int get_keylockstates() +{ + return btif_hh_keylockstates; +} + +static void set_keylockstate(int keymask, BOOLEAN isSet) +{ + if(isSet) + btif_hh_keylockstates |= keymask; +} + +/******************************************************************************* +** +** Function toggle_os_keylockstates +** +** Description Function to toggle the keyboard lock states managed by the linux. +** This function is used in by two call paths +** (1) if the lock state change occurred from an onscreen keyboard, +** this function is called to update the lock state maintained + for the HID keyboard(s) +** (2) if a HID keyboard is disconnected and reconnected, +** this function is called to update the lock state maintained + for the HID keyboard(s) +** Returns void +*******************************************************************************/ + +static void toggle_os_keylockstates(int fd, int changedlockstates) +{ + BTIF_TRACE_EVENT3("%s: fd = %d, changedlockstates = 0x%x", + __FUNCTION__, fd, changedlockstates); + UINT8 hidreport[9]; + int reportIndex; + memset(hidreport,0,9); + hidreport[0]=1; + reportIndex=4; + + if (changedlockstates & BTIF_HH_KEYSTATE_MASK_CAPSLOCK) { + BTIF_TRACE_DEBUG1("%s Setting CAPSLOCK", __FUNCTION__); + hidreport[reportIndex++] = (UINT8)HID_REPORT_CAPSLOCK; + } + + if (changedlockstates & BTIF_HH_KEYSTATE_MASK_NUMLOCK) { + BTIF_TRACE_DEBUG1("%s Setting NUMLOCK", __FUNCTION__); + hidreport[reportIndex++] = (UINT8)HID_REPORT_NUMLOCK; + } + + if (changedlockstates & BTIF_HH_KEYSTATE_MASK_SCROLLLOCK) { + BTIF_TRACE_DEBUG1("%s Setting SCROLLLOCK", __FUNCTION__); + hidreport[reportIndex++] = (UINT8) HID_REPORT_SCROLLLOCK; + } + + BTIF_TRACE_DEBUG4("Writing hidreport #1 to os: "\ + "%s: %x %x %x", __FUNCTION__, + hidreport[0], hidreport[1], hidreport[2]); + BTIF_TRACE_DEBUG4("%s: %x %x %x", __FUNCTION__, + hidreport[3], hidreport[4], hidreport[5]); + BTIF_TRACE_DEBUG4("%s: %x %x %x", __FUNCTION__, + hidreport[6], hidreport[7], hidreport[8]); + bta_hh_co_write(fd , hidreport, sizeof(hidreport)); + usleep(200000); + memset(hidreport,0,9); + hidreport[0]=1; + BTIF_TRACE_DEBUG4("Writing hidreport #2 to os: "\ + "%s: %x %x %x", __FUNCTION__, + hidreport[0], hidreport[1], hidreport[2]); + BTIF_TRACE_DEBUG4("%s: %x %x %x", __FUNCTION__, + hidreport[3], hidreport[4], hidreport[5]); + BTIF_TRACE_DEBUG4("%s: %x %x %x ", __FUNCTION__, + hidreport[6], hidreport[7], hidreport[8]); + bta_hh_co_write(fd , hidreport, sizeof(hidreport)); +} + +/******************************************************************************* +** +** Function update_keyboard_lockstates +** +** Description Sends a report to the keyboard to set the lock states of keys +** +*******************************************************************************/ +static void update_keyboard_lockstates(btif_hh_device_t *p_dev) +{ + UINT8 len = 2; /* reportid + 1 byte report*/ + BD_ADDR* bda; + + /* Set report for other keyboards */ + BTIF_TRACE_EVENT3("%s: setting report on dev_handle %d to 0x%x", + __FUNCTION__, p_dev->dev_handle, btif_hh_keylockstates); + + if (p_dev->p_buf != NULL) { + GKI_freebuf(p_dev->p_buf); + } + /* Get SetReport buffer */ + p_dev->p_buf = GKI_getbuf((UINT16) (len + BTA_HH_MIN_OFFSET + + sizeof(BT_HDR))); + if (p_dev->p_buf != NULL) { + p_dev->p_buf->len = len; + p_dev->p_buf->offset = BTA_HH_MIN_OFFSET; + p_dev->p_buf->layer_specific = BTA_HH_RPTT_OUTPUT; + + /* LED status updated by data event */ + UINT8 *pbuf_data = (UINT8 *)(p_dev->p_buf + 1) + + p_dev->p_buf->offset; + pbuf_data[0]=0x01; /*report id */ + pbuf_data[1]=btif_hh_keylockstates; /*keystate*/ + bda = (BD_ADDR*) (&p_dev->bd_addr); + BTA_HhSendData(p_dev->dev_handle, *bda, + p_dev->p_buf); + } +} + +/******************************************************************************* +** +** Function sync_lockstate_on_connect +** +** Description Function to update the keyboard lock states managed by the OS +** when a HID keyboard is connected or disconnected and reconnected +** Returns void +*******************************************************************************/ +static void sync_lockstate_on_connect(btif_hh_device_t *p_dev) +{ + int keylockstates; + + BTIF_TRACE_EVENT1("%s: Syncing keyboard lock states after "\ + "reconnect...",__FUNCTION__); + /*If the device is connected, update keyboard state */ + update_keyboard_lockstates(p_dev); + + /*Check if the lockstate of caps,scroll,num is set. + If so, send a report to the kernel + so the lockstate is in sync */ + keylockstates = get_keylockstates(); + if (keylockstates) + { + BTIF_TRACE_DEBUG2("%s: Sending hid report to kernel "\ + "indicating lock key state 0x%x",__FUNCTION__, + keylockstates); + usleep(200000); + toggle_os_keylockstates(p_dev->fd, keylockstates); + } + else + { + BTIF_TRACE_DEBUG2("%s: NOT sending hid report to kernel "\ + "indicating lock key state 0x%x",__FUNCTION__, + keylockstates); + } +} + +/******************************************************************************* +** +** Function btif_hh_find_dev_by_handle +** +** Description Return the device pointer of the specified device handle +** +** Returns Device entry pointer in the device table +*******************************************************************************/ +static btif_hh_device_t *btif_hh_find_dev_by_handle(UINT8 handle) +{ + UINT32 i; + // LOGV("%s: handle = %d", __FUNCTION__, handle); + for (i = 0; i < BTIF_HH_MAX_HID; i++) { + if (btif_hh_cb.devices[i].dev_status != BTHH_CONN_STATE_UNKNOWN && + btif_hh_cb.devices[i].dev_handle == handle) + { + return &btif_hh_cb.devices[i]; + } + } + return NULL; +} + + +/******************************************************************************* +** +** Function btif_hh_find_connected_dev_by_handle +** +** Description Return the connected device pointer of the specified device handle +** +** Returns Device entry pointer in the device table +*******************************************************************************/ +btif_hh_device_t *btif_hh_find_connected_dev_by_handle(UINT8 handle) +{ + UINT32 i; + for (i = 0; i < BTIF_HH_MAX_HID; i++) { + if (btif_hh_cb.devices[i].dev_status == BTHH_CONN_STATE_CONNECTED && + btif_hh_cb.devices[i].dev_handle == handle) + { + return &btif_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function btif_hh_find_dev_by_bda +** +** Description Return the device pointer of the specified bt_bdaddr_t. +** +** Returns Device entry pointer in the device table +*******************************************************************************/ +static btif_hh_device_t *btif_hh_find_dev_by_bda(bt_bdaddr_t *bd_addr) +{ + UINT32 i; + for (i = 0; i < BTIF_HH_MAX_HID; i++) { + if (btif_hh_cb.devices[i].dev_status != BTHH_CONN_STATE_UNKNOWN && + memcmp(&(btif_hh_cb.devices[i].bd_addr), bd_addr, BD_ADDR_LEN) == 0) + { + return &btif_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function btif_hh_find_connected_dev_by_bda +** +** Description Return the connected device pointer of the specified bt_bdaddr_t. +** +** Returns Device entry pointer in the device table +*******************************************************************************/ +static btif_hh_device_t *btif_hh_find_connected_dev_by_bda(bt_bdaddr_t *bd_addr) +{ + UINT32 i; + for (i = 0; i < BTIF_HH_MAX_HID; i++) { + if (btif_hh_cb.devices[i].dev_status == BTHH_CONN_STATE_CONNECTED && + memcmp(&(btif_hh_cb.devices[i].bd_addr), bd_addr, BD_ADDR_LEN) == 0) + { + return &btif_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function btif_hh_stop_vup_timer +** +** Description stop vitual unplug timer +** +** Returns void +*******************************************************************************/ +void btif_hh_stop_vup_timer(bt_bdaddr_t *bd_addr) +{ + btif_hh_device_t *p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if(p_dev != NULL) + { + if (p_dev->vup_timer_active) + { + BTIF_TRACE_DEBUG0("stop VUP timer "); + btu_stop_timer(&p_dev->vup_timer); + } + p_dev->vup_timer_active = FALSE; + } +} +/******************************************************************************* +** +** Function btif_hh_start_vup_timer +** +** Description start virtual unplug timer +** +** Returns void +*******************************************************************************/ +void btif_hh_start_vup_timer(bt_bdaddr_t *bd_addr) +{ + btif_hh_device_t *p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + + if (p_dev->vup_timer_active == FALSE) + { + BTIF_TRACE_DEBUG0("Start VUP timer "); + memset(&p_dev->vup_timer, 0, sizeof(TIMER_LIST_ENT)); + p_dev->vup_timer.param = (UINT32)btif_hh_tmr_hdlr; + btu_start_timer(&p_dev->vup_timer, BTU_TTYPE_USER_FUNC, + BTIF_TIMEOUT_VUP_SECS); + } + else + { + BTIF_TRACE_DEBUG0("Restart VUP timer "); + btu_stop_timer(&p_dev->vup_timer); + btu_start_timer(&p_dev->vup_timer, BTU_TTYPE_USER_FUNC, + BTIF_TIMEOUT_VUP_SECS); + } + p_dev->vup_timer_active = TRUE; + +} + +/******************************************************************************* +** +** Function btif_hh_add_added_dev +** +** Description Add a new device to the added device list. +** +** Returns TRUE if add successfully, otherwise FALSE. +*******************************************************************************/ +BOOLEAN btif_hh_add_added_dev(bt_bdaddr_t bda, tBTA_HH_ATTR_MASK attr_mask) +{ + int i; + for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) { + if (memcmp(&(btif_hh_cb.added_devices[i].bd_addr), &bda, BD_ADDR_LEN) == 0) { + BTIF_TRACE_WARNING6(" Device %02X:%02X:%02X:%02X:%02X:%02X already added", + bda.address[0], bda.address[1], bda.address[2], bda.address[3], bda.address[4], bda.address[5]); + return FALSE; + } + } + for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) { + if (btif_hh_cb.added_devices[i].bd_addr.address[0] == 0 && + btif_hh_cb.added_devices[i].bd_addr.address[1] == 0 && + btif_hh_cb.added_devices[i].bd_addr.address[2] == 0 && + btif_hh_cb.added_devices[i].bd_addr.address[3] == 0 && + btif_hh_cb.added_devices[i].bd_addr.address[4] == 0 && + btif_hh_cb.added_devices[i].bd_addr.address[5] == 0) + { + BTIF_TRACE_WARNING6(" Added device %02X:%02X:%02X:%02X:%02X:%02X", + bda.address[0], bda.address[1], bda.address[2], bda.address[3], bda.address[4], bda.address[5]); + memcpy(&(btif_hh_cb.added_devices[i].bd_addr), &bda, BD_ADDR_LEN); + btif_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; + btif_hh_cb.added_devices[i].attr_mask = attr_mask; + return TRUE; + } + } + + BTIF_TRACE_WARNING1("%s: Error, out of space to add device",__FUNCTION__); + return FALSE; +} + +/******************************************************************************* + ** + ** Function btif_hh_remove_device + ** + ** Description Remove an added device from the stack. + ** + ** Returns void + *******************************************************************************/ +void btif_hh_remove_device(bt_bdaddr_t bd_addr) +{ + int i; + btif_hh_device_t *p_dev; + btif_hh_added_device_t *p_added_dev; + + ALOGI("%s: bda = %02x:%02x:%02x:%02x:%02x:%02x", __FUNCTION__, + bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); + + for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) { + p_added_dev = &btif_hh_cb.added_devices[i]; + if (memcmp(&(p_added_dev->bd_addr),&bd_addr, 6) == 0) { + BTA_HhRemoveDev(p_added_dev->dev_handle); + btif_storage_remove_hid_info(&(p_added_dev->bd_addr)); + memset(&(p_added_dev->bd_addr), 0, 6); + p_added_dev->dev_handle = BTA_HH_INVALID_HANDLE; + break; + } + } + + p_dev = btif_hh_find_dev_by_bda(&bd_addr); + if (p_dev == NULL) { + BTIF_TRACE_WARNING6(" Oops, can't find device [%02x:%02x:%02x:%02x:%02x:%02x]", + bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); + return; + } + + /* need to notify up-layer device is disconnected to avoid state out of sync with up-layer */ + HAL_CBACK(bt_hh_callbacks, connection_state_cb, &(p_dev->bd_addr), BTHH_CONN_STATE_DISCONNECTED); + + p_dev->dev_status = BTHH_CONN_STATE_UNKNOWN; + p_dev->dev_handle = BTA_HH_INVALID_HANDLE; + if (btif_hh_cb.device_num > 0) { + btif_hh_cb.device_num--; + } + else { + BTIF_TRACE_WARNING1("%s: device_num = 0", __FUNCTION__); + } + if (p_dev->p_buf != NULL) { + GKI_freebuf(p_dev->p_buf); + p_dev->p_buf = NULL; + } + + p_dev->hh_keep_polling = 0; + p_dev->hh_poll_thread_id = -1; + BTIF_TRACE_DEBUG2("%s: uhid fd = %d", __FUNCTION__, p_dev->fd); + if (p_dev->fd >= 0) { + bta_hh_co_destroy(p_dev->fd); + p_dev->fd = -1; + } +} + + +BOOLEAN btif_hh_copy_hid_info(tBTA_HH_DEV_DSCP_INFO* dest , tBTA_HH_DEV_DSCP_INFO* src) +{ + dest->descriptor.dl_len = 0; + if (src->descriptor.dl_len >0) + { + dest->descriptor.dsc_list = (UINT8 *) GKI_getbuf(src->descriptor.dl_len); + if (dest->descriptor.dsc_list == NULL) + { + BTIF_TRACE_WARNING1("%s: Failed to allocate DSCP for CB", __FUNCTION__); + return FALSE; + } + } + memcpy(dest->descriptor.dsc_list, src->descriptor.dsc_list, src->descriptor.dl_len); + dest->descriptor.dl_len = src->descriptor.dl_len; + dest->vendor_id = src->vendor_id; + dest->product_id = src->product_id; + dest->version = src->version; + dest->ctry_code = src->ctry_code; + return TRUE; +} + + +/******************************************************************************* +** +** Function btif_hh_virtual_unplug +** +** Description Virtual unplug initiated from the BTIF thread context +** Special handling for HID mouse- +** +** Returns void +** +*******************************************************************************/ + +bt_status_t btif_hh_virtual_unplug(bt_bdaddr_t *bd_addr) +{ + BTIF_TRACE_DEBUG1("%s", __FUNCTION__); + btif_hh_device_t *p_dev; + char bd_str[18]; + sprintf(bd_str, "%02X:%02X:%02X:%02X:%02X:%02X", + bd_addr->address[0], bd_addr->address[1], bd_addr->address[2], bd_addr->address[3], + bd_addr->address[4], bd_addr->address[5]); + p_dev = btif_hh_find_dev_by_bda(bd_addr); + if ((p_dev != NULL) && (p_dev->dev_status == BTHH_CONN_STATE_CONNECTED) + && (p_dev->attr_mask & HID_VIRTUAL_CABLE)) + { + BTIF_TRACE_DEBUG1("%s Sending BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG", __FUNCTION__); + /* start the timer */ + btif_hh_start_vup_timer(bd_addr); + p_dev->local_vup = TRUE; + BTA_HhSendCtrl(p_dev->dev_handle, BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG); + return BT_STATUS_SUCCESS; + } + else + { + BTIF_TRACE_ERROR2("%s: Error, device %s not opened.", __FUNCTION__, bd_str); + return BT_STATUS_FAIL; + } +} + +/******************************************************************************* +** +** Function btif_hh_connect +** +** Description connection initiated from the BTIF thread context +** +** Returns int status +** +*******************************************************************************/ + +bt_status_t btif_hh_connect(bt_bdaddr_t *bd_addr) +{ + btif_hh_device_t *dev; + btif_hh_added_device_t *added_dev = NULL; + char bda_str[20]; + int i; + BD_ADDR *bda = (BD_ADDR*)bd_addr; + tBTA_HH_CONN conn; + CHECK_BTHH_INIT(); + dev = btif_hh_find_dev_by_bda(bd_addr); + BTIF_TRACE_DEBUG0("Connect _hh"); + sprintf(bda_str, "%02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + if (dev == NULL && btif_hh_cb.device_num >= BTIF_HH_MAX_HID) { + // No space for more HID device now. + BTIF_TRACE_WARNING2("%s: Error, exceeded the maximum supported HID device number %d", + __FUNCTION__, BTIF_HH_MAX_HID); + return BT_STATUS_FAIL; + } + + for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) { + if (memcmp(&(btif_hh_cb.added_devices[i].bd_addr), bd_addr, BD_ADDR_LEN) == 0) { + added_dev = &btif_hh_cb.added_devices[i]; + BTIF_TRACE_WARNING3("%s: Device %s already added, attr_mask = 0x%x", + __FUNCTION__, bda_str, added_dev->attr_mask); + } + } + + if (added_dev != NULL) { + if (added_dev->dev_handle == BTA_HH_INVALID_HANDLE) { + // No space for more HID device now. + BTIF_TRACE_ERROR2("%s: Error, device %s added but addition failed", __FUNCTION__, bda_str); + memset(&(added_dev->bd_addr), 0, 6); + added_dev->dev_handle = BTA_HH_INVALID_HANDLE; + return BT_STATUS_FAIL; + } + } + + if (added_dev == NULL || + (added_dev->attr_mask & HID_NORMALLY_CONNECTABLE) != 0 || + (added_dev->attr_mask & HID_RECONN_INIT) == 0) + { + tBTA_SEC sec_mask = BTUI_HH_SECURITY; + btif_hh_cb.status = BTIF_HH_DEV_CONNECTING; + BD_ADDR *bda = (BD_ADDR*)bd_addr; + BTA_HhOpen(*bda, BTA_HH_PROTO_RPT_MODE, sec_mask); + } + else + { + // This device shall be connected from the host side. + BTIF_TRACE_ERROR2("%s: Error, device %s can only be reconnected from device side", + __FUNCTION__, bda_str); + return BT_STATUS_FAIL; + } + + HAL_CBACK(bt_hh_callbacks, connection_state_cb, bd_addr, BTHH_CONN_STATE_CONNECTING); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btif_hh_disconnect +** +** Description disconnection initiated from the BTIF thread context +** +** Returns void +** +*******************************************************************************/ + +void btif_hh_disconnect(bt_bdaddr_t *bd_addr) +{ + BD_ADDR *bda = (BD_ADDR*)bd_addr; + btif_hh_device_t *p_dev; + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev != NULL) + { + BTA_HhClose(p_dev->dev_handle); + } + else + BTIF_TRACE_DEBUG1("%s-- Error: device not connected:",__FUNCTION__); +} + +/******************************************************************************* +** +** Function btif_btif_hh_setreport +** +** Description setreport initiated from the BTIF thread context +** +** Returns void +** +*******************************************************************************/ + +void btif_hh_setreport(btif_hh_device_t *p_dev, bthh_report_type_t r_type, UINT16 size, + UINT8* report) +{ + UINT8 hexbuf[20]; + UINT16 len = size; + int i = 0; + if (p_dev->p_buf != NULL) { + GKI_freebuf(p_dev->p_buf); + } + p_dev->p_buf = GKI_getbuf((UINT16) (len + BTA_HH_MIN_OFFSET + sizeof(BT_HDR))); + if (p_dev->p_buf == NULL) { + APPL_TRACE_ERROR2("%s: Error, failed to allocate RPT buffer, len = %d", __FUNCTION__, len); + return; + } + + p_dev->p_buf->len = len; + p_dev->p_buf->offset = BTA_HH_MIN_OFFSET; + + //Build a SetReport data buffer + memset(hexbuf, 0, 20); + for(i=0; i<len; i++) + hexbuf[i] = report[i]; + + UINT8* pbuf_data; + pbuf_data = (UINT8*) (p_dev->p_buf + 1) + p_dev->p_buf->offset; + memcpy(pbuf_data, hexbuf, len); + BTA_HhSetReport(p_dev->dev_handle, r_type, p_dev->p_buf); + +} + +/***************************************************************************** +** Section name (Group of functions) +*****************************************************************************/ + +/***************************************************************************** +** +** btif hh api functions (no context switch) +** +*****************************************************************************/ + + +/******************************************************************************* +** +** Function btif_hh_upstreams_evt +** +** Description Executes HH UPSTREAMS events in btif context +** +** Returns void +** +*******************************************************************************/ +static void btif_hh_upstreams_evt(UINT16 event, char* p_param) +{ + tBTA_HH *p_data = (tBTA_HH *)p_param; + bdstr_t bdstr; + btif_hh_device_t *p_dev = NULL; + int i; + int len, tmplen; + + BTIF_TRACE_DEBUG2("%s: event=%s", __FUNCTION__, dump_hh_event(event)); + + switch (event) + { + case BTA_HH_ENABLE_EVT: + BTIF_TRACE_DEBUG2("%s: BTA_HH_ENABLE_EVT: status =%d",__FUNCTION__, p_data->status); + if (p_data->status == BTA_HH_OK) { + btif_hh_cb.status = BTIF_HH_ENABLED; + BTIF_TRACE_DEBUG1("%s--Loading added devices",__FUNCTION__); + /* Add hid descriptors for already bonded hid devices*/ + btif_storage_load_bonded_hid_info(); + } + else { + btif_hh_cb.status = BTIF_HH_DISABLED; + BTIF_TRACE_WARNING1("BTA_HH_ENABLE_EVT: Error, HH enabling failed, status = %d", p_data->status); + } + break; + + case BTA_HH_DISABLE_EVT: + btif_hh_cb.status = BTIF_HH_DISABLED; + if (p_data->status == BTA_HH_OK) { + int i; + //Clear the control block + memset(&btif_hh_cb, 0, sizeof(btif_hh_cb)); + for (i = 0; i < BTIF_HH_MAX_HID; i++){ + btif_hh_cb.devices[i].dev_status = BTHH_CONN_STATE_UNKNOWN; + } + } + else + BTIF_TRACE_WARNING1("BTA_HH_DISABLE_EVT: Error, HH disabling failed, status = %d", p_data->status); + break; + + case BTA_HH_OPEN_EVT: + BTIF_TRACE_WARNING3("%s: BTA_HH_OPN_EVT: handle=%d, status =%d",__FUNCTION__, p_data->conn.handle, p_data->conn.status); + if (p_data->conn.status == BTA_HH_OK) { + p_dev = btif_hh_find_connected_dev_by_handle(p_data->conn.handle); + if (p_dev == NULL) { + BTIF_TRACE_WARNING1("BTA_HH_OPEN_EVT: Error, cannot find device with handle %d", p_data->conn.handle); + btif_hh_cb.status = BTIF_HH_DEV_DISCONNECTED; + // The connect request must come from device side and exceeded the connected + // HID device number. + BTA_HhClose(p_data->conn.handle); + HAL_CBACK(bt_hh_callbacks, connection_state_cb, (bt_bdaddr_t*) &p_data->conn.bda,BTHH_CONN_STATE_DISCONNECTED); + } + else if (p_dev->fd < 0) { + BTIF_TRACE_WARNING0("BTA_HH_OPEN_EVT: Error, failed to find the uhid driver..."); + memcpy(&(p_dev->bd_addr), p_data->conn.bda, BD_ADDR_LEN); + //remove the connection and then try again to reconnect from the mouse side to recover + btif_hh_cb.status = BTIF_HH_DEV_DISCONNECTED; + BTA_HhClose(p_data->conn.handle); + } + else { + BTIF_TRACE_WARNING1("BTA_HH_OPEN_EVT: Found device...Getting dscp info for handle ... %d",p_data->conn.handle); + memcpy(&(p_dev->bd_addr), p_data->conn.bda, BD_ADDR_LEN); + btif_hh_cb.status = BTIF_HH_DEV_CONNECTED; + // Send set_idle if the peer_device is a keyboard + if (check_cod((bt_bdaddr_t*)p_data->conn.bda, COD_HID_KEYBOARD )|| + check_cod((bt_bdaddr_t*)p_data->conn.bda, COD_HID_COMBO)) + BTA_HhSetIdle(p_data->conn.handle, 0); + btif_hh_cb.p_curr_dev = btif_hh_find_connected_dev_by_handle(p_data->conn.handle); + BTA_HhGetDscpInfo(p_data->conn.handle); + p_dev->dev_status = BTHH_CONN_STATE_CONNECTED; + HAL_CBACK(bt_hh_callbacks, connection_state_cb,&(p_dev->bd_addr), p_dev->dev_status); + } + } + else { + bt_bdaddr_t *bdaddr = (bt_bdaddr_t*)p_data->conn.bda; + btif_dm_hh_open_failed(bdaddr); + HAL_CBACK(bt_hh_callbacks, connection_state_cb, (bt_bdaddr_t*) &p_data->conn.bda,BTHH_CONN_STATE_DISCONNECTED); + btif_hh_cb.status = BTIF_HH_DEV_DISCONNECTED; + } + break; + case BTA_HH_CLOSE_EVT: + BTIF_TRACE_DEBUG2("BTA_HH_CLOSE_EVT: status = %d, handle = %d", + p_data->dev_status.status, p_data->dev_status.handle); + p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + if (p_dev != NULL) { + BTIF_TRACE_DEBUG2("%s: uhid fd = %d", __FUNCTION__, p_dev->fd); + if(p_dev->vup_timer_active) + { + btif_hh_stop_vup_timer(&(p_dev->bd_addr)); + } + btif_hh_cb.status = BTIF_HH_DEV_DISCONNECTED; + p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED; + HAL_CBACK(bt_hh_callbacks, connection_state_cb,&(p_dev->bd_addr), p_dev->dev_status); + BTIF_TRACE_DEBUG2("%s: Closing uhid fd = %d", __FUNCTION__, p_dev->fd); + bta_hh_co_destroy(p_dev->fd); + p_dev->fd = -1; + } + else { + BTIF_TRACE_WARNING1("Error: cannot find device with handle %d", p_data->dev_status.handle); + } + break; + case BTA_HH_GET_RPT_EVT: { + BT_HDR *hdr = p_data->hs_data.rsp_data.p_rpt_data; + UINT8 *data = NULL; + UINT16 len = 0; + + BTIF_TRACE_DEBUG2("BTA_HH_GET_RPT_EVT: status = %d, handle = %d", + p_data->hs_data.status, p_data->hs_data.handle); + /* p_rpt_data in HANDSHAKE response case */ + if (hdr) { + data = (UINT8 *)(hdr + 1) + hdr->offset; + len = hdr->len; + } + p_dev = btif_hh_find_connected_dev_by_handle(p_data->hs_data.handle); + if (p_dev) { + HAL_CBACK(bt_hh_callbacks, get_report_cb, + (bt_bdaddr_t*) &(p_dev->bd_addr), + (bthh_status_t) p_data->hs_data.status, data, len); + } else { + BTIF_TRACE_WARNING1("Error: cannot find device with handle %d", p_data->hs_data.handle); + } + break; + } + case BTA_HH_SET_RPT_EVT: + BTIF_TRACE_DEBUG2("BTA_HH_SET_RPT_EVT: status = %d, handle = %d", + p_data->dev_status.status, p_data->dev_status.handle); + p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + if (p_dev != NULL && p_dev->p_buf != NULL) { + BTIF_TRACE_DEBUG0("Freeing buffer..." ); + GKI_freebuf(p_dev->p_buf); + p_dev->p_buf = NULL; + } + break; + + case BTA_HH_GET_PROTO_EVT: + p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + BTIF_TRACE_WARNING4("BTA_HH_GET_PROTO_EVT: status = %d, handle = %d, proto = [%d], %s", + p_data->hs_data.status, p_data->hs_data.handle, + p_data->hs_data.rsp_data.proto_mode, + (p_data->hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE) ? "Report Mode" : + (p_data->hs_data.rsp_data.proto_mode == BTA_HH_PROTO_BOOT_MODE) ? "Boot Mode" : "Unsupported"); + HAL_CBACK(bt_hh_callbacks, protocol_mode_cb,(bt_bdaddr_t*) &(p_dev->bd_addr), (bthh_status_t)p_data->hs_data.status, + (bthh_protocol_mode_t) p_data->hs_data.rsp_data.proto_mode); + break; + + case BTA_HH_SET_PROTO_EVT: + BTIF_TRACE_DEBUG2("BTA_HH_SET_PROTO_EVT: status = %d, handle = %d", + p_data->dev_status.status, p_data->dev_status.handle); + break; + + case BTA_HH_GET_IDLE_EVT: + BTIF_TRACE_DEBUG3("BTA_HH_GET_IDLE_EVT: handle = %d, status = %d, rate = %d", + p_data->hs_data.handle, p_data->hs_data.status, + p_data->hs_data.rsp_data.idle_rate); + break; + + case BTA_HH_SET_IDLE_EVT: + BTIF_TRACE_DEBUG2("BTA_HH_SET_IDLE_EVT: status = %d, handle = %d", + p_data->dev_status.status, p_data->dev_status.handle); + break; + + case BTA_HH_GET_DSCP_EVT: + BTIF_TRACE_WARNING2("BTA_HH_GET_DSCP_EVT: status = %d, handle = %d", + p_data->dev_status.status, p_data->dev_status.handle); + len = p_data->dscp_info.descriptor.dl_len; + BTIF_TRACE_DEBUG1("BTA_HH_GET_DSCP_EVT: len = %d", len); + p_dev = btif_hh_cb.p_curr_dev; + if (p_dev == NULL) { + BTIF_TRACE_ERROR0("BTA_HH_GET_DSCP_EVT: No HID device is currently connected"); + return; + } + if (p_dev->fd < 0) { + ALOGE("BTA_HH_GET_DSCP_EVT: Error, failed to find the uhid driver..."); + return; + } + { + char *cached_name = NULL; + bt_bdname_t bdname; + bt_property_t prop_name; + BTIF_STORAGE_FILL_PROPERTY(&prop_name, BT_PROPERTY_BDNAME, + sizeof(bt_bdname_t), &bdname); + if (btif_storage_get_remote_device_property( + &p_dev->bd_addr, &prop_name) == BT_STATUS_SUCCESS) + { + cached_name = (char *)bdname.name; + } + else + { + cached_name = "Bluetooth HID"; + } + + BTIF_TRACE_WARNING2("%s: name = %s", __FUNCTION__, cached_name); + bta_hh_co_send_hid_info(p_dev, cached_name, + p_data->dscp_info.vendor_id, p_data->dscp_info.product_id, + p_data->dscp_info.version, p_data->dscp_info.ctry_code, + len, p_data->dscp_info.descriptor.dsc_list); + if (btif_hh_add_added_dev(p_dev->bd_addr, p_dev->attr_mask)) { + BD_ADDR bda; + bdcpy(bda, p_dev->bd_addr.address); + tBTA_HH_DEV_DSCP_INFO dscp_info; + bt_status_t ret; + bdcpy(bda, p_dev->bd_addr.address); + btif_hh_copy_hid_info(&dscp_info, &p_data->dscp_info); + BTIF_TRACE_DEBUG6("BTA_HH_GET_DSCP_EVT:bda = %02x:%02x:%02x:%02x:%02x:%02x", + p_dev->bd_addr.address[0], p_dev->bd_addr.address[1], + p_dev->bd_addr.address[2],p_dev->bd_addr.address[3], + p_dev->bd_addr.address[4], p_dev->bd_addr.address[5]); + BTA_HhAddDev(bda, p_dev->attr_mask,p_dev->sub_class,p_dev->app_id, dscp_info); + // write hid info to nvram + ret = btif_storage_add_hid_device_info(&(p_dev->bd_addr), p_dev->attr_mask,p_dev->sub_class,p_dev->app_id, + p_data->dscp_info.vendor_id, p_data->dscp_info.product_id, + p_data->dscp_info.version, p_data->dscp_info.ctry_code, + len, p_data->dscp_info.descriptor.dsc_list); + + ASSERTC(ret == BT_STATUS_SUCCESS, "storing hid info failed", ret); + BTIF_TRACE_WARNING0("BTA_HH_GET_DSCP_EVT: Called add device"); + + //Free buffer created for dscp_info; + if (dscp_info.descriptor.dl_len >0 && dscp_info.descriptor.dsc_list != NULL) + { + GKI_freebuf(dscp_info.descriptor.dsc_list); + dscp_info.descriptor.dsc_list = NULL; + dscp_info.descriptor.dl_len=0; + } + } + else { + //Device already added. + BTIF_TRACE_WARNING1("%s: Device already added ",__FUNCTION__); + } + /*Sync HID Keyboard lockstates */ + tmplen = sizeof(hid_kb_numlock_on_list) + / sizeof(tHID_KB_LIST); + for(i = 0; i< tmplen; i++) + { + if(p_data->dscp_info.vendor_id + == hid_kb_numlock_on_list[i].version_id && + p_data->dscp_info.product_id + == hid_kb_numlock_on_list[i].product_id) + { + BTIF_TRACE_DEBUG3("%s() idx[%d] Enabling "\ + "NUMLOCK for device :: %s", __FUNCTION__, + i, hid_kb_numlock_on_list[i].kb_name); + /* Enable NUMLOCK by default so that numeric + keys work from first keyboard connect */ + set_keylockstate(BTIF_HH_KEYSTATE_MASK_NUMLOCK, + TRUE); + sync_lockstate_on_connect(p_dev); + /* End Sync HID Keyboard lockstates */ + break; + } + } + } + break; + + case BTA_HH_ADD_DEV_EVT: + BTIF_TRACE_WARNING2("BTA_HH_ADD_DEV_EVT: status = %d, handle = %d",p_data->dev_info.status, p_data->dev_info.handle); + int i; + for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btif_hh_cb.added_devices[i].bd_addr.address, p_data->dev_info.bda, 6) == 0) { + if (p_data->dev_info.status == BTA_HH_OK) { + btif_hh_cb.added_devices[i].dev_handle = p_data->dev_info.handle; + } + else { + memset(btif_hh_cb.added_devices[i].bd_addr.address, 0, 6); + btif_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; + } + break; + } + } + break; + case BTA_HH_RMV_DEV_EVT: + BTIF_TRACE_DEBUG2("BTA_HH_RMV_DEV_EVT: status = %d, handle = %d", + p_data->dev_info.status, p_data->dev_info.handle); + BTIF_TRACE_DEBUG6("BTA_HH_RMV_DEV_EVT:bda = %02x:%02x:%02x:%02x:%02x:%02x", + p_data->dev_info.bda[0], p_data->dev_info.bda[1], p_data->dev_info.bda[2], + p_data->dev_info.bda[3], p_data->dev_info.bda[4], p_data->dev_info.bda[5]); + break; + + + case BTA_HH_VC_UNPLUG_EVT: + BTIF_TRACE_DEBUG2("BTA_HH_VC_UNPLUG_EVT: status = %d, handle = %d", + p_data->dev_status.status, p_data->dev_status.handle); + p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + btif_hh_cb.status = BTIF_HH_DEV_DISCONNECTED; + if (p_dev != NULL) { + BTIF_TRACE_DEBUG6("BTA_HH_VC_UNPLUG_EVT:bda = %02x:%02x:%02x:%02x:%02x:%02x", + p_dev->bd_addr.address[0], p_dev->bd_addr.address[1], + p_dev->bd_addr.address[2],p_dev->bd_addr.address[3], + p_dev->bd_addr.address[4], p_dev->bd_addr.address[5]); + /* Stop the VUP timer */ + if(p_dev->vup_timer_active) + { + btif_hh_stop_vup_timer(&(p_dev->bd_addr)); + } + p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED; + BTIF_TRACE_DEBUG1("%s---Sending connection state change", __FUNCTION__); + HAL_CBACK(bt_hh_callbacks, connection_state_cb,&(p_dev->bd_addr), p_dev->dev_status); + BTIF_TRACE_DEBUG1("%s---Removing HID bond", __FUNCTION__); + /* If it is locally initiated VUP or remote device has its major COD as + Peripheral removed the bond.*/ + if (p_dev->local_vup || check_cod_hid(&(p_dev->bd_addr), COD_HID_MAJOR)) + { + p_dev->local_vup = FALSE; + BTA_DmRemoveDevice((UINT8 *)p_dev->bd_addr.address); + } + else + btif_hh_remove_device(p_dev->bd_addr); + HAL_CBACK(bt_hh_callbacks, virtual_unplug_cb,&(p_dev->bd_addr), + p_data->dev_status.status); + } + break; + + case BTA_HH_API_ERR_EVT : + ALOGI("BTA_HH API_ERR"); + break; + + + + default: + BTIF_TRACE_WARNING2("%s: Unhandled event: %d", __FUNCTION__, event); + break; + } +} + +/******************************************************************************* +** +** Function bte_hh_evt +** +** Description Switches context from BTE to BTIF for all HH events +** +** Returns void +** +*******************************************************************************/ + +static void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH *p_data) +{ + bt_status_t status; + int param_len = 0; + + if (BTA_HH_ENABLE_EVT == event) + param_len = sizeof(tBTA_HH_STATUS); + else if (BTA_HH_OPEN_EVT == event) + param_len = sizeof(tBTA_HH_CONN); + else if (BTA_HH_DISABLE_EVT == event) + param_len = sizeof(tBTA_HH_STATUS); + else if (BTA_HH_CLOSE_EVT == event) + param_len = sizeof(tBTA_HH_CBDATA); + else if (BTA_HH_GET_DSCP_EVT == event) + param_len = sizeof(tBTA_HH_DEV_DSCP_INFO); + else if ((BTA_HH_GET_PROTO_EVT == event) || (BTA_HH_GET_RPT_EVT == event)|| (BTA_HH_GET_IDLE_EVT == event)) + param_len = sizeof(tBTA_HH_HSDATA); + else if ((BTA_HH_SET_PROTO_EVT == event) || (BTA_HH_SET_RPT_EVT == event) || (BTA_HH_VC_UNPLUG_EVT == event) || (BTA_HH_SET_IDLE_EVT == event)) + param_len = sizeof(tBTA_HH_CBDATA); + else if ((BTA_HH_ADD_DEV_EVT == event) || (BTA_HH_RMV_DEV_EVT == event) ) + param_len = sizeof(tBTA_HH_DEV_INFO); + else if (BTA_HH_API_ERR_EVT == event) + param_len = 0; + /* switch context to btif task context (copy full union size for convenience) */ + status = btif_transfer_context(btif_hh_upstreams_evt, (uint16_t)event, (void*)p_data, param_len, NULL); + + /* catch any failed context transfers */ + ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status); +} + +/******************************************************************************* +** +** Function btif_hh_handle_evt +** +** Description Switches context for immediate callback +** +** Returns void +** +*******************************************************************************/ + +static void btif_hh_handle_evt(UINT16 event, char *p_param) +{ + bt_bdaddr_t *bd_addr = (bt_bdaddr_t*)p_param; + BTIF_TRACE_EVENT2("%s: event=%d", __FUNCTION__, event); + int ret; + switch(event) + { + case BTIF_HH_CONNECT_REQ_EVT: + { + ret = btif_hh_connect(bd_addr); + if(ret == BT_STATUS_SUCCESS) + { + HAL_CBACK(bt_hh_callbacks, connection_state_cb,bd_addr,BTHH_CONN_STATE_CONNECTING); + } + else + HAL_CBACK(bt_hh_callbacks, connection_state_cb,bd_addr,BTHH_CONN_STATE_DISCONNECTED); + } + break; + + case BTIF_HH_DISCONNECT_REQ_EVT: + { + BTIF_TRACE_EVENT2("%s: event=%d", __FUNCTION__, event); + btif_hh_disconnect(bd_addr); + HAL_CBACK(bt_hh_callbacks, connection_state_cb,bd_addr,BTHH_CONN_STATE_DISCONNECTING); + } + break; + + case BTIF_HH_VUP_REQ_EVT: + { + BTIF_TRACE_EVENT2("%s: event=%d", __FUNCTION__, event); + ret = btif_hh_virtual_unplug(bd_addr); + } + break; + + default: + { + BTIF_TRACE_WARNING2("%s : Unknown event 0x%x", __FUNCTION__, event); + } + break; + } +} + +/******************************************************************************* +** +** Function btif_hh_tmr_hdlr +** +** Description Process timer timeout +** +** Returns void +*******************************************************************************/ +void btif_hh_tmr_hdlr(TIMER_LIST_ENT *tle) +{ + btif_hh_device_t *p_dev; + UINT8 i,j; + tBTA_HH_EVT event; + tBTA_HH p_data; + int param_len = 0; + memset(&p_data, 0, sizeof(tBTA_HH)); + + BTIF_TRACE_DEBUG2("%s timer_in_use=%d", __FUNCTION__, tle->in_use ); + + for (i = 0; i < BTIF_HH_MAX_HID; i++) { + if (btif_hh_cb.devices[i].dev_status == BTHH_CONN_STATE_CONNECTED) + { + + p_dev = &btif_hh_cb.devices[i]; + + if (p_dev->vup_timer_active) + { + p_dev->vup_timer_active = FALSE; + event = BTA_HH_VC_UNPLUG_EVT; + p_data.dev_status.status = BTHH_ERR; + p_data.dev_status.handle = p_dev->dev_handle; + param_len = sizeof(tBTA_HH_CBDATA); + + /* switch context to btif task context */ + btif_transfer_context(btif_hh_upstreams_evt, (uint16_t)event, (void*)&p_data, + param_len, NULL); + } + } + } +} + +/******************************************************************************* +** +** Function btif_hh_init +** +** Description initializes the hh interface +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t init( bthh_callbacks_t* callbacks ) +{ + UINT32 i; + BTIF_TRACE_EVENT1("%s", __FUNCTION__); + + bt_hh_callbacks = callbacks; + memset(&btif_hh_cb, 0, sizeof(btif_hh_cb)); + for (i = 0; i < BTIF_HH_MAX_HID; i++){ + btif_hh_cb.devices[i].dev_status = BTHH_CONN_STATE_UNKNOWN; + } + /* Invoke the enable service API to the core to set the appropriate service_id */ + btif_enable_service(BTA_HID_SERVICE_ID); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function connect +** +** Description connect to hid device +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t connect( bt_bdaddr_t *bd_addr) +{ + if(btif_hh_cb.status != BTIF_HH_DEV_CONNECTING) + { + btif_transfer_context(btif_hh_handle_evt, BTIF_HH_CONNECT_REQ_EVT, + (char*)bd_addr, sizeof(bt_bdaddr_t), NULL); + return BT_STATUS_SUCCESS; + } + else + return BT_STATUS_BUSY; +} + +/******************************************************************************* +** +** Function disconnect +** +** Description disconnect from hid device +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t disconnect( bt_bdaddr_t *bd_addr ) +{ + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + + if (btif_hh_cb.status == BTIF_HH_DISABLED) + { + BTIF_TRACE_WARNING2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev != NULL) + { + return btif_transfer_context(btif_hh_handle_evt, BTIF_HH_DISCONNECT_REQ_EVT, + (char*)bd_addr, sizeof(bt_bdaddr_t), NULL); + } + else + { + BTIF_TRACE_WARNING1("%s: Error, device not opened.", __FUNCTION__); + return BT_STATUS_FAIL; + } +} + +/******************************************************************************* +** +** Function virtual_unplug +** +** Description Virtual UnPlug (VUP) the specified HID device. +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t virtual_unplug (bt_bdaddr_t *bd_addr) +{ + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + char bd_str[18]; + sprintf(bd_str, "%02X:%02X:%02X:%02X:%02X:%02X", + bd_addr->address[0], bd_addr->address[1], bd_addr->address[2], bd_addr->address[3], + bd_addr->address[4], bd_addr->address[5]); + if (btif_hh_cb.status == BTIF_HH_DISABLED) + { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + p_dev = btif_hh_find_dev_by_bda(bd_addr); + if (!p_dev) + { + BTIF_TRACE_ERROR2("%s: Error, device %s not opened.", __FUNCTION__, bd_str); + return BT_STATUS_FAIL; + } + btif_transfer_context(btif_hh_handle_evt, BTIF_HH_VUP_REQ_EVT, + (char*)bd_addr, sizeof(bt_bdaddr_t), NULL); + return BT_STATUS_SUCCESS; +} + + +/******************************************************************************* +** +** Function set_info +** +** Description Set the HID device descriptor for the specified HID device. +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t set_info (bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info ) +{ + CHECK_BTHH_INIT(); + tBTA_HH_DEV_DSCP_INFO dscp_info; + BD_ADDR* bda = (BD_ADDR*) bd_addr; + + BTIF_TRACE_DEBUG6("addr = %02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + BTIF_TRACE_DEBUG6("%s: sub_class = 0x%02x, app_id = %d, vendor_id = 0x%04x, " + "product_id = 0x%04x, version= 0x%04x", + __FUNCTION__, hid_info.sub_class, + hid_info.app_id, hid_info.vendor_id, hid_info.product_id, + hid_info.version); + + if (btif_hh_cb.status == BTIF_HH_DISABLED) + { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + + dscp_info.vendor_id = hid_info.vendor_id; + dscp_info.product_id = hid_info.product_id; + dscp_info.version = hid_info.version; + dscp_info.ctry_code = hid_info.ctry_code; + + dscp_info.descriptor.dl_len = hid_info.dl_len; + dscp_info.descriptor.dsc_list = (UINT8 *) GKI_getbuf(dscp_info.descriptor.dl_len); + if (dscp_info.descriptor.dsc_list == NULL) + { + ALOGE("%s: Failed to allocate DSCP for CB", __FUNCTION__); + return BT_STATUS_FAIL; + } + memcpy(dscp_info.descriptor.dsc_list, &(hid_info.dsc_list), hid_info.dl_len); + + if (btif_hh_add_added_dev(*bd_addr, hid_info.attr_mask)) + { + BTA_HhAddDev(*bda, hid_info.attr_mask, hid_info.sub_class, + hid_info.app_id, dscp_info); + } + + GKI_freebuf(dscp_info.descriptor.dsc_list); + + return BT_STATUS_SUCCESS; +} +/******************************************************************************* +** +** Function get_idle_time +** +** Description Get the HID idle time +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t get_idle_time(bt_bdaddr_t *bd_addr) +{ + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + BD_ADDR* bda = (BD_ADDR*) bd_addr; + + BTIF_TRACE_DEBUG6(" addr = %02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + + if (btif_hh_cb.status == BTIF_HH_DISABLED) { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev != NULL) { + //BTA_HhGetIdle(p_dev->dev_handle); + } + else { + return BT_STATUS_FAIL; + } + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function set_idle_time +** +** Description Set the HID idle time +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t set_idle_time (bt_bdaddr_t *bd_addr, uint8_t idle_time) +{ + UNUSED(idle_time); + + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + BD_ADDR* bda = (BD_ADDR*) bd_addr; + + BTIF_TRACE_DEBUG6("addr = %02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + + if (btif_hh_cb.status == BTIF_HH_DISABLED) { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTIF_TRACE_WARNING6(" Error, device %02X:%02X:%02X:%02X:%02X:%02X not opened.", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + return BT_STATUS_FAIL; + } + else { + //BTA_HhSetIdle(p_dev->dev_handle, idle_time); + } + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function get_protocol +** +** Description Get the HID proto mode. +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t get_protocol (bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode) +{ + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + BD_ADDR* bda = (BD_ADDR*) bd_addr; + UNUSED(protocolMode); + + BTIF_TRACE_DEBUG6(" addr = %02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + + if (btif_hh_cb.status == BTIF_HH_DISABLED) { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev != NULL) { + + BTA_HhGetProtoMode(p_dev->dev_handle); + } + else { + return BT_STATUS_FAIL; + } + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function set_protocol +** +** Description Set the HID proto mode. +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t set_protocol (bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode) +{ + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + UINT8 proto_mode = protocolMode; + BD_ADDR* bda = (BD_ADDR*) bd_addr; + + BTIF_TRACE_DEBUG2("%s:proto_mode = %d", __FUNCTION__,protocolMode); + + BTIF_TRACE_DEBUG6("addr = %02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + + if (btif_hh_cb.status == BTIF_HH_DISABLED) { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTIF_TRACE_WARNING6(" Error, device %02X:%02X:%02X:%02X:%02X:%02X not opened.", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + return BT_STATUS_FAIL; + } + else if (protocolMode != BTA_HH_PROTO_RPT_MODE && protocolMode != BTA_HH_PROTO_BOOT_MODE) { + BTIF_TRACE_WARNING2("s: Error, device proto_mode = %d.", __FUNCTION__, proto_mode); + return BT_STATUS_FAIL; + } + else { + BTA_HhSetProtoMode(p_dev->dev_handle, protocolMode); + } + + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function get_report +** +** Description Send a GET_REPORT to HID device. +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t get_report (bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, uint8_t reportId, int bufferSize) +{ + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + BD_ADDR* bda = (BD_ADDR*) bd_addr; + + BTIF_TRACE_DEBUG4("%s:proto_mode = %dr_type = %d, rpt_id = %d, buf_size = %d", __FUNCTION__, + reportType, reportId, bufferSize); + + BTIF_TRACE_DEBUG6("addr = %02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + + if (btif_hh_cb.status == BTIF_HH_DISABLED) { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + + + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTIF_TRACE_ERROR6("%s: Error, device %02X:%02X:%02X:%02X:%02X:%02X not opened.", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + return BT_STATUS_FAIL; + } + else if ( ((int) reportType) <= BTA_HH_RPTT_RESRV || ((int) reportType) > BTA_HH_RPTT_FEATURE) { + BTIF_TRACE_ERROR6(" Error, device %02X:%02X:%02X:%02X:%02X:%02X not opened.", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + return BT_STATUS_FAIL; + } + else { + BTA_HhGetReport(p_dev->dev_handle, reportType, + reportId, bufferSize); + } + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function set_report +** +** Description Send a SET_REPORT to HID device. +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t set_report (bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, char* report) +{ + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + BD_ADDR* bda = (BD_ADDR*) bd_addr; + + BTIF_TRACE_DEBUG2("%s:reportType = %d", __FUNCTION__,reportType); + + BTIF_TRACE_DEBUG6("addr = %02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + + + if (btif_hh_cb.status == BTIF_HH_DISABLED) { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTIF_TRACE_ERROR6("%s: Error, device %02X:%02X:%02X:%02X:%02X:%02X not opened.", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + return BT_STATUS_FAIL; + } + else if ( ( (int) reportType) <= BTA_HH_RPTT_RESRV || ( (int) reportType) > BTA_HH_RPTT_FEATURE) { + BTIF_TRACE_ERROR6(" Error, device %02X:%02X:%02X:%02X:%02X:%02X not opened.", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + return BT_STATUS_FAIL; + } + else { + int hex_bytes_filled; + UINT8 hexbuf[200]; + UINT16 len = (strlen(report) + 1) / 2; + + if (p_dev->p_buf != NULL) { + GKI_freebuf(p_dev->p_buf); + } + p_dev->p_buf = GKI_getbuf((UINT16) (len + BTA_HH_MIN_OFFSET + sizeof(BT_HDR))); + if (p_dev->p_buf == NULL) { + BTIF_TRACE_ERROR2("%s: Error, failed to allocate RPT buffer, len = %d", __FUNCTION__, len); + return BT_STATUS_FAIL; + } + + p_dev->p_buf->len = len; + p_dev->p_buf->offset = BTA_HH_MIN_OFFSET; + + /* Build a SetReport data buffer */ + memset(hexbuf, 0, 200); + //TODO + hex_bytes_filled = ascii_2_hex(report, len, hexbuf); + ALOGI("Hex bytes filled, hex value: %d", hex_bytes_filled); + if (hex_bytes_filled) { + UINT8* pbuf_data; + pbuf_data = (UINT8*) (p_dev->p_buf + 1) + p_dev->p_buf->offset; + memcpy(pbuf_data, hexbuf, hex_bytes_filled); + BTA_HhSetReport(p_dev->dev_handle, reportType, p_dev->p_buf); + } + return BT_STATUS_SUCCESS; + } + + +} + +/******************************************************************************* +** +** Function send_data +** +** Description Send a SEND_DATA to HID device. +** +** Returns bt_status_t +** +*******************************************************************************/ +static bt_status_t send_data (bt_bdaddr_t *bd_addr, char* data) +{ + CHECK_BTHH_INIT(); + btif_hh_device_t *p_dev; + BD_ADDR* bda = (BD_ADDR*) bd_addr; + + BTIF_TRACE_DEBUG1("%s", __FUNCTION__); + + BTIF_TRACE_DEBUG6("addr = %02X:%02X:%02X:%02X:%02X:%02X", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + + if (btif_hh_cb.status == BTIF_HH_DISABLED) { + BTIF_TRACE_ERROR2("%s: Error, HH status = %d", __FUNCTION__, btif_hh_cb.status); + return BT_STATUS_FAIL; + } + + p_dev = btif_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTIF_TRACE_ERROR6("%s: Error, device %02X:%02X:%02X:%02X:%02X:%02X not opened.", + (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); + return BT_STATUS_FAIL; + } + + else { + int hex_bytes_filled; + UINT8 hexbuf[200]; + UINT16 len = (strlen(data) + 1) / 2; + + if (p_dev->p_buf != NULL) { + GKI_freebuf(p_dev->p_buf); + } + p_dev->p_buf = GKI_getbuf((UINT16) (len + BTA_HH_MIN_OFFSET + sizeof(BT_HDR))); + if (p_dev->p_buf == NULL) { + BTIF_TRACE_ERROR2("%s: Error, failed to allocate RPT buffer, len = %d", __FUNCTION__, len); + return BT_STATUS_FAIL; + } + + p_dev->p_buf->len = len; + p_dev->p_buf->offset = BTA_HH_MIN_OFFSET; + + /* Build a SetReport data buffer */ + memset(hexbuf, 0, 200); + hex_bytes_filled = ascii_2_hex(data, len, hexbuf); + BTIF_TRACE_ERROR2("Hex bytes filled, hex value: %d, %d", hex_bytes_filled, len); + + if (hex_bytes_filled) { + UINT8* pbuf_data; + pbuf_data = (UINT8*) (p_dev->p_buf + 1) + p_dev->p_buf->offset; + memcpy(pbuf_data, hexbuf, hex_bytes_filled); + p_dev->p_buf->layer_specific = BTA_HH_RPTT_OUTPUT; + BTA_HhSendData(p_dev->dev_handle, *bda, p_dev->p_buf); + return BT_STATUS_SUCCESS; + } + + } + return BT_STATUS_FAIL; +} + + +/******************************************************************************* +** +** Function cleanup +** +** Description Closes the HH interface +** +** Returns bt_status_t +** +*******************************************************************************/ +static void cleanup( void ) +{ + BTIF_TRACE_EVENT1("%s", __FUNCTION__); + btif_hh_device_t *p_dev; + int i; + if (btif_hh_cb.status == BTIF_HH_DISABLED) { + BTIF_TRACE_WARNING2("%s: HH disabling or disabled already, status = %d", __FUNCTION__, btif_hh_cb.status); + return; + } + btif_hh_cb.status = BTIF_HH_DISABLING; + for (i = 0; i < BTIF_HH_MAX_HID; i++) { + p_dev = &btif_hh_cb.devices[i]; + if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN && p_dev->fd >= 0) { + BTIF_TRACE_DEBUG2("%s: Closing uhid fd = %d", __FUNCTION__, p_dev->fd); + bta_hh_co_destroy(p_dev->fd); + p_dev->fd = -1; + p_dev->hh_keep_polling = 0; + p_dev->hh_poll_thread_id = -1; + } + } + + if (bt_hh_callbacks) + { + btif_disable_service(BTA_HID_SERVICE_ID); + bt_hh_callbacks = NULL; + } + +} + +static const bthh_interface_t bthhInterface = { + sizeof(bthhInterface), + init, + connect, + disconnect, + virtual_unplug, + set_info, + get_protocol, + set_protocol, +// get_idle_time, +// set_idle_time, + get_report, + set_report, + send_data, + cleanup, +}; + +/******************************************************************************* +** +** Function btif_hh_execute_service +** +** Description Initializes/Shuts down the service +** +** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise +** +*******************************************************************************/ +bt_status_t btif_hh_execute_service(BOOLEAN b_enable) +{ + if (b_enable) + { + /* Enable and register with BTA-HH */ + BTA_HhEnable(BTA_SEC_NONE, bte_hh_evt); + } + else { + /* Disable HH */ + BTA_HhDisable(); + } + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function btif_hh_get_interface +** +** Description Get the hh callback interface +** +** Returns bthh_interface_t +** +*******************************************************************************/ +const bthh_interface_t *btif_hh_get_interface() +{ + BTIF_TRACE_EVENT1("%s", __FUNCTION__); + return &bthhInterface; +} diff --git a/stack/hid/hidd_api.c b/stack/hid/hidd_api.c new file mode 100644 index 000000000..1868671f9 --- /dev/null +++ b/stack/hid/hidd_api.c @@ -0,0 +1,599 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID HOST API entry points + * + ******************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "gki.h" +#include "bt_types.h" +#include "hiddefs.h" +#include "hidh_api.h" +#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; +#endif + +static void hidh_search_callback (UINT16 sdp_result); + +/******************************************************************************* +** +** Function HID_HostGetSDPRecord +** +** Description This function reads the device SDP record +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostGetSDPRecord ( BD_ADDR addr, tSDP_DISCOVERY_DB *p_db, UINT32 db_len, + tHID_HOST_SDP_CALLBACK *sdp_cback ) +{ + tSDP_UUID uuid_list; + + if( hh_cb.sdp_busy ) + return HID_ERR_SDP_BUSY; + + uuid_list.len = 2; + uuid_list.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE; + + hh_cb.p_sdp_db = p_db; + SDP_InitDiscoveryDb (p_db, db_len, 1, &uuid_list, 0, NULL); + + if (SDP_ServiceSearchRequest (addr, p_db, hidh_search_callback)) + { + hh_cb.sdp_cback = sdp_cback ; + hh_cb.sdp_busy = TRUE; + return HID_SUCCESS; + } + else + return HID_ERR_NO_RESOURCES; +} + +void hidh_get_str_attr( tSDP_DISC_REC *p_rec, UINT16 attr_id, UINT16 max_len, char *str ) +{ + tSDP_DISC_ATTR *p_attr; + UINT16 name_len; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, attr_id)) != NULL) + { + if((name_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type)) < max_len ) + { + memcpy( str, (char *) p_attr->attr_value.v.array, name_len ); + str[name_len] = '\0'; + } + else + { + memcpy( str, (char *) p_attr->attr_value.v.array, max_len-1 ); + str[max_len-1] = '\0'; + } + } + else + str[0] = '\0'; +} + + +static void hidh_search_callback (UINT16 sdp_result) +{ + tSDP_DISCOVERY_DB *p_db = hh_cb.p_sdp_db; + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_subattr1, *p_subattr2, *p_repdesc; + tBT_UUID hid_uuid; + tHID_DEV_SDP_INFO *p_nvi = &hh_cb.sdp_rec; + UINT16 attr_mask = 0; + + hid_uuid.len = LEN_UUID_16; + hid_uuid.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE; + + hh_cb.sdp_busy = FALSE; + + if (sdp_result != SDP_SUCCESS) + { + hh_cb.sdp_cback(sdp_result, 0, NULL); + return; + } + + if ((p_rec = SDP_FindServiceUUIDInDb (p_db, &hid_uuid, NULL)) == NULL) + { + hh_cb.sdp_cback(HID_SDP_NO_SERV_UUID, 0, NULL); + return; + } + + memset (&hh_cb.sdp_rec, 0, sizeof( tHID_DEV_SDP_INFO )); + + /* First, verify the mandatory fields we care about */ + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DESCRIPTOR_LIST)) == NULL) + || (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) + || ((p_subattr1 = p_attr->attr_value.v.p_sub_attr) == NULL) + || (SDP_DISC_ATTR_TYPE(p_subattr1->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) + || ((p_subattr2 = p_subattr1->attr_value.v.p_sub_attr) == NULL) + || ((p_repdesc = p_subattr2->p_next_attr) == NULL) + || (SDP_DISC_ATTR_TYPE(p_repdesc->attr_len_type) != TEXT_STR_DESC_TYPE)) + { + hh_cb.sdp_cback(HID_SDP_MANDATORY_MISSING, 0, NULL); + return; + } + + if ((p_nvi->dscp_info.dl_len = SDP_DISC_ATTR_LEN(p_repdesc->attr_len_type)) != 0) + p_nvi->dscp_info.dsc_list = (UINT8 *) &p_repdesc->attr_value; + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_VIRTUAL_CABLE)) != NULL) && + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_VIRTUAL_CABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_RECONNECT_INITIATE)) != NULL) && + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_RECONN_INIT; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_NORMALLY_CONNECTABLE)) != NULL) && + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_NORMALLY_CONNECTABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SDP_DISABLE)) != NULL)&& + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_SDP_DISABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_BATTERY_POWER)) != NULL)&& + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_BATTERY_POWER; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_REMOTE_WAKE)) != NULL)&& + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_REMOTE_WAKE; + } + + hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_NAME, HID_MAX_SVC_NAME_LEN, p_nvi->svc_name ); + hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_DESCRIPTION, HID_MAX_SVC_DESCR_LEN, p_nvi->svc_descr ); + hidh_get_str_attr( p_rec, ATTR_ID_PROVIDER_NAME, HID_MAX_PROV_NAME_LEN, p_nvi->prov_name ); + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_RELNUM)) != NULL)) + { + p_nvi->rel_num = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_COUNTRY_CODE)) != NULL)) + { + p_nvi->ctry_code = p_attr->attr_value.v.u8; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_SUBCLASS)) != NULL)) + { + p_nvi->sub_class = p_attr->attr_value.v.u8; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_PARSER_VERSION)) != NULL)) + { + p_nvi->hpars_ver = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_LINK_SUPERVISION_TO)) != NULL)) + { + attr_mask |= HID_SUP_TOUT_AVLBL; + p_nvi->sup_timeout = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MAX_LAT)) != NULL)) + { + attr_mask |= HID_SSR_MAX_LATENCY; + p_nvi->ssr_max_latency = p_attr->attr_value.v.u16; + } + else + p_nvi->ssr_max_latency = HID_SSR_PARAM_INVALID; + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MIN_TOUT)) != NULL)) + { + attr_mask |= HID_SSR_MIN_TOUT; + p_nvi->ssr_min_tout = p_attr->attr_value.v.u16; + } + else + p_nvi->ssr_min_tout = HID_SSR_PARAM_INVALID; + + hh_cb.sdp_rec.p_sdp_layer_rec = p_rec; + hh_cb.sdp_cback(SDP_SUCCESS, attr_mask, &hh_cb.sdp_rec); +} + + +/******************************************************************************* +** +** Function HID_HostInit +** +** Description This function initializes the control block and trace variable +** +** Returns void +** +*******************************************************************************/ +void HID_HostInit (void) +{ + memset(&hh_cb, 0, sizeof(tHID_HOST_CTB)); + +#if defined(HID_INITIAL_TRACE_LEVEL) + hh_cb.trace_level = HID_INITIAL_TRACE_LEVEL; +#else + hh_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif +} + +/******************************************************************************* +** +** Function HID_HostSetTraceLevel +** +** Description This function sets the trace level for HID Host. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 HID_HostSetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + hh_cb.trace_level = new_level; + + return (hh_cb.trace_level); +} + +/******************************************************************************* +** +** Function HID_HostRegister +** +** Description This function registers HID-Host with lower layers +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback) +{ + tHID_STATUS st; + + if( hh_cb.reg_flag ) + return HID_ERR_ALREADY_REGISTERED; + + if( dev_cback == NULL ) + return HID_ERR_INVALID_PARAM; + + /* Register with L2CAP */ + if( (st = hidh_conn_reg()) != HID_SUCCESS ) + { + return st; + } + + hh_cb.callback = dev_cback ; + hh_cb.reg_flag = TRUE; + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function HID_HostDeregister +** +** Description This function is called when the host is about power down. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostDeregister(void) +{ + UINT8 i; + + if( !hh_cb.reg_flag ) + return (HID_ERR_NOT_REGISTERED); + + for( i=0; i<HID_HOST_MAX_DEVICES; i++ ) + { + HID_HostRemoveDev( i ) ; + } + + hidh_conn_dereg(); + hh_cb.reg_flag = FALSE; + + return (HID_SUCCESS) ; +} + +/******************************************************************************* +** +** Function HID_HostAddDev +** +** Description This is called so HID-host may manage this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +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); + + for( i=0; i<HID_HOST_MAX_DEVICES; i++) + { + if((hh_cb.devices[i].in_use) && + (!memcmp(addr, hh_cb.devices[i].addr, BD_ADDR_LEN))) + break; + } + + if (i== HID_HOST_MAX_DEVICES ) + { + for( i=0; i<HID_HOST_MAX_DEVICES; i++) + { + if( !hh_cb.devices[i].in_use) + break; + } + } + + if( i==HID_HOST_MAX_DEVICES ) + return HID_ERR_NO_RESOURCES; + + if (!hh_cb.devices[i].in_use) + { + hh_cb.devices[i].in_use = TRUE; + memcpy( hh_cb.devices[i].addr, addr, sizeof( BD_ADDR ) ) ; + hh_cb.devices[i].state = HID_DEV_NO_CONN; + hh_cb.devices[i].conn_tries = 0 ; + } + + if (attr_mask != HID_ATTR_MASK_IGNORE) + hh_cb.devices[i].attr_mask = attr_mask; + + *handle = i; + + return (HID_SUCCESS); +} + + +/******************************************************************************* +** +** Function HID_HostRemoveDev +** +** Description This removes the device from list devices that host has to manage. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostRemoveDev ( UINT8 dev_handle ) +{ + if( !hh_cb.reg_flag ) + return (HID_ERR_NOT_REGISTERED); + + if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) + return HID_ERR_INVALID_PARAM; + + HID_HostCloseDev( dev_handle ) ; + hh_cb.devices[dev_handle].in_use = FALSE; + hh_cb.devices[dev_handle].conn.conn_state = HID_CONN_STATE_UNUSED; + hh_cb.devices[dev_handle].conn.ctrl_cid = hh_cb.devices[dev_handle].conn.intr_cid = 0; + + return HID_SUCCESS; +} + +/******************************************************************************* +** +** Function HID_HostOpenDev +** +** Description This function is called when the user wants to initiate a +** connection attempt to a device. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostOpenDev ( UINT8 dev_handle ) +{ + if( !hh_cb.reg_flag ) + return (HID_ERR_NOT_REGISTERED); + + if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) + return HID_ERR_INVALID_PARAM; + + if( hh_cb.devices[dev_handle].state != HID_DEV_NO_CONN ) + return HID_ERR_ALREADY_CONN; + + hh_cb.devices[dev_handle].conn_tries = 1; + return hidh_conn_initiate( dev_handle ); +} + +/******************************************************************************* +** +** Function HID_HostWriteDev +** +** Description This function is called when the host has a report to send. +** +** report_id: is only used on GET_REPORT transaction if is specified. +** only valid when it's a non-zero value. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostWriteDev( UINT8 dev_handle, UINT8 t_type, + UINT8 param, UINT16 data, UINT8 report_id, BT_HDR *pbuf ) +{ + tHID_STATUS status = HID_SUCCESS; + + if( !hh_cb.reg_flag ) + { + HIDH_TRACE_ERROR0("HID_ERR_NOT_REGISTERED"); + status = HID_ERR_NOT_REGISTERED; + } + + if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) + { + HIDH_TRACE_ERROR0("HID_ERR_INVALID_PARAM"); + status = HID_ERR_INVALID_PARAM; + } + + else if( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) + { + HIDH_TRACE_ERROR1("HID_ERR_NO_CONNECTION dev_handle %d", dev_handle); + status = HID_ERR_NO_CONNECTION; + } + + if (status != HID_SUCCESS) + { + if (pbuf) + GKI_freebuf ((void *)pbuf); + } + else + status = hidh_conn_snd_data( dev_handle, t_type, param, data, report_id, pbuf ) ; + + return status; +} + +/******************************************************************************* +** +** Function HID_HostCloseDev +** +** Description This function disconnects the device. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostCloseDev( UINT8 dev_handle ) +{ + if( !hh_cb.reg_flag ) + return (HID_ERR_NOT_REGISTERED); + + if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) + return HID_ERR_INVALID_PARAM; + + hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1; + btu_stop_timer( &(hh_cb.devices[dev_handle].conn.timer_entry) ) ; + + if( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) + return HID_ERR_NO_CONNECTION; + + hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1; + return hidh_conn_disconnect( dev_handle ); +} + +tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl ) +{ + 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_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_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_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_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_HIDH_INTR, + BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) + { + HIDH_TRACE_ERROR0 ("Security Registration 6 failed"); + return (HID_ERR_NO_RESOURCES); + } + + 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; i<HID_HOST_MAX_DEVICES; i++) + { + if ((hh_cb.devices[i].in_use) && + (memcmp(bd_addr, hh_cb.devices[i].addr, BD_ADDR_LEN) == 0)) + return TRUE; + } + /* Check if this device is marked as HID Device in IOP Dev */ + HIDH_TRACE_DEBUG0("hid_known_hid_device:remote is not HID device"); + return FALSE; +} diff --git a/stack/hid/hidd_conn.c b/stack/hid/hidd_conn.c new file mode 100644 index 000000000..86b41d36f --- /dev/null +++ b/stack/hid/hidd_conn.c @@ -0,0 +1,1043 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the connection interface functions + * + ******************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + + +#include "gki.h" +#include "bt_types.h" + +#include "l2cdefs.h" +#include "l2c_api.h" + +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" + +#include "hiddefs.h" + +#include "hidh_api.h" +#include "hidh_int.h" +#include "bt_utils.h" + +static UINT8 find_conn_by_cid (UINT16 cid); +static void hidh_conn_retry (UINT8 dhandle); + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, + UINT16 psm, UINT8 l2cap_id); +static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); +static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result); +static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested); + +static const tL2CAP_APPL_INFO hst_reg_info = +{ + hidh_l2cif_connect_ind, + hidh_l2cif_connect_cfm, + NULL, + hidh_l2cif_config_ind, + hidh_l2cif_config_cfm, + hidh_l2cif_disconnect_ind, + hidh_l2cif_disconnect_cfm, + NULL, + hidh_l2cif_data_ind, + hidh_l2cif_cong_ind, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function hidh_l2cif_reg +** +** Description This function initializes the SDP unit. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS hidh_conn_reg (void) +{ + int xx; + + /* Initialize the L2CAP configuration. We only care about MTU and flush */ + memset(&hh_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + hh_cb.l2cap_cfg.mtu_present = TRUE; + hh_cb.l2cap_cfg.mtu = HID_HOST_MTU; + hh_cb.l2cap_cfg.flush_to_present = TRUE; + hh_cb.l2cap_cfg.flush_to = HID_HOST_FLUSH_TO; + + /* Now, register with L2CAP */ + if (!L2CA_Register (HID_PSM_CONTROL, (tL2CAP_APPL_INFO *) &hst_reg_info)) + { + HIDH_TRACE_ERROR0 ("HID Control Registration failed"); + return (HID_ERR_L2CAP_FAILED) ; + } + if (!L2CA_Register (HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *) &hst_reg_info)) + { + L2CA_Deregister( HID_PSM_CONTROL ) ; + HIDH_TRACE_ERROR0 ("HID Interrupt Registration failed"); + return (HID_ERR_L2CAP_FAILED) ; + } + + for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++) + { + hh_cb.devices[xx].in_use = FALSE ; + hh_cb.devices[xx].conn.conn_state = HID_CONN_STATE_UNUSED; + } + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function hidh_conn_disconnect +** +** Description This function disconnects a connection. +** +** Returns TRUE if disconnect started, FALSE if already disconnected +** +*******************************************************************************/ +tHID_STATUS hidh_conn_disconnect (UINT8 dhandle) +{ + tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn; + + HIDH_TRACE_EVENT0 ("HID-Host disconnect"); + + if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) + { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; + + /* Disconnect both interrupt and control channels */ + if (p_hcon->intr_cid) + L2CA_DisconnectReq (p_hcon->intr_cid); + + if (p_hcon->ctrl_cid) + L2CA_DisconnectReq (p_hcon->ctrl_cid); + } + else + { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + } + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function hidh_sec_check_complete_term +** +** Description HID security check complete callback function. +** +** Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise +** send security block L2C connection response. +** +*******************************************************************************/ +void hidh_sec_check_complete_term (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tHID_HOST_DEV_CTB *p_dev= (tHID_HOST_DEV_CTB *) p_ref_data; + UNUSED(bd_addr); + + if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) + { + p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */ + + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* Send a Configuration Request. */ + L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg); + + } + /* security check fail */ + else if (res != BTM_SUCCESS) + { + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */ + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); + } +} + +/******************************************************************************* +** +** Function hidh_l2cif_connect_ind +** +** Description This function handles an inbound connection indication +** from L2CAP. This is the case where we are acting as a +** server. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ + tHID_CONN *p_hcon; + BOOLEAN bAccept = TRUE; + UINT8 i = HID_HOST_MAX_DEVICES; + tHID_HOST_DEV_CTB *p_dev; + + HIDH_TRACE_EVENT2 ("HID-Host Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); + + /* 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; + } + + p_hcon = &hh_cb.devices[i].conn; + p_dev = &hh_cb.devices[i]; + + /* Check we are in the correct state for this */ + if (psm == HID_PSM_INTERRUPT) + { + if (p_hcon->ctrl_cid == 0) + { + 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-Host Rcvd INTR L2CAP conn ind, wrong state: %d", + p_hcon->conn_state); + bAccept = FALSE; + } + } + else /* CTRL channel */ + { +#if defined(HID_HOST_ACPT_NEW_CONN) && (HID_HOST_ACPT_NEW_CONN == TRUE) + p_hcon->ctrl_cid = p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; +#else + if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) + { + HIDH_TRACE_WARNING1 ("HID-Host - Rcvd CTL L2CAP conn ind, wrong state: %d", + p_hcon->conn_state); + bAccept = FALSE; + } +#endif + } + + if (!bAccept) + { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + + if (psm == HID_PSM_CONTROL) + { + p_hcon->conn_flags = 0; + p_hcon->ctrl_cid = l2cap_cid; + p_hcon->ctrl_id = l2cap_id; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to 'connection failure' */ + + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + if(btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL, + FALSE, BTM_SEC_PROTO_HID, + (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN, + &hidh_sec_check_complete_term, p_dev) == BTM_CMD_STARTED) + { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + + return; + } + + /* Transition to the next appropriate state, configuration */ + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + p_hcon->intr_cid = l2cap_cid; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); + + HIDH_TRACE_EVENT2 ("HID-Host Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x", + psm, l2cap_cid); +} + +/******************************************************************************* +** +** Function hidh_proc_repage_timeout +** +** Description This function handles timeout (to page device). +** +** Returns void +** +*******************************************************************************/ +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, hh_cb.devices[p_tle->param].addr, + HID_HDEV_EVT_RETRYING, hh_cb.devices[p_tle->param].conn_tries, NULL ) ; +} + +/******************************************************************************* +** +** Function hidh_sec_check_complete_orig +** +** Description This function checks to see if security procedures are being +** carried out or not.. +** +** Returns void +** +*******************************************************************************/ +void hidh_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tHID_HOST_DEV_CTB *p_dev = (tHID_HOST_DEV_CTB *) p_ref_data; + UINT8 dhandle; +#if (HID_HOST_MAX_CONN_RETRY > 0) + UINT32 cb_res = HID_ERR_AUTH_FAILED; +#endif + UINT32 reason; + UNUSED(bd_addr); + + 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-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-Host INTR Originate failed"); + reason = HID_L2CAP_REQ_FAIL ; + hidh_conn_disconnect (dhandle); + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + return; + } + else + { + /* Transition to the next appropriate state, waiting for connection confirm on control channel. */ + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + + if( res != BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) + { +#if (HID_HOST_MAX_CONN_RETRY > 0) + if( res == BTM_DEVICE_TIMEOUT ) + { + if( p_dev->conn_tries <= HID_HOST_MAX_CONN_RETRY ) + { + hidh_conn_retry (dhandle); + return; + } + else + cb_res = HID_L2CAP_CONN_FAIL | HCI_ERR_PAGE_TIMEOUT ; + } +#endif + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */ + hidh_conn_disconnect(dhandle); + } + +} + +/******************************************************************************* +** +** Function hidh_l2cif_connect_cfm +** +** Description This function handles the connect confirm events +** from L2CAP. This is the case when we are acting as a +** client and have sent a connect request. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT32 reason; + tHID_HOST_DEV_CTB *p_dev = NULL; + + /* Find CCB based on CID, and verify we are in a state to accept this message */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + { + p_dev = &hh_cb.devices[dhandle]; + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if ((p_hcon == NULL) + || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG)) + || ((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-Host Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); + return; + } + + if (result != L2CAP_CONN_OK) + { + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->ctrl_cid = 0; + else + p_hcon->intr_cid = 0; + + hidh_conn_disconnect(dhandle); + +#if (HID_HOST_MAX_CONN_RETRY > 0) + if( (hh_cb.devices[dhandle].conn_tries <= HID_HOST_MAX_CONN_RETRY) && + (result == HCI_ERR_CONNECTION_TOUT || result == HCI_ERR_UNSPECIFIED || + result == HCI_ERR_PAGE_TIMEOUT) ) + { + hidh_conn_retry(dhandle); + } + else +#endif + { + reason = HID_L2CAP_CONN_FAIL | (UINT32) result ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + } + return; + } + /* receive Control Channel connect confirmation */ + if (l2cap_cid == p_hcon->ctrl_cid) + { + /* check security requirement */ + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to "connection failure" */ + + btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL, + TRUE, BTM_SEC_PROTO_HID, + (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN, + &hidh_sec_check_complete_orig, p_dev); + } + else + { + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + } + + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); + + HIDH_TRACE_EVENT1 ("HID-Host got CTRL conn cnf, sent cfg req, CID: 0x%x", l2cap_cid); + return; +} + +/******************************************************************************* +** +** Function hidh_l2cif_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + tHID_HOST_DEV_CTB *p_dev; + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + { + p_dev = &hh_cb.devices[dhandle]; + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + 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)) + p_hcon->rem_mtu_size = HID_HOST_MTU; + else + p_hcon->rem_mtu_size = p_cfg->mtu; + + /* For now, always accept configuration from the other side */ + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + + L2CA_ConfigRsp (l2cap_cid, p_cfg); + + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE; + else + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE; + + /* If all configuration is complete, change state and tell management we are up */ + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) + && (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) + { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + + hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT32 reason; + + 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 ) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + /* If configuration failed, disconnect the channel(s) */ + if (p_cfg->result != L2CAP_CFG_OK) + { + hidh_conn_disconnect (dhandle); + reason = HID_L2CAP_CFG_FAIL | (UINT32) p_cfg->result ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + return; + } + + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE; + else + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE; + + /* If all configuration is complete, change state and tell management we are up */ + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) + && (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) + { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + + hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_disconnect_ind +** +** Description This function handles a disconnect event from L2CAP. If +** requested to, we ack the disconnect before dropping the CCB +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT16 disc_res = HCI_SUCCESS; + UINT16 hid_close_evt_reason; + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + 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-Host Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); + + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; + + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->ctrl_cid = 0; + else + p_hcon->intr_cid = 0; + + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) + { + hh_cb.devices[dhandle].state = HID_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + + if( !ack_needed ) + disc_res = btm_get_acl_disc_reason_code(); + +#if (HID_HOST_MAX_CONN_RETRY > 0) + if( (disc_res == HCI_ERR_CONNECTION_TOUT || disc_res == HCI_ERR_UNSPECIFIED) && + (!(hh_cb.devices[dhandle].attr_mask & HID_RECONN_INIT)) && + (hh_cb.devices[dhandle].attr_mask & HID_NORMALLY_CONNECTABLE)) + { + hh_cb.devices[dhandle].conn_tries = 0; + hh_cb.devices[dhandle].conn.timer_entry.param = (UINT32) dhandle; + btu_start_timer (&(hh_cb.devices[dhandle].conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN); + } + else +#endif + { + /* Set reason code for HID_HDEV_EVT_CLOSE */ + hid_close_evt_reason = p_hcon->disc_reason; + + /* If we got baseband sent HCI_DISCONNECT_COMPLETE_EVT due to security failure, then set reason to HID_ERR_AUTH_FAILED */ + if ((disc_res == HCI_ERR_AUTH_FAILURE) || + (disc_res == HCI_ERR_KEY_MISSING) || + (disc_res == HCI_ERR_HOST_REJECT_SECURITY) || + (disc_res == HCI_ERR_PAIRING_NOT_ALLOWED) || + (disc_res == HCI_ERR_UNIT_KEY_USED) || + (disc_res == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) || + (disc_res == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) || + (disc_res == HCI_ERR_REPEATED_ATTEMPTS)) + { + hid_close_evt_reason = HID_ERR_AUTH_FAILED; + } + + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, hid_close_evt_reason, NULL ) ; + } + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_disconnect_cfm +** +** Description This function handles a disconnect confirm event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UNUSED(result); + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); + return; + } + + 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; + else + p_hcon->intr_cid = 0; + + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) + { + hh_cb.devices[dhandle].state = HID_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_cong_ind +** +** Description This function handles a congestion status event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid); + return; + } + + 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; + else + { + p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED; + + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the SDP +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the SDP +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) +{ + UINT8 *p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 ttype, param, rep_type, evt; + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + + 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) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid); + GKI_freebuf (p_msg); + return; + } + + + ttype = HID_GET_TRANS_FROM_HDR(*p_data); + param = HID_GET_PARAM_FROM_HDR(*p_data); + rep_type = param & HID_PAR_REP_TYPE_MASK; + p_data++; + + /* Get rid of the data type */ + p_msg->len--; + p_msg->offset++; + + switch (ttype) + { + case HID_TRANS_HANDSHAKE: + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_HANDSHAKE, param, NULL); + GKI_freebuf (p_msg); + break; + + case HID_TRANS_CONTROL: + switch (param) + { + case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG: + hidh_conn_disconnect( dhandle ) ; + /* Device is unplugging from us. Tell USB */ + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_VC_UNPLUG, 0, NULL); + break; + + default: + break; + } + GKI_freebuf (p_msg); + break; + + + 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, 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, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg); + break; + + default: + GKI_freebuf (p_msg); + break; + } + +} + +/******************************************************************************* +** +** Function hidh_conn_snd_data +** +** Description This function is sends out data. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS hidh_conn_snd_data (UINT8 dhandle, UINT8 trans_type, UINT8 param, + UINT16 data, UINT8 report_id, BT_HDR *buf) +{ + tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn; + BT_HDR *p_buf; + UINT8 *p_out; + UINT16 bytes_copied; + BOOLEAN seg_req = FALSE; + UINT16 data_size; + UINT16 cid; + UINT8 pool_id; + UINT8 use_data = 0 ; + BOOLEAN blank_datc = FALSE; + + if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) + { + if (buf) + GKI_freebuf ((void *)buf); + return( HID_ERR_CONGESTED ); + } + + switch( trans_type ) + { + case HID_TRANS_CONTROL: + case HID_TRANS_GET_REPORT: + case HID_TRANS_SET_REPORT: + case HID_TRANS_GET_PROTOCOL: + case HID_TRANS_SET_PROTOCOL: + case HID_TRANS_GET_IDLE: + case HID_TRANS_SET_IDLE: + cid = p_hcon->ctrl_cid; + pool_id = HID_CONTROL_POOL_ID; + break; + case HID_TRANS_DATA: + cid = p_hcon->intr_cid; + pool_id = HID_INTERRUPT_POOL_ID; + break; + default: + return (HID_ERR_INVALID_PARAM) ; + } + + if( trans_type == HID_TRANS_SET_IDLE ) + use_data = 1; + else if( (trans_type == HID_TRANS_GET_REPORT) && (param & 0x08) ) + use_data = 2; + + do + { + if ( buf == NULL || blank_datc ) + { + if((p_buf = (BT_HDR *)GKI_getpoolbuf (pool_id)) == NULL) + return (HID_ERR_NO_RESOURCES); + + p_buf->offset = L2CAP_MIN_OFFSET; + seg_req = FALSE; + data_size = 0; + bytes_copied = 0; + blank_datc = FALSE; + } + else if ( (buf->len > (p_hcon->rem_mtu_size - 1))) + { + if((p_buf = (BT_HDR *)GKI_getpoolbuf (pool_id)) == NULL) + return (HID_ERR_NO_RESOURCES); + + p_buf->offset = L2CAP_MIN_OFFSET; + seg_req = TRUE; + data_size = buf->len; + bytes_copied = p_hcon->rem_mtu_size - 1; + } + else + { + p_buf = buf ; + p_buf->offset -= 1; + seg_req = FALSE; + data_size = buf->len; + bytes_copied = buf->len; + } + + p_out = (UINT8 *)(p_buf + 1) + p_buf->offset; + *p_out++ = HID_BUILD_HDR(trans_type, param); + + /* If report ID required for this device */ + if( (trans_type == HID_TRANS_GET_REPORT) && (report_id != 0) ) + { + *p_out = report_id; + data_size = bytes_copied = 1; + } + + + if (seg_req) + { + memcpy (p_out, (((UINT8 *)(buf+1)) + buf->offset), bytes_copied); + buf->offset += bytes_copied; + buf->len -= bytes_copied; + } + else if( use_data == 1) + { + *(p_out+bytes_copied) = data & 0xff; + } + else if( use_data == 2 ) + { + *(p_out+bytes_copied) = data & 0xff; + *(p_out+bytes_copied+1) = (data >> 8) & 0xff ; + } + + p_buf->len = bytes_copied + 1 + use_data; + data_size -= bytes_copied; + + /* Send the buffer through L2CAP */ + if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) || (!L2CA_DataWrite (cid, p_buf))) + return (HID_ERR_CONGESTED); + + if (data_size) + trans_type = HID_TRANS_DATAC; + else if( bytes_copied == (p_hcon->rem_mtu_size - 1) ) + { + trans_type = HID_TRANS_DATAC; + blank_datc = TRUE; + } + + } while ((data_size != 0) || blank_datc ) ; + + return (HID_SUCCESS); +} +/******************************************************************************* +** +** Function hidh_conn_initiate +** +** Description This function is called by the management to create a connection. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS hidh_conn_initiate (UINT8 dhandle) +{ + 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]; + + if( p_dev->conn.conn_state != HID_CONN_STATE_UNUSED ) + return( HID_ERR_CONN_IN_PROCESS ); + + p_dev->conn.ctrl_cid = 0; + p_dev->conn.intr_cid = 0; + p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */ + + /* We are the originator of this connection */ + p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG; + + if(p_dev->attr_mask & HID_SEC_REQUIRED) + { + service_id = BTM_SEC_SERVICE_HIDH_SEC_CTRL; + mx_chan_id = HID_SEC_CHN; + } + BTM_SetOutService (p_dev->addr, service_id, mx_chan_id); + + /* 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-Host Originate failed"); + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, + HID_ERR_L2CAP_FAILED, NULL ) ; + } + else + { + /* Transition to the next appropriate state, waiting for connection confirm on control channel. */ + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL; + } + + return( HID_SUCCESS ); +} + + +/******************************************************************************* +** +** Function find_conn_by_cid +** +** Description This function finds a connection control block based on CID +** +** Returns address of control block, or NULL if not found +** +*******************************************************************************/ +static UINT8 find_conn_by_cid (UINT16 cid) +{ + UINT8 xx; + + for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++) + { + if ((hh_cb.devices[xx].in_use) && (hh_cb.devices[xx].conn.conn_state != HID_CONN_STATE_UNUSED) + && ((hh_cb.devices[xx].conn.ctrl_cid == cid) || (hh_cb.devices[xx].conn.intr_cid == cid))) + break; + } + + return (xx); +} + +void hidh_conn_dereg( void ) +{ + L2CA_Deregister (HID_PSM_CONTROL); + L2CA_Deregister (HID_PSM_INTERRUPT); +} + +/******************************************************************************* +** +** Function hidh_conn_retry +** +** Description This function is called to retry a failed connection. +** +** Returns void +** +*******************************************************************************/ +static void hidh_conn_retry( UINT8 dhandle ) +{ + tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle]; + + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + p_dev->conn.timer_entry.param = (UINT32) dhandle; +#if (HID_HOST_REPAGE_WIN > 0) + btu_start_timer (&(p_dev->conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN); +#else + hidh_proc_repage_timeout( &(p_dev->conn.timer_entry) ); +#endif +} diff --git a/stack/hid/hidd_int.h b/stack/hid/hidd_int.h new file mode 100644 index 000000000..ad074129c --- /dev/null +++ b/stack/hid/hidd_int.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains HID HOST internal definitions + * + ******************************************************************************/ + +#ifndef HIDH_INT_H +#define HIDH_INT_H + +#include "hidh_api.h" +#include "hid_conn.h" +#include "l2c_api.h" + +enum { + HID_DEV_NO_CONN, + HID_DEV_CONNECTED +}; + +typedef struct per_device_ctb +{ + BOOLEAN in_use; + BD_ADDR addr; /* BD-Addr of the host device */ + UINT16 attr_mask; /* 0x01- virtual_cable; 0x02- normally_connectable; 0x03- reconn_initiate; + 0x04- sdp_disable; */ + UINT8 state; /* Device state if in HOST-KNOWN mode */ + UINT8 conn_substate; + UINT8 conn_tries; /* Remembers to the number of connection attempts while CONNECTING */ + + tHID_CONN conn; /* L2CAP channel info */ +} tHID_HOST_DEV_CTB; + +typedef struct host_ctb +{ + tHID_HOST_DEV_CTB devices[HID_HOST_MAX_DEVICES]; + tHID_HOST_DEV_CALLBACK *callback; /* Application callbacks */ + tL2CAP_CFG_INFO l2cap_cfg; + +#define MAX_SERVICE_DB_SIZE 4000 + + BOOLEAN sdp_busy; + tHID_HOST_SDP_CALLBACK *sdp_cback; + tSDP_DISCOVERY_DB *p_sdp_db; + tHID_DEV_SDP_INFO sdp_rec; + BOOLEAN reg_flag; + UINT8 trace_level; +} tHID_HOST_CTB; + +extern tHID_STATUS hidh_conn_snd_data(UINT8 dhandle, UINT8 trans_type, UINT8 param, \ + UINT16 data,UINT8 rpt_id, BT_HDR *buf); +extern tHID_STATUS hidh_conn_reg (void); +extern void hidh_conn_dereg( void ); +extern tHID_STATUS hidh_conn_disconnect (UINT8 dhandle); +extern tHID_STATUS hidh_conn_initiate (UINT8 dhandle); +extern void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle); + +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if HID_DYNAMIC_MEMORY == FALSE +HID_API extern tHID_HOST_CTB hh_cb; +#else +HID_API extern tHID_HOST_CTB *hidh_cb_ptr; +#define hh_cb (*hidh_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/stack/include/hidd_api.h b/stack/include/hidd_api.h new file mode 100644 index 000000000..27e8ac5de --- /dev/null +++ b/stack/include/hidd_api.h @@ -0,0 +1,236 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef HIDH_API_H +#define HIDH_API_H + +#include "hiddefs.h" +#include "sdp_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +enum { + HID_SDP_NO_SERV_UUID = (SDP_ILLEGAL_PARAMETER+1), + HID_SDP_MANDATORY_MISSING +}; + +/* Attributes mask values to be used in HID_HostAddDev API */ +#define HID_VIRTUAL_CABLE 0x0001 +#define HID_NORMALLY_CONNECTABLE 0x0002 +#define HID_RECONN_INIT 0x0004 +#define HID_SDP_DISABLE 0x0008 +#define HID_BATTERY_POWER 0x0010 +#define HID_REMOTE_WAKE 0x0020 +#define HID_SUP_TOUT_AVLBL 0x0040 +#define HID_SSR_MAX_LATENCY 0x0080 +#define HID_SSR_MIN_TOUT 0x0100 + +#define HID_SEC_REQUIRED 0x8000 +#define HID_ATTR_MASK_IGNORE 0 + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +typedef void (tHID_HOST_SDP_CALLBACK) (UINT16 result, UINT16 attr_mask, + tHID_DEV_SDP_INFO *sdp_rec ); + +/* HID-HOST returns the events in the following table to the application via tHID_HOST_DEV_CALLBACK +HID_HDEV_EVT_OPEN Connected to device with Interrupt and Control Channels in OPEN state. + Data = NA +HID_HDEV_EVT_CLOSE Connection with device is closed. Data=reason code. +HID_HDEV_EVT_RETRYING Lost connection is being re-connected. + Data=Retrial number +HID_HDEV_EVT_IN_REPORT Device sent an input report Data=Report Type pdata= pointer to BT_HDR + (GKI buffer having report data.) +HID_HDEV_EVT_HANDSHAKE Device sent SET_REPORT Data=Result-code pdata=NA. +HID_HDEV_EVT_VC_UNPLUG Device sent Virtual Unplug Data=NA. pdata=NA. +*/ + +enum +{ + HID_HDEV_EVT_OPEN, + HID_HDEV_EVT_CLOSE, + HID_HDEV_EVT_RETRYING, + HID_HDEV_EVT_INTR_DATA, + HID_HDEV_EVT_INTR_DATC, + HID_HDEV_EVT_CTRL_DATA, + HID_HDEV_EVT_CTRL_DATC, + HID_HDEV_EVT_HANDSHAKE, + 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. */ + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function HID_HostGetSDPRecord +** +** Description This function reads the device SDP record. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostGetSDPRecord (BD_ADDR addr, + tSDP_DISCOVERY_DB *p_db, + UINT32 db_len, + tHID_HOST_SDP_CALLBACK *sdp_cback ); + +/******************************************************************************* +** +** Function HID_HostRegister +** +** Description This function registers HID-Host with lower layers. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback); + +/******************************************************************************* +** +** Function HID_HostDeregister +** +** Description This function is called when the host is about power down. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostDeregister(void); + +/******************************************************************************* +** +** Function HID_HostAddDev +** +** Description This is called so HID-host may manage this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostAddDev (BD_ADDR addr, UINT16 attr_mask, + UINT8 *handle ); + +/******************************************************************************* +** +** Function HID_HostRemoveDev +** +** Description This removes the device from list devices that host has to manage. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostRemoveDev (UINT8 dev_handle ); + +/******************************************************************************* +** +** Function HID_HostOpenDev +** +** Description This function is called when the user wants to initiate a +** connection attempt to a device. +** +** Returns void +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostOpenDev (UINT8 dev_handle ); + +/******************************************************************************* +** +** Function HID_HostWriteDev +** +** Description This function is called when the host has a report to send. +** +** Returns void +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostWriteDev(UINT8 dev_handle, UINT8 t_type, + UINT8 param, UINT16 data, + UINT8 report_id, BT_HDR *pbuf); + +/******************************************************************************* +** +** Function HID_HostCloseDev +** +** Description This function disconnects the device. +** +** Returns void +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostCloseDev(UINT8 dev_handle ); + +/******************************************************************************* +** Function HID_HostInit +** +** Description This function initializes the control block and trace variable +** +** Returns void +*******************************************************************************/ +HID_API extern void HID_HostInit(void); + +/******************************************************************************* +** Function HID_HostSetSecurityLevel +** +** Description This function sets the security level for the devices which +** are marked by application as requiring security +** +** Returns tHID_STATUS +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl ); + +/******************************************************************************* +** +** 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 +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +HID_API extern UINT8 HID_HostSetTraceLevel (UINT8 new_level); + +#ifdef __cplusplus +} +#endif + +#endif /* HIDH_API_H */ |