| /****************************************************************************** |
| * |
| * 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_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 |
| }; |
| |
| /* 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_IGNORE, BTA_HH_IDLE_ST } |
| |
| }; |
| |
| |
| 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_CLOSE_ACT, BTA_HH_IDLE_ST }, |
| /* BTA_HH_INT_DATA_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, |
| /* BTA_HH_INT_CTRL_DATA */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, |
| /* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, |
| /* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_SDP_CMPL, BTA_HH_W4_CONN_ST }, |
| /* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_IGNORE , BTA_HH_W4_CONN_ST }, |
| /* BTA_HH_API_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 } |
| }; |
| |
| |
| 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 } |
| }; |
| |
| /* 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 |
| }; |
| |
| /***************************************************************************** |
| ** 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.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 */ |
| } |
| 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 |
| |
| 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_MAX_KNOWN; |
| 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_cb.cb_index[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 MAX_KNOWN |
| */ |
| if (bta_hh_cb.kdev[index].in_use == FALSE) { |
| index = BTA_HH_MAX_KNOWN; |
| } |
| // btla-specific -- |
| } |
| } |
| else if (p_msg->layer_specific < BTA_HH_MAX_KNOWN ) |
| index = bta_hh_cb.cb_index[p_msg->layer_specific]; |
| |
| if (index != BTA_HH_MAX_KNOWN) |
| 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"; |
| 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"; |
| default: |
| return "unknown HID Host state"; |
| } |
| } |
| |
| #endif /* Debug Functions */ |
| |
| #endif /* BTA_HH_INCLUDED */ |