diff options
author | Andre Eisenbach <andre@broadcom.com> | 2012-02-22 13:18:21 -0800 |
---|---|---|
committer | Matthew Xie <mattx@google.com> | 2012-07-14 11:19:11 -0700 |
commit | e448862a47c08eb23185aaed574b39264f5005fc (patch) | |
tree | 2bc6246e3091315e77224fd798ea2fe8074ef972 /stack/hid | |
parent | a2ca4b83ab8bbbfd8d5f6693e927ed4b82094624 (diff) | |
download | external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.zip external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.gz external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.bz2 |
Initial Bluedroid stack commit
Diffstat (limited to 'stack/hid')
-rw-r--r-- | stack/hid/hid_conn.h | 56 | ||||
-rw-r--r-- | stack/hid/hidd_api.c | 536 | ||||
-rw-r--r-- | stack/hid/hidd_conn.c | 970 | ||||
-rw-r--r-- | stack/hid/hidd_int.h | 136 | ||||
-rw-r--r-- | stack/hid/hidd_mgmt.c | 287 | ||||
-rw-r--r-- | stack/hid/hidd_pm.c | 288 | ||||
-rw-r--r-- | stack/hid/hidh_api.c | 534 | ||||
-rw-r--r-- | stack/hid/hidh_conn.c | 1021 | ||||
-rw-r--r-- | stack/hid/hidh_int.h | 81 |
9 files changed, 3909 insertions, 0 deletions
diff --git a/stack/hid/hid_conn.h b/stack/hid/hid_conn.h new file mode 100644 index 0000000..41eeaaf --- /dev/null +++ b/stack/hid/hid_conn.h @@ -0,0 +1,56 @@ +/****************************************************************************/ +/* */ +/* Name: hid_conn.h */ +/* */ +/* Function: this file contains HID connection internal definitions */ +/* */ +/* */ +/* Copyright (c) 2002-2004, WIDCOMM Inc., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/* */ +/****************************************************************************/ +#ifndef HID_CONN_H +#define HID_CONN_H + + +/* Define the HID Connection Block +*/ +typedef struct hid_conn +{ +#define HID_CONN_STATE_UNUSED (0) +#define HID_CONN_STATE_CONNECTING_CTRL (1) +#define HID_CONN_STATE_CONNECTING_INTR (2) +#define HID_CONN_STATE_CONFIG (3) +#define HID_CONN_STATE_CONNECTED (4) +#define HID_CONN_STATE_DISCONNECTING (5) +#define HID_CONN_STATE_SECURITY (6) + + UINT8 conn_state; + +#define HID_CONN_FLAGS_IS_ORIG (0x01) +#define HID_CONN_FLAGS_HIS_CTRL_CFG_DONE (0x02) +#define HID_CONN_FLAGS_MY_CTRL_CFG_DONE (0x04) +#define HID_CONN_FLAGS_HIS_INTR_CFG_DONE (0x08) +#define HID_CONN_FLAGS_MY_INTR_CFG_DONE (0x10) +#define HID_CONN_FLAGS_ALL_CONFIGURED (0x1E) /* All the config done */ +#define HID_CONN_FLAGS_CONGESTED (0x20) +#define HID_CONN_FLAGS_INACTIVE (0x40) + + UINT8 conn_flags; + + UINT8 ctrl_id; + UINT16 ctrl_cid; + UINT16 intr_cid; + UINT16 rem_mtu_size; + UINT16 disc_reason; /* Reason for disconnecting (for HID_HDEV_EVT_CLOSE) */ + TIMER_LIST_ENT timer_entry; + +} tHID_CONN; + +#define HID_SEC_CHN 1 +#define HID_NOSEC_CHN 2 + +#define HIDD_SEC_CHN 3 +#define HIDD_NOSEC_CHN 4 + +#endif diff --git a/stack/hid/hidd_api.c b/stack/hid/hidd_api.c new file mode 100644 index 0000000..7ee41ea --- /dev/null +++ b/stack/hid/hidd_api.c @@ -0,0 +1,536 @@ +/*****************************************************************************/ +/* */ +/* Name: hidd_api.c */ +/* */ +/* Description: this file contains the Device HID API entry points */ +/* */ +/* */ +/* Copyright (c) 2002-2004, WIDCOMM Inc., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "wcassert.h" + +#include "gki.h" +#include "bt_types.h" +#include "hiddefs.h" +#include "hidd_api.h" +#include "hidd_int.h" +#include "btm_api.h" + +#include "hcimsgs.h" +#include "btu.h" +#include "sdpdefs.h" +#include "sdp_api.h" + +static const UINT8 HidDevLangList[] = HID_DEV_LANGUAGELIST; + +#ifndef HID_DEV_BOOT_DEVICE +#define HID_DEV_BOOT_DEVICE TRUE +#endif + + +/******************************************************************************* +** +** Function HID_DevSetSDPRecord +** +** Description This function should be called at startup to create the +** device SDP record +** +** Returns 0 if error else sdp handle for the record. +** +*******************************************************************************/ +UINT32 HID_DevSetSDPRecord (tHID_DEV_SDP_INFO *p_sdp_info) +{ + UINT32 sdp_handle; + tSDP_PROTOCOL_ELEM protocol_list[2]; + tSDP_PROTO_LIST_ELEM additional_list; + UINT16 u16; + UINT8 u8; + UINT8 *pRepDescriptor, *pd; + char buf[2]; + + if( p_sdp_info == NULL ) + return (0); + + /* Create an SDP record for the ctrl/data or notification channel */ + if ((sdp_handle = SDP_CreateRecord()) == FALSE) + { + HIDD_TRACE_ERROR0 ("Could not create service record"); + return (0); + } + + /* Add the UUID to the Service Class ID List */ + u16 = UUID_SERVCLASS_HUMAN_INTERFACE; + SDP_AddServiceClassIdList(sdp_handle, 1, &u16); + + /* Build the protocol descriptor list */ + protocol_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protocol_list[0].num_params = 1; + protocol_list[0].params[0] = HID_PSM_CONTROL; + + protocol_list[1].num_params = 0; + protocol_list[1].protocol_uuid = UUID_PROTOCOL_HIDP; + + SDP_AddProtocolList(sdp_handle, 2, protocol_list); + + /* Language base */ + SDP_AddLanguageBaseAttrIDList (sdp_handle, LANG_ID_CODE_ENGLISH, LANG_ID_CHAR_ENCODE_UTF8, + LANGUAGE_BASE_ID); + + /* Add the Bluetooth Profile Descriptor List (profile version number) */ + SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_HUMAN_INTERFACE, 0x0100); + + /* Add the PSM of the interrupt channel to the Additional Protocol Descriptor List */ + additional_list.num_elems = 2; + additional_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + additional_list.list_elem[0].num_params = 1; + additional_list.list_elem[0].params[0] = HID_PSM_INTERRUPT; + additional_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_HIDP; + additional_list.list_elem[1].num_params = 0; + + SDP_AddAdditionProtoLists (sdp_handle, 1, &additional_list); + + if( p_sdp_info->svc_name[0] != '\0' ) + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT8)(strlen(p_sdp_info->svc_name)+1), (UINT8 *)p_sdp_info->svc_name); + + if( p_sdp_info->svc_descr[0] != '\0' ) + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE, + (UINT8)(strlen(p_sdp_info->svc_descr)+1), (UINT8 *)p_sdp_info->svc_descr); + + if( p_sdp_info->prov_name[0] != '\0' ) + SDP_AddAttribute (sdp_handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, + (UINT8)(strlen(p_sdp_info->prov_name)+1), (UINT8 *)p_sdp_info->prov_name); + + /* HID parser version */ + UINT16_TO_BE_FIELD(buf,p_sdp_info->hpars_ver) ; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_PARSER_VERSION, UINT_DESC_TYPE, 2, (UINT8 *)buf); + + /* HID subclass */ + u8 = p_sdp_info->sub_class; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_DEVICE_SUBCLASS, UINT_DESC_TYPE, 1, &u8); + + /* HID country code */ + u8 = p_sdp_info->ctry_code; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_COUNTRY_CODE, UINT_DESC_TYPE, 1, &u8); + + /* HID Virtual Cable */ + u8 = HID_DEV_VIRTUAL_CABLE; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_VIRTUAL_CABLE, BOOLEAN_DESC_TYPE, 1, &u8); + + /* HID reconnect initiate */ + u8 = HID_DEV_RECONN_INITIATE; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_RECONNECT_INITIATE, BOOLEAN_DESC_TYPE, 1, &u8); + + /* HID report descriptor */ + if ( NULL != (pRepDescriptor = (UINT8 *)GKI_getbuf((UINT16)(p_sdp_info->dscp_info.dl_len + 8 ))) ) + { + pd = pRepDescriptor; + *pd++ = (UINT8)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + *pd++ = (UINT8)(p_sdp_info->dscp_info.dl_len + 4); + *pd++ = (UINT8)((UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE); + *pd++ = (UINT8)(HID_SDP_DESCRIPTOR_REPORT); + *pd++ = (UINT8)((TEXT_STR_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + *pd++ = (UINT8)(p_sdp_info->dscp_info.dl_len); + memcpy (pd, p_sdp_info->dscp_info.dsc_list, p_sdp_info->dscp_info.dl_len); + pd += p_sdp_info->dscp_info.dl_len; + + SDP_AddAttribute (sdp_handle, ATTR_ID_HID_DESCRIPTOR_LIST, DATA_ELE_SEQ_DESC_TYPE, + (UINT32)(pd - pRepDescriptor), pRepDescriptor); + GKI_freebuf( pRepDescriptor ); + } + else + { + SDP_DeleteRecord( sdp_handle ); /* delete freshly allocated record */ + HIDD_TRACE_ERROR1( "HID_DevSetSDPRecord(): SDP creation failed: no memory for rep dscr len: %d", + p_sdp_info->dscp_info.dl_len ); + return 0; + } + /* HID language base list */ + SDP_AddAttribute (sdp_handle, ATTR_ID_HID_LANGUAGE_ID_BASE, DATA_ELE_SEQ_DESC_TYPE, + sizeof (HidDevLangList), (UINT8 *)HidDevLangList); + + /* HID SDP disable (i.e. SDP while Control and Interrupt are up) */ +#if (MAX_L2CAP_CHANNELS > 2) + u8 = 0; +#else + u8 = 1; +#endif + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_SDP_DISABLE, BOOLEAN_DESC_TYPE, 1, &u8); + +#if defined(HID_DEV_BATTERY_POW) + /* HID battery power */ + u8 = HID_DEV_BATTERY_POW; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_BATTERY_POWER, BOOLEAN_DESC_TYPE, 1, &u8); +#endif + +#if defined(HID_DEV_REMOTE_WAKE) + /* HID remote wakeup capable */ + u8 = HID_DEV_REMOTE_WAKE; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_REMOTE_WAKE, BOOLEAN_DESC_TYPE, 1, &u8); +#endif + + /* Link supervision timeout */ + UINT16_TO_BE_FIELD(buf,HID_DEV_LINK_SUPERVISION_TO) ; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_LINK_SUPERVISION_TO, UINT_DESC_TYPE, 2, (UINT8 *)buf); + + /* HID remote wakeup capable */ + u8 = HID_DEV_NORMALLY_CONN; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_NORMALLY_CONNECTABLE, BOOLEAN_DESC_TYPE, 1, &u8); + + /* HID BOOT Device */ + u8 = HID_DEV_BOOT_DEVICE; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_BOOT_DEVICE, BOOLEAN_DESC_TYPE, 1, &u8); + + u16 = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &u16); + + /* SSR host max latency */ + if (p_sdp_info->ssr_max_latency != HID_SSR_PARAM_INVALID) + { + UINT16_TO_BE_FIELD(buf,HID_DEV_LINK_SUPERVISION_TO) ; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_SSR_HOST_MAX_LAT, UINT_DESC_TYPE, 2, (UINT8 *)buf); + } + + /* SSR host min timeout */ + if (p_sdp_info->ssr_max_latency != HID_SSR_PARAM_INVALID) + { + UINT16_TO_BE_FIELD(buf,HID_DEV_LINK_SUPERVISION_TO) ; + SDP_AddAttribute(sdp_handle, ATTR_ID_HID_SSR_HOST_MIN_TOUT, UINT_DESC_TYPE, 2, (UINT8 *)buf); + } + return (sdp_handle); +} + +/******************************************************************************* +** +** Function HID_DevInit +** +** Description This function initializes the control block and trace variable +** +** Returns void +** +*******************************************************************************/ +void HID_DevInit (void) +{ + memset(&hd_cb, 0, sizeof(tHIDDEV_CB)); + + /* Initialize control channel L2CAP configuration */ + hd_cb.l2cap_ctrl_cfg.mtu_present = TRUE; + hd_cb.l2cap_ctrl_cfg.mtu = HID_DEV_MTU_SIZE; + + /* Initialize interrupt channel L2CAP configuration */ + hd_cb.l2cap_int_cfg.mtu_present = TRUE; + hd_cb.l2cap_int_cfg.mtu = HID_DEV_MTU_SIZE; + + hd_cb.conn.timer_entry.param = (UINT32) hidd_proc_repage_timeout; +#if defined(HID_INITIAL_TRACE_LEVEL) + hd_cb.trace_level = HID_INITIAL_TRACE_LEVEL; +#else + hd_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif +} + +/******************************************************************************* +** +** Function HID_DevRegister +** +** Description This function must be called at startup so the device receive +** HID related events and call other HID API Calls. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevRegister( tHID_DEV_REG_INFO *p_reg_info ) +{ + tHID_STATUS st; + BD_ADDR bt_bd_any = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + UINT16 conn_able; + + if( p_reg_info == NULL || + p_reg_info->app_cback == NULL ) + return HID_ERR_INVALID_PARAM; + + if( hd_cb.reg_flag ) + return HID_ERR_ALREADY_REGISTERED; + + /* Check if the host address is provided */ + if( memcmp( p_reg_info->host_addr, bt_bd_any, BD_ADDR_LEN ) ) + { + hd_cb.host_known = TRUE; + memcpy( hd_cb.host_addr, p_reg_info->host_addr, BD_ADDR_LEN ); + hd_cb.dev_state = HID_DEV_ST_NO_CONN ; + + /* When host address is provided, connectibility is determined by the + SDP attribute, otherwise device has to be connectable for initial + pairing process with the host */ + conn_able = (UINT16) HID_DEV_NORMALLY_CONN; + } + else + { + hd_cb.host_known = FALSE; + conn_able = BTM_CONNECTABLE; + } + + hd_cb.virtual_cable = HID_DEV_VIRTUAL_CABLE; + + /* Copy QoS parameters if provided */ + if( p_reg_info->qos_info ) + { + memcpy( &(hd_cb.qos_info), p_reg_info->qos_info, sizeof( tHID_DEV_QOS_INFO ) ); + hd_cb.use_qos_flg = TRUE; + } + else + { + hd_cb.use_qos_flg = FALSE; + } + + hd_cb.callback = p_reg_info->app_cback ; + + /* Register with L2CAP */ + if( (st = hidd_conn_reg()) != HID_SUCCESS ) + { + return st; + } + +#if (!defined(HID_DEV_SET_CONN_MODE) || HID_DEV_SET_CONN_MODE == TRUE) + if( BTM_SetConnectability (conn_able, HID_DEV_PAGE_SCAN_WIN, HID_DEV_PAGE_SCAN_INT) != BTM_SUCCESS ) + return HID_ERR_SET_CONNABLE_FAIL ; +#endif + + hd_cb.reg_flag = TRUE; + hd_cb.unplug_on = FALSE; + + HIDD_TRACE_DEBUG0 ("HID_DevRegister successful"); + return HID_SUCCESS; +} + +/******************************************************************************* +** +** Function HID_DevDeregister +** +** Description This function may be used to remove HID service records and +** deregister from L2CAP. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevDeregister( void ) +{ + if( !hd_cb.reg_flag ) + return HID_ERR_NOT_REGISTERED; + + hidd_mgmt_process_evt( HID_API_DISCONNECT, NULL ) ; /* Disconnect first */ + /* Deregister with L2CAP */ + hidd_conn_dereg() ; + hd_cb.reg_flag = FALSE; + return HID_SUCCESS; +} + +/******************************************************************************* +** +** Function HID_DevConnect +** +** Description This function may be used to initiate a connection to the host.. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevConnect( void ) +{ + if( hd_cb.reg_flag == FALSE ) + return HID_ERR_NOT_REGISTERED; + + return hidd_mgmt_process_evt( HID_API_CONNECT, NULL ) ; /* This will initiate connection */ +} + +/******************************************************************************* +** +** Function HID_DevDisconnect +** +** Description This function may be used to disconnect from the host +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevDisconnect( void ) +{ + if( hd_cb.reg_flag == FALSE ) + return HID_ERR_NOT_REGISTERED; + + return hidd_mgmt_process_evt( HID_API_DISCONNECT, NULL ) ; /* This will initiate disconnection */ +} + +/******************************************************************************* +** +** Function HID_DevHandShake +** +** Description This function may be used to send HAND-SHAKE to host +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevHandShake( UINT8 res_code ) +{ + tHID_SND_DATA_PARAMS hsk_data; + + if( hd_cb.reg_flag == FALSE ) + return HID_ERR_NOT_REGISTERED; + + hsk_data.trans_type = HID_TRANS_HANDSHAKE ; + hsk_data.ctrl_ch = TRUE ; + hsk_data.param = res_code; + hsk_data.buf = NULL; + + return hidd_mgmt_process_evt( HID_API_SEND_DATA, &hsk_data ) ; +} + +/******************************************************************************* +** +** Function HID_DevVirtualUnplug +** +** Description This function may be used to send VIRTUAL-UNPLUG to host +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevVirtualUnplug ( void ) +{ + tHID_STATUS st; + + tHID_SND_DATA_PARAMS vup_data; + + if( hd_cb.reg_flag == FALSE ) + return HID_ERR_NOT_REGISTERED; + + vup_data.trans_type = HID_TRANS_CONTROL ; + vup_data.ctrl_ch = TRUE ; + vup_data.param = HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG; + vup_data.buf = NULL; + + if( (st = hidd_mgmt_process_evt(HID_API_SEND_DATA, &vup_data)) == HID_SUCCESS ) + { + hd_cb.unplug_on = TRUE; + } + + return st; +} + +/******************************************************************************* +** +** Function HID_DevSendData +** +** Description This function may be used to send input reports to host +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevSendData ( BOOLEAN control_ch, UINT8 rep_type, BT_HDR *data_buf ) +{ + tHID_SND_DATA_PARAMS snd_data; + + WC_ASSERT(control_ch != TRUE && control_ch != FALSE); + + if( hd_cb.reg_flag == FALSE ) + return HID_ERR_NOT_REGISTERED; + + snd_data.trans_type = HID_TRANS_DATA ; + snd_data.ctrl_ch = control_ch ; + snd_data.param = rep_type; + snd_data.buf = data_buf; + + return hidd_mgmt_process_evt( HID_API_SEND_DATA, &snd_data ) ; +} + +/******************************************************************************* +** +** Function HID_DevSetSecurityLevel +** +** Description This function set security level for the Hid Device service. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevSetSecurityLevel( char serv_name[], UINT8 sec_lvl ) +{ + hd_cb.sec_mask = sec_lvl; + + if (sec_lvl == 0) + { + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL, + BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID , HIDD_NOSEC_CHN)) + { + HIDD_TRACE_ERROR0 ("Security Registration 1 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL, + BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) + { + HIDD_TRACE_ERROR0 ("Security Registration 2 failed"); + return (HID_ERR_NO_RESOURCES); + } + } + else + { + /* Register with Security Manager for the specific security level */ + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL, + sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HIDD_SEC_CHN)) + { + HIDD_TRACE_ERROR0 ("Security Registration 3 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL, + sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HIDD_SEC_CHN)) + { + HIDD_TRACE_ERROR0 ("Security Registration 4 failed"); + return (HID_ERR_NO_RESOURCES); + } + } + + /* Register with Security Manager for the specific security level for interupt channel*/ + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_INTR, + BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) + { + HIDD_TRACE_ERROR0 ("Security Registration 5 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_INTR, + BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) + { + HIDD_TRACE_ERROR0 ("Security Registration 6 failed"); + return (HID_ERR_NO_RESOURCES); + } + + return HID_SUCCESS; +} + +#if HID_DEV_PM_INCLUDED == TRUE +/******************************************************************************* +** +** Function HID_DevSetPowerMgmtParams +** +** Description This function may be used to change power mgmt parameters. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_DevSetPowerMgmtParams( UINT8 conn_substate, tHID_DEV_PM_PWR_MD pm_params ) +{ + if( conn_substate > HID_DEV_SUSP_CONN_ST ) + return (HID_ERR_INVALID_PARAM); + + memcpy( &hd_cb.pm_params[conn_substate], &pm_params, sizeof( tHID_DEV_PM_PWR_MD ) ) ; + + /* Set the power mode to new parameters if currently in that state */ + if( conn_substate == hd_cb.conn_substate ) + hidd_pm_set_power_mode ( &(hd_cb.pm_params[conn_substate]) ); + + return (HID_SUCCESS); +} + +#endif + diff --git a/stack/hid/hidd_conn.c b/stack/hid/hidd_conn.c new file mode 100644 index 0000000..111dcb7 --- /dev/null +++ b/stack/hid/hidd_conn.c @@ -0,0 +1,970 @@ +/*****************************************************************************/ +/* */ +/* Name: hidd_conn.c */ +/* */ +/* Description: this file contains the connection interface functions */ +/* for device role */ +/* Copyright (c) 2002-2011, Broadcom Corp., All Rights Reserved. */ +/* Broadcom Bluetooth Core. Proprietary and confidential. */ +/* */ +/*****************************************************************************/ + +#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 "hidd_api.h" +#include "hidd_int.h" + +/********************************************************************************/ +/* 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 hidd_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, + UINT8 l2cap_id); +static void hidd_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void hidd_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void hidd_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void hidd_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void hidd_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); +static void hidd_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result); +static void hidd_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested); + +static const tL2CAP_APPL_INFO reg_info = +{ + hidd_l2cif_connect_ind, + hidd_l2cif_connect_cfm, + NULL, + hidd_l2cif_config_ind, + hidd_l2cif_config_cfm, + hidd_l2cif_disconnect_ind, + hidd_l2cif_disconnect_cfm, + NULL, + hidd_l2cif_data_ind, + hidd_l2cif_cong_ind, + NULL /* tL2CA_TX_COMPLETE_CB */ +} ; + + +/******************************************************************************* +** +** Function hidd_conn_reg +** +** Description This function registers with L2CAP. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS hidd_conn_reg (void) +{ + /* Now, register with L2CAP for control and interrupt PSMs*/ + if (!L2CA_Register (HID_PSM_CONTROL, (tL2CAP_APPL_INFO *) ®_info)) + { + HIDD_TRACE_ERROR0 ("HID Control Registration failed"); + return( HID_ERR_L2CAP_FAILED ); + } + + if (!L2CA_Register (HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *) ®_info)) + { + L2CA_Deregister( HID_PSM_CONTROL ) ; + HIDD_TRACE_ERROR0 ("HID Interrupt Registration failed"); + return( HID_ERR_L2CAP_FAILED ); + } + + /* Set up / initialize the l2cap configuration data */ + hd_cb.l2cap_ctrl_cfg.flush_to_present = TRUE; + hd_cb.l2cap_ctrl_cfg.flush_to = HID_DEV_FLUSH_TO; + hd_cb.l2cap_int_cfg.flush_to_present = TRUE; + hd_cb.l2cap_int_cfg.flush_to = HID_DEV_FLUSH_TO; + + if( hd_cb.use_qos_flg == TRUE ) + { + memcpy( &(hd_cb.l2cap_ctrl_cfg.qos), &(hd_cb.qos_info.ctrl_ch), sizeof( FLOW_SPEC ) ) ; + hd_cb.l2cap_ctrl_cfg.qos_present = TRUE ; + + memcpy( &(hd_cb.l2cap_int_cfg.qos), &(hd_cb.qos_info.int_ch), sizeof( FLOW_SPEC ) ) ; + hd_cb.l2cap_int_cfg.qos_present = TRUE ; + } + else + { + hd_cb.l2cap_ctrl_cfg.qos_present = FALSE ; + hd_cb.l2cap_int_cfg.qos_present = FALSE ; + } + + hd_cb.conn.conn_state = HID_CONN_STATE_UNUSED ; + + return( HID_SUCCESS ); +} + +/******************************************************************************* +** +** Function hidd_conn_dereg +** +** Description This function deregisters with L2CAP. +** +** Returns void +** +*******************************************************************************/ +void hidd_conn_dereg( void ) +{ + L2CA_Deregister (HID_PSM_CONTROL); + L2CA_Deregister (HID_PSM_INTERRUPT); +} + +/******************************************************************************* +** +** Function hid_conn_disconnect +** +** Description This function disconnects a connection. +** +** Returns TRUE if disconnect started, FALSE if already disconnected +** +*******************************************************************************/ +void hidd_conn_disconnect () +{ + tHID_CONN *p_hcon = &hd_cb.conn; +#if HID_DEV_PM_INCLUDED == TRUE + tHID_DEV_PM_PWR_MD act_pm = { 0, 0, 0, 0, HCI_MODE_ACTIVE } ; +#endif + + HIDD_TRACE_EVENT0 ("HID - disconnect"); + +#if HID_DEV_PM_INCLUDED == TRUE + hidd_pm_stop(); /* This will stop the idle timer if running */ + + /* Need to go to to active mode to be able to send disconnect */ + hidd_pm_set_power_mode( &act_pm ); +#endif + + if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) + { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; + /* Disconnect both control and interrupt 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; + } +} + +/******************************************************************************* +** +** Function hidd_conn_initiate +** +** Description This function is called by the management to create a connection. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS hidd_conn_initiate (void) +{ + tHID_CONN *p_hcon = &hd_cb.conn; + UINT8 service_id = BTM_SEC_SERVICE_HID_NOSEC_CTRL; + UINT32 mx_chan_id = HIDD_NOSEC_CHN; + +#if HID_DEV_NORMALLY_CONN == TRUE + if( p_hcon->conn_state != HID_CONN_STATE_UNUSED ) + return HID_ERR_CONN_IN_PROCESS ; +#endif + + HIDD_TRACE_EVENT0 ("HID - Originate started"); + + p_hcon->ctrl_cid = 0; + p_hcon->intr_cid = 0; + + /* We are the originator of this connection */ + p_hcon->conn_flags = HID_CONN_FLAGS_IS_ORIG; + if(hd_cb.sec_mask) + { + service_id = BTM_SEC_SERVICE_HID_SEC_CTRL; + mx_chan_id = HIDD_SEC_CHN; + } + BTM_SetOutService (hd_cb.host_addr, service_id, mx_chan_id); + + /* Check if L2CAP started the connection process */ + if ((p_hcon->ctrl_cid = L2CA_ConnectReq (HID_PSM_CONTROL, hd_cb.host_addr)) == 0) + { + HIDD_TRACE_WARNING0 ("HID - Originate failed"); + return (HID_ERR_L2CAP_FAILED); + } + else + { + /* Transition to the next appropriate state, waiting for connection confirm on control channel. */ + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_CTRL; + + return (HID_SUCCESS); + } +} + +/******************************************************************************* +** +** Function hidd_sec_check_complete_term +** +** Description HID device security check complete callback function. +** +** Returns When security check succeed, send L2Cap connect response with +** OK code and send L2C configuration requret; otherwise send +** L2C connection response with security block code. +** +** +*******************************************************************************/ +void hidd_sec_check_complete_term (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tHID_CONN *p_hcon = &hd_cb.conn; + + if( res == BTM_SUCCESS && p_hcon->conn_state == HID_CONN_STATE_SECURITY ) + { + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, p_hcon->ctrl_id, p_hcon->ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* Send a Configuration Request. */ + L2CA_ConfigReq (p_hcon->ctrl_cid, &hd_cb.l2cap_ctrl_cfg); + + } + /* security check fail */ + else if (res != BTM_SUCCESS) + { + L2CA_ConnectRsp (bd_addr, p_hcon->ctrl_id, p_hcon->ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + } +} + +/******************************************************************************* +** +** Function hidd_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 hidd_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ + tHID_CONN *p_hcon = &hd_cb.conn; + BOOLEAN bAccept = TRUE; + tL2CAP_CFG_INFO *p_l2cfg = NULL; + + HIDD_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); + + /* If host address provided during registration does not match, reject connection */ + if( hd_cb.virtual_cable && hd_cb.host_known && + memcmp( hd_cb.host_addr, bd_addr, BD_ADDR_LEN) ) + bAccept = FALSE; + else + { + /* Check we are in the correct state for this */ + if (psm == HID_PSM_INTERRUPT) + { + if (p_hcon->ctrl_cid == 0) + { + HIDD_TRACE_WARNING0 ("HID - Rcvd INTR L2CAP conn ind, but no CTL channel"); + bAccept = FALSE; + } + if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) + { + HIDD_TRACE_WARNING1 ("HID - Rcvd INTR L2CAP conn ind, wrong state: %d", p_hcon->conn_state); + bAccept = FALSE; + } + p_l2cfg = &hd_cb.l2cap_int_cfg; + } + else + { + if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) + { + HIDD_TRACE_WARNING1 ("HID - Rcvd CTL L2CAP conn ind, wrong state: %d", p_hcon->conn_state); + bAccept = FALSE; + } + p_l2cfg = &hd_cb.l2cap_ctrl_cfg; + } + } + + 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->conn_state = HID_CONN_STATE_SECURITY; + + if(btm_sec_mx_access_request (bd_addr, HID_PSM_CONTROL, + FALSE, BTM_SEC_PROTO_HID, + (hd_cb.sec_mask == 0) ? HIDD_NOSEC_CHN : HIDD_SEC_CHN, + &hidd_sec_check_complete_term, NULL) == BTM_CMD_STARTED) + { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + return; + } + else + { + /* 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, p_l2cfg); + + memcpy( hd_cb.host_addr, bd_addr, BD_ADDR_LEN ) ; + + HIDD_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); +} + +/******************************************************************************* +** +** Function hidd_sec_check_complete_orig +** +** Description This function checks to see if security procedures are being +** carried out or not.. +** +** Returns void +** +*******************************************************************************/ +void hidd_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + UINT32 reason; + tHID_CONN *p_hcon = &hd_cb.conn; + + if( res == BTM_SUCCESS && p_hcon->conn_state == HID_CONN_STATE_SECURITY ) + { + HIDD_TRACE_EVENT0 ("HID Device - Originator security pass."); + + /* Send connect request for interrupt channel */ + if ((p_hcon->intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hd_cb.host_addr)) == 0) + { + HIDD_TRACE_WARNING0 ("HID - INTR Originate failed"); + hidd_conn_disconnect();/* Disconnects the ctrl channel if setting of int chnl failed */ + reason = HID_L2CAP_REQ_FAIL ; + hd_cb.conn_tries = HID_DEV_MAX_CONN_RETRY+1; + hidd_mgmt_process_evt( HOST_CONN_FAIL, &reason ) ; + return; + } + else + { + /* Transition to the next appropriate state, waiting for connection confirm on control channel. */ + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + + if( res != BTM_SUCCESS && p_hcon->conn_state == HID_CONN_STATE_SECURITY ) + { + hidd_conn_disconnect(); + } + +} + +/******************************************************************************* +** +** Function hid_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 hidd_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + UINT32 reason; + tHID_CONN *p_hcon = &hd_cb.conn; + tL2CAP_CFG_INFO *p_l2cfg; + + /* Verify we are in a state to accept this message */ + if (((p_hcon->ctrl_cid != l2cap_cid) && (p_hcon->intr_cid != l2cap_cid)) + || (!(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))) + { + HIDD_TRACE_WARNING1 ("HID - 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; + + reason = HID_L2CAP_CONN_FAIL | ((UINT32) result) ; + hidd_conn_disconnect(); /* Disconnects the ctrl channel if setting of int chnl failed */ + + /* If connection failed due to bad air link, retry... */ + if (result != HCI_ERR_CONNECTION_TOUT && result != HCI_ERR_UNSPECIFIED + && result != HCI_ERR_PAGE_TIMEOUT) + hd_cb.conn_tries = HID_DEV_MAX_CONN_RETRY+1; + hidd_mgmt_process_evt( HOST_CONN_FAIL, &reason ) ; + return; + } + + if (l2cap_cid == p_hcon->ctrl_cid) + { + /* let HID Device handle security itself */ + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + + btm_sec_mx_access_request (hd_cb.host_addr, HID_PSM_CONTROL, + TRUE, BTM_SEC_PROTO_HID, + (hd_cb.sec_mask == 0) ? HIDD_NOSEC_CHN : HIDD_SEC_CHN, + &hidd_sec_check_complete_orig, NULL); + + p_l2cfg = &hd_cb.l2cap_int_cfg; + + } + else + { + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + p_l2cfg = &hd_cb.l2cap_ctrl_cfg; + } + + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, p_l2cfg); + + HIDD_TRACE_EVENT1 ("HID - got CTRL conn cnf, sent cfg req, CID: 0x%x", l2cap_cid); + return; +} + +/******************************************************************************* +** +** Function hidd_l2c_connected +** +** Description This function is called when both control and interrupt channels +** have been created. +** +** Returns void +** +*******************************************************************************/ +void hidd_l2c_connected( tHID_CONN *p_hcon ) +{ + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + + /* Set HCI QoS */ + if( hd_cb.use_qos_flg ) + BTM_SetQoS( hd_cb.host_addr, &(hd_cb.qos_info.hci), NULL ) ; + + hidd_mgmt_process_evt( HOST_CONN_OPEN, hd_cb.host_addr) ; +#if HID_DEV_PM_INCLUDED == TRUE + hidd_pm_init(); + hidd_pm_start(); /* This kicks in the idle timer */ +#endif +} + +/******************************************************************************* +** +** Function hidd_l2cif_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void hidd_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tHID_CONN *p_hcon = &hd_cb.conn; + + /* Find CCB based on CID */ + if ((p_hcon->ctrl_cid != l2cap_cid) && (p_hcon->intr_cid != l2cap_cid)) + { + HIDD_TRACE_WARNING1 ("HID - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDD_TRACE_EVENT1 ("HID - 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_DEV_MTU_SIZE)) + p_hcon->rem_mtu_size = HID_DEV_MTU_SIZE; + 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)) + { + hidd_l2c_connected( p_hcon ); + } +} + + +/******************************************************************************* +** +** Function hid_l2cif_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void hidd_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + UINT32 reason; + tHID_CONN *p_hcon = &hd_cb.conn; + tL2CAP_CFG_INFO * p_l2cfg = NULL; + + HIDD_TRACE_EVENT2 ("HID - Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result); + + /* Find CCB based on CID */ + if ((p_hcon->ctrl_cid != l2cap_cid) && (p_hcon->intr_cid != l2cap_cid)) + { + HIDD_TRACE_WARNING1 ("HID - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + /* config fail for unaccepted param, send reconfiguration */ + if (p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS) + { + /* remember the host prefered config param in control block */ + p_l2cfg = (l2cap_cid == p_hcon->ctrl_cid)? &hd_cb.l2cap_ctrl_cfg : &hd_cb.l2cap_int_cfg ; + memcpy(p_l2cfg, p_cfg, sizeof(tL2CAP_CFG_INFO)); + + /* Send a second time configuration request. */ + L2CA_ConfigReq (l2cap_cid, p_l2cfg); + return; + } + /* If configuration failed for other reason, disconnect the channel(s) */ + else if (p_cfg->result != L2CAP_CFG_OK) + { + reason = HID_L2CAP_CFG_FAIL | ((UINT32) p_cfg->result) ; + hidd_conn_disconnect(); + hd_cb.conn_tries = HID_DEV_MAX_CONN_RETRY+1; + hidd_mgmt_process_evt( HOST_CONN_FAIL, &reason ) ; + 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)) + { + hidd_l2c_connected( p_hcon ); + } +} + + +/******************************************************************************* +** +** Function hidd_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 hidd_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + tHID_CONN *p_hcon = &hd_cb.conn; + UINT16 disc_res = HCI_PENDING ; + UINT8 evt = HOST_CONN_CLOSE; + + /* Find CCB based on CID */ + if ((p_hcon->ctrl_cid != l2cap_cid) && (p_hcon->intr_cid != l2cap_cid)) + { + HIDD_TRACE_WARNING1 ("HID - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); + return; + } + + if (ack_needed) + L2CA_DisconnectRsp (l2cap_cid); + + HIDD_TRACE_EVENT1 ("HID - 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)) + { + if (!ack_needed) + disc_res = btm_get_acl_disc_reason_code(); + HIDD_TRACE_EVENT1 ("disc_res: 0x%x", disc_res); + + if( disc_res == HCI_ERR_CONNECTION_TOUT || disc_res == HCI_ERR_UNSPECIFIED ) + evt = HOST_CONN_LOST; + + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidd_mgmt_process_evt( evt, &disc_res ) ; +#if HID_DEV_PM_INCLUDED == TRUE + hidd_pm_stop(); +#endif + } +} + + +/******************************************************************************* +** +** Function hid_l2cif_disconnect_cfm +** +** Description This function handles a disconnect confirm event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void hidd_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + UINT16 disc_res = HCI_SUCCESS; + tHID_CONN *p_hcon = &hd_cb.conn; + + /* Find CCB based on CID */ + if ((p_hcon->ctrl_cid != l2cap_cid) && (p_hcon->intr_cid != l2cap_cid)) + { + HIDD_TRACE_WARNING1 ("HID - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDD_TRACE_EVENT1 ("HID - 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)) + { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidd_mgmt_process_evt( HOST_CONN_CLOSE, &disc_res ) ; +#if HID_DEV_PM_INCLUDED == TRUE + hidd_pm_stop(); +#endif + } +} + + +/******************************************************************************* +** +** Function hidd_l2cif_cong_ind +** +** Description This function handles a congestion status event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void hidd_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested) +{ + tHID_CONN *p_hcon = &hd_cb.conn; + + /* Find CCB based on CID */ + if ((p_hcon->ctrl_cid != l2cap_cid) && (p_hcon->intr_cid != l2cap_cid)) + { + HIDD_TRACE_WARNING1 ("HID - Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDD_TRACE_EVENT2 ("HID - 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; + } + + hd_cb.callback( HID_DEV_EVT_L2CAP_CONGEST, (p_hcon->ctrl_cid == l2cap_cid), (tHID_DEV_CBACK_DATA*) &congested ) ; +} + + +/******************************************************************************* +** +** Function hid_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 hidd_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) +{ + tHID_CONN *p_hcon = &hd_cb.conn; + UINT8 *p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 ttype, param, rep_type, idle_rate; + tHID_DEV_GET_REP_DATA get_rep; + BOOLEAN suspend = FALSE; + + /* Find CCB based on CID */ + if ((p_hcon->ctrl_cid != l2cap_cid) && (p_hcon->intr_cid != l2cap_cid)) + { + HIDD_TRACE_WARNING1 ("HID - 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++; + + switch (ttype) + { + case HID_TRANS_CONTROL: + switch (param) + { + case HID_PAR_CONTROL_NOP: + case HID_PAR_CONTROL_HARD_RESET: + case HID_PAR_CONTROL_SOFT_RESET: + case HID_PAR_CONTROL_EXIT_SUSPEND: + break; + + case HID_PAR_CONTROL_SUSPEND: + suspend = TRUE; +#if HID_DEV_PM_INCLUDED == TRUE + hidd_pm_suspend_evt() ; +#endif + break; + + case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG: + hidd_mgmt_process_evt( HID_API_DISCONNECT, NULL ) ; + hd_cb.unplug_on = TRUE; + break; + + default: + GKI_freebuf (p_msg); + HID_DevHandShake( HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM ) ; + return; + } + hd_cb.callback( HID_DEV_EVT_CONTROL, param, NULL ) ; + break; + + case HID_TRANS_GET_REPORT: + if (param & HID_PAR_GET_REP_BUFSIZE_FOLLOWS) + { + STREAM_TO_UINT8 (get_rep.rep_id, p_data); + STREAM_TO_UINT16 (hd_cb.get_rep_buf_sz, p_data); + } + else + hd_cb.get_rep_buf_sz = 0; + + get_rep.rep_type = param & HID_PAR_REP_TYPE_MASK; + + hd_cb.callback( HID_DEV_EVT_GET_REPORT, param, + (tHID_DEV_CBACK_DATA*) &get_rep ) ; + break; + + case HID_TRANS_SET_REPORT: + hd_cb.callback( HID_DEV_EVT_SET_REPORT, rep_type, (tHID_DEV_CBACK_DATA*) &p_msg ) ; + break; + + case HID_TRANS_DATA: + hd_cb.callback( HID_DEV_EVT_DATA, rep_type, (tHID_DEV_CBACK_DATA*) &p_msg ) ; + break; + + case HID_TRANS_DATAC: + hd_cb.callback( HID_DEV_EVT_DATC, rep_type, (tHID_DEV_CBACK_DATA*) &p_msg ) ; + break; + + case HID_TRANS_GET_PROTOCOL: + /* Get current protocol */ + hd_cb.callback( HID_DEV_EVT_GET_PROTO, 0, NULL ) ; + break; + + case HID_TRANS_SET_PROTOCOL: + hd_cb.callback( HID_DEV_EVT_SET_PROTO, (UINT32) (param & HID_PAR_PROTOCOL_MASK), NULL ) ; + break; + + /* for HID 1.0 device only */ + case HID_TRANS_GET_IDLE: + hd_cb.callback( HID_DEV_EVT_GET_IDLE, 0, NULL ) ; + break; + + /* for HID 1.0 device only */ + case HID_TRANS_SET_IDLE: + STREAM_TO_UINT8 (idle_rate, p_data); + hd_cb.callback( HID_DEV_EVT_SET_IDLE, idle_rate, NULL ) ; + break; + + default: + GKI_freebuf (p_msg); + HID_DevHandShake( HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ ) ; + return; + } + +#if HID_DEV_PM_INCLUDED == TRUE + if( !suspend ) + hidd_pm_start(); +#endif + + if( (ttype != HID_TRANS_SET_REPORT) && (ttype != HID_TRANS_DATA) && (ttype != HID_TRANS_DATAC) ) + GKI_freebuf (p_msg); + /* Else application frees the buffer */ +} + +/******************************************************************************* +** +** Function hidd_conn_snd_data +** +** Description This function is called to send HID data. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS hidd_conn_snd_data (tHID_SND_DATA_PARAMS *p_data) +{ + tHID_CONN *p_hcon = &hd_cb.conn; + BT_HDR *p_buf; + UINT8 *p_out; + UINT16 bytes_copied; + BOOLEAN seg_req = FALSE; + UINT16 data_size; + UINT16 cid; + UINT8 pool_id; + UINT16 rem_mtu; + +#if HID_DEV_PM_INCLUDED == TRUE + hidd_pm_start(); +#endif + + /* Safety check */ + if (p_data) + { + pool_id = (p_data->ctrl_ch) ? HID_CONTROL_POOL_ID : HID_INTERRUPT_POOL_ID; + } + else + { + HIDD_TRACE_ERROR0 ("HID_ERR_INVALID_PARAM"); + return HID_ERR_INVALID_PARAM; + } + + if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) + { + return( HID_ERR_CONGESTED ); + } + + if( (p_data->trans_type == HID_TRANS_DATA) && p_data->ctrl_ch && hd_cb.get_rep_buf_sz ) + { + rem_mtu = hd_cb.get_rep_buf_sz; + hd_cb.get_rep_buf_sz = 0; + } + else + { + rem_mtu = p_hcon->rem_mtu_size; + } + + do + { + /* If buf is null form the HID message with ttype and params alone */ + if ( p_data->buf == NULL ) + { + 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; + } + /* Segmentation case */ + else if ( (p_data->buf->len > (rem_mtu - 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 = p_data->buf->len; + bytes_copied = rem_mtu - 1; + } + else /* NOTE: The initial buffer gets used last */ + { + p_buf = p_data->buf ; + p_buf->offset -= 1; + seg_req = FALSE; + data_size = p_data->buf->len; + bytes_copied = p_data->buf->len; + } + + p_out = (UINT8 *)(p_buf + 1) + p_buf->offset; + *p_out++ = HID_BUILD_HDR(p_data->trans_type, p_data->param); + + if (seg_req) + { + memcpy (p_out, (((UINT8 *)(p_data->buf+1)) + p_data->buf->offset), bytes_copied); + p_data->buf->offset += bytes_copied; + p_data->buf->len -= bytes_copied; + } + + p_buf->len = bytes_copied + 1; + data_size -= bytes_copied; + + cid = (p_data->ctrl_ch ? p_hcon->ctrl_cid : p_hcon->intr_cid); + /* 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) + p_data->trans_type = HID_TRANS_DATAC; + + } while (data_size != 0); + + if( bytes_copied == (rem_mtu - 1) ) + { + tHID_SND_DATA_PARAMS datc; + + datc.buf = NULL; + datc.ctrl_ch = p_data->ctrl_ch; + datc.param = p_data->param ; + datc.trans_type = HID_TRANS_DATAC ; + + hidd_conn_snd_data( &datc ) ; + } + + return (HID_SUCCESS); +} + diff --git a/stack/hid/hidd_int.h b/stack/hid/hidd_int.h new file mode 100644 index 0000000..58d7a83 --- /dev/null +++ b/stack/hid/hidd_int.h @@ -0,0 +1,136 @@ +/****************************************************************************/ +/* */ +/* Name: hidd_int.h */ +/* */ +/* Function: this file contains HID DEVICE internal definitions */ +/* */ +/* */ +/* Copyright (c) 2002-2004, WIDCOMM Inc., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/* */ +/****************************************************************************/ + +#ifndef HIDD_INT_H +#define HIDD_INT_H +#include "hidd_api.h" +#include "hid_conn.h" +#include "l2c_api.h" + +/* Define the possible events of the HID Device state machine. +*/ +enum +{ + HOST_CONN_OPEN, + HOST_CONN_CLOSE, + HOST_CONN_LOST, + HOST_CONN_FAIL, + HID_API_CONNECT, + HID_API_DISCONNECT, + HID_API_SEND_DATA +}; + +/* Define the possible states of the HID Device. +*/ +enum +{ + HID_DEV_ST_NO_CONN, + HID_DEV_ST_CONNECTING, + HID_DEV_ST_CONNECTED, + HID_DEV_ST_DISC_ING +}; + +/* To remember the power mode and setting */ +typedef struct curr_pm_setting +{ + UINT8 mode; + UINT16 interval; +} tHID_DEV_PM_CURR; + +/* Define the HID management control block. +*/ +typedef struct hid_control_block +{ + BD_ADDR host_addr; /* BD-Addr of the host device */ + BOOLEAN host_known; /* Mode */ + BOOLEAN virtual_cable;/* If the device is to behave as virtual cable */ + UINT8 dev_state; /* Device state if in HOST-KNOWN mode */ + UINT8 conn_tries; /* Remembers to the number of connection attempts while CONNECTING */ + UINT8 sec_mask; + UINT16 get_rep_buf_sz; + tHID_CONN conn; /* L2CAP channel info */ + +#if HID_DEV_PM_INCLUDED == TRUE + TIMER_LIST_ENT idle_tle; /* Timer used for inactivity timing */ + tHID_DEV_PM_PWR_MD pm_params[3]; /* Power management parameters for the three possible states */ + tHID_DEV_PM_CURR curr_pm; /* Current power mode */ + BOOLEAN pm_ctrl_busy;/* A power mode transition is going on */ + UINT8 conn_substate; + tHID_DEV_PM_PWR_MD final_pm;/* To remember the power mode while a power mode change is ongoing */ +#endif + + BOOLEAN use_qos_flg; /* Qos information provided by application or not */ + BOOLEAN unplug_on; /* Virtual unplug has been sent or received */ + tHID_DEV_QOS_INFO qos_info; /* Storage for QoS provided by application */ + + tHID_DEV_CALLBACK *callback; /* Application callbacks */ + tL2CAP_CFG_INFO l2cap_ctrl_cfg; /* Configuration data for control channel */ + tL2CAP_CFG_INFO l2cap_int_cfg; /* Configuration data for interrupt channel */ + BOOLEAN reg_flag; + UINT8 trace_level; +} tHIDDEV_CB; + +typedef struct snd_data_params +{ + BOOLEAN ctrl_ch; /* TRUE if control channel, FALSE if interrupt */ + UINT8 trans_type; /* Transaction type */ + UINT8 param; /* Second byte after trans type */ + BT_HDR *buf ; /* Data that comes after param */ +} tHID_SND_DATA_PARAMS; /* Is defined for hidd_conn_snd_data */ + +/* HID management function prototype +*/ +typedef tHID_STATUS (tHIDD_MGMT_EVT_HDLR) (UINT8, void *); + +/* HID Globals +*/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if HID_DYNAMIC_MEMORY == FALSE +HID_API extern tHIDDEV_CB hd_cb; +#else +HID_API extern tHIDDEV_CB *hidd_cb_ptr; +#define hd_cb (*hidd_cb_ptr) +#endif + +extern tHID_STATUS hidd_conn_reg (void); +extern void hidd_conn_dereg( void ); +extern tHID_STATUS hidd_conn_initiate (void); +extern void hidd_conn_disconnect (void); +extern tHID_STATUS hidd_conn_snd_data (tHID_SND_DATA_PARAMS *p_data); + +extern tHID_STATUS hidd_mgmt_process_evt( UINT8 event, void *data ); +extern void hidd_proc_repage_timeout (TIMER_LIST_ENT *p_tle); + +#if HID_DEV_PM_INCLUDED == TRUE +extern tHID_STATUS hidd_pm_start( void ) ; +extern tHID_STATUS hidd_pm_stop( void ); +extern tHID_STATUS hidd_pm_activity_evt( void ); +extern tHID_STATUS hidd_pm_suspend_evt( void ); +extern tHID_STATUS hidd_pm_unsuspend_evt( void ); +extern void hidd_pm_init( void ); +extern BOOLEAN hidd_pm_set_power_mode( tHID_DEV_PM_PWR_MD *pm) ; + +extern void hidd_pm_proc_mode_change( UINT8 hci_status, UINT8 mode, UINT16 interval ); +#endif /* HID_DEV_PM_INCLUDED == TRUE */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stack/hid/hidd_mgmt.c b/stack/hid/hidd_mgmt.c new file mode 100644 index 0000000..819dd11 --- /dev/null +++ b/stack/hid/hidd_mgmt.c @@ -0,0 +1,287 @@ +/*****************************************************************************/ +/* */ +/* Name: hidd_mgmt.c */ +/* */ +/* Description: this file contains the HID Device Management logic */ +/* */ +/* NOTE: In the interest of keeping code small, there is an */ +/* inherent assumption that L2CAP always runs a timer, */ +/* and so the HID management never needs a timer on L2CAP */ +/* actions like connect and disconnect. */ +/* */ +/* */ +/* Copyright (c) 2002-2004, WIDCOMM Inc., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "gki.h" +#include "bt_types.h" +#include "hidd_api.h" +#include "hiddefs.h" +#include "hidd_int.h" +#include "btu.h" +#include "btm_api.h" + + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static tHID_STATUS hidd_no_conn_proc_evt( UINT8 event, void *p_data ); +static tHID_STATUS hidd_connecting_proc_evt( UINT8 event, void *p_data ); +static tHID_STATUS hidd_connected_proc_evt( UINT8 event, void *p_data ); +static tHID_STATUS hidd_disc_ing_proc_evt( UINT8 event, void *p_data ); + + +/* State machine jump table. +*/ +tHIDD_MGMT_EVT_HDLR * const hidd_sm_proc_evt[] = +{ + hidd_no_conn_proc_evt, + hidd_connecting_proc_evt, + hidd_connected_proc_evt, + hidd_disc_ing_proc_evt +}; + +/* Main HID control block */ +#if HID_DYNAMIC_MEMORY == FALSE +tHIDDEV_CB hd_cb; +#endif + +/******************************************************************************* +** +** Function hidd_mgmt_process_evt +** +** Description This function is called by other modules to report an event +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS hidd_mgmt_process_evt( UINT8 event, void *p_data ) +{ + HIDD_TRACE_DEBUG3("hidd_mgmt_process_evt known: %d, s:%d, e:%d", + hd_cb.host_known, hd_cb.dev_state, event); + if( hd_cb.host_known ) + { + return ((hidd_sm_proc_evt[hd_cb.dev_state]) (event, p_data)); /* Event is passed to main State Machine */ + } + + if( event == HOST_CONN_OPEN ) + { + hd_cb.host_known = TRUE; + memcpy( hd_cb.host_addr, (BD_ADDR *) p_data, BD_ADDR_LEN ) ; + hd_cb.dev_state = HID_DEV_ST_CONNECTED ; + /* Call-Back the Application with this information */ + hd_cb.callback(HID_DEV_EVT_OPEN, 0, (tHID_DEV_CBACK_DATA *) hd_cb.host_addr ) ; + return( HID_SUCCESS ); + } + + return( HID_ERR_HOST_UNKNOWN ); +} + +/******************************************************************************* +** +** Function hidd_no_conn_proc_evt +** +** Description NO-CONN state event handler. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +static tHID_STATUS hidd_no_conn_proc_evt( UINT8 event, void *p_data ) +{ + tHID_STATUS st = HID_SUCCESS; + + switch( event ) + { + case HOST_CONN_OPEN: + hd_cb.dev_state = HID_DEV_ST_CONNECTED ; + hd_cb.callback(HID_DEV_EVT_OPEN, 0, (tHID_DEV_CBACK_DATA *) hd_cb.host_addr ) ; + break; + case HID_API_CONNECT: + hd_cb.conn_tries = 1; + hd_cb.dev_state = HID_DEV_ST_CONNECTING ; + if( (st = hidd_conn_initiate()) != HID_SUCCESS ) + { +#if HID_DEV_MAX_CONN_RETRY > 0 + btu_start_timer (&(hd_cb.conn.timer_entry), BTU_TTYPE_USER_FUNC, HID_DEV_REPAGE_WIN); + return HID_SUCCESS; +#else + hd_cb.dev_state = HID_DEV_ST_NO_CONN ; +#endif + } + break; + default: + st = HID_ERR_NO_CONNECTION; + } + + return st; +} + +/******************************************************************************* +** +** Function hidd_proc_repage_timeout +** +** Description function to handle timeout. +** +** Returns void +** +*******************************************************************************/ +void hidd_proc_repage_timeout (TIMER_LIST_ENT *p_tle) +{ + HIDD_TRACE_DEBUG0 ("hidd_proc_repage_timeout"); + hd_cb.conn_tries++; + if( hidd_conn_initiate() != HID_SUCCESS ) + { + if( hd_cb.conn_tries > HID_DEV_MAX_CONN_RETRY ) + { + hd_cb.dev_state = HID_DEV_ST_NO_CONN ; + hd_cb.callback(HID_DEV_EVT_CLOSE, 0, NULL ); + } + else + btu_start_timer (&(hd_cb.conn.timer_entry), BTU_TTYPE_USER_FUNC, HID_DEV_REPAGE_WIN); + } + else + hd_cb.callback( HID_DEV_EVT_RETRYING, hd_cb.conn_tries, NULL ); +} + +/******************************************************************************* +** +** Function hidd_connecting_proc_evt +** +** Description CONNECTING state event handler +** +** Returns tHID_STATUS +** +*******************************************************************************/ +static tHID_STATUS hidd_connecting_proc_evt( UINT8 event, void *p_data ) +{ + switch( event ) + { + case HOST_CONN_OPEN: + hd_cb.dev_state = HID_DEV_ST_CONNECTED ; + hd_cb.callback(HID_DEV_EVT_OPEN, 0, (tHID_DEV_CBACK_DATA *) hd_cb.host_addr ) ; + break; + + case HOST_CONN_FAIL: + if( hd_cb.conn_tries > HID_DEV_MAX_CONN_RETRY ) + { + UINT16 reason = *( (UINT16 *) p_data); + + hd_cb.dev_state = HID_DEV_ST_NO_CONN; + hd_cb.callback(HID_DEV_EVT_CLOSE, reason, NULL ); + } +#if HID_DEV_MAX_CONN_RETRY > 0 + else + { + btu_start_timer (&(hd_cb.conn.timer_entry), BTU_TTYPE_USER_FUNC, HID_DEV_REPAGE_WIN); + } +#endif + break; + + case HOST_CONN_CLOSE: + case HOST_CONN_LOST: + hd_cb.dev_state = HID_DEV_ST_NO_CONN; + hd_cb.callback(HID_DEV_EVT_CLOSE, *((UINT16 *) p_data), NULL ); + break; + + case HID_API_DISCONNECT: + hd_cb.dev_state = HID_DEV_ST_NO_CONN ; + btu_stop_timer (&(hd_cb.conn.timer_entry)); + hidd_conn_disconnect(); + break; + + default: + return( HID_ERR_CONN_IN_PROCESS ) ; + } + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function hidd_mgmt_conn_closed +** +** Description Called when l2cap channels have been released +** +** Returns void +** +*******************************************************************************/ +void hidd_mgmt_conn_closed( UINT16 reason ) +{ + if( hd_cb.unplug_on ) + { + hd_cb.host_known = FALSE; /* This allows the device to accept connection from other hosts */ + } + + hd_cb.dev_state = HID_DEV_ST_NO_CONN; + hd_cb.callback(HID_DEV_EVT_CLOSE, reason, NULL ); +} + +/******************************************************************************* +** +** Function hidd_connected_proc_evt +** +** Description CONNECTED state event handler +** +** Returns tHID_STATUS +** +*******************************************************************************/ +static tHID_STATUS hidd_connected_proc_evt( UINT8 event, void *p_data ) +{ + switch( event ) + { + case HOST_CONN_LOST: +#if HID_DEV_RECONN_INITIATE == TRUE + hd_cb.dev_state = HID_DEV_ST_CONNECTING ; + hd_cb.conn_tries = 0; + btu_start_timer (&(hd_cb.conn.timer_entry), BTU_TTYPE_USER_FUNC, HID_DEV_REPAGE_WIN); +#else + hidd_mgmt_conn_closed( *((UINT16 *) p_data) ) ; +#endif + break; + case HOST_CONN_CLOSE: + hidd_mgmt_conn_closed( *((UINT16 *) p_data) ) ; + break; + case HID_API_DISCONNECT: + hd_cb.dev_state = HID_DEV_ST_DISC_ING ; + hidd_conn_disconnect(); + break; + case HID_API_SEND_DATA: /*Send Input reports, handshake or virtual-unplug */ + return hidd_conn_snd_data( (tHID_SND_DATA_PARAMS *) p_data ); + default: + return( HID_ERR_ALREADY_CONN ) ; + } + + return( HID_SUCCESS ); +} + +/******************************************************************************* +** +** Function hidd_disc_ing_proc_evt +** +** Description DISCONNECTING state event handler +** +** Returns tHID_STATUS +** +*******************************************************************************/ +static tHID_STATUS hidd_disc_ing_proc_evt( UINT8 event, void *p_data ) +{ + switch( event ) + { + case HOST_CONN_LOST: + case HOST_CONN_FAIL: + case HOST_CONN_CLOSE: + hidd_mgmt_conn_closed( *((UINT16 *) p_data) ) ; + break; + default: + return( HID_ERR_DISCONNECTING ) ; + } + + return( HID_SUCCESS ); +} + + diff --git a/stack/hid/hidd_pm.c b/stack/hid/hidd_pm.c new file mode 100644 index 0000000..ff3cc22 --- /dev/null +++ b/stack/hid/hidd_pm.c @@ -0,0 +1,288 @@ +/*****************************************************************************/ +/* */ +/* Name: hidd_pm.c */ +/* */ +/* Description: this file contains the HID Device Power Management logic */ +/* */ +/* */ +/* Copyright (c) 2002-2004, WIDCOMM Inc., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ + + +#include "gki.h" +#include "bt_types.h" +#include "hiddefs.h" +#include "btm_api.h" +#include "string.h" + +#include "hiddefs.h" + +#include "hidd_api.h" +#include "hidd_int.h" +#include "btu.h" + +#if HID_DEV_PM_INCLUDED == TRUE + +/******************************************************************************* +** +** Function hidd_pm_init +** +** Description This function is called when connection is established. It +** initializes the control block for power management. +** +** Returns void +** +*******************************************************************************/ + +static const tHID_DEV_PM_PWR_MD pwr_modes[] = +{ + HID_DEV_BUSY_MODE_PARAMS, + HID_DEV_IDLE_MODE_PARAMS, + HID_DEV_SUSP_MODE_PARAMS +}; + +void hidd_pm_init( void ) +{ + int i; + + hd_cb.curr_pm.mode = HCI_MODE_ACTIVE ; + hd_cb.final_pm.mode = 0xff; + + for( i=0; i<3; i++ ) + memcpy( &hd_cb.pm_params[i], &pwr_modes[i], sizeof( tHID_DEV_PM_PWR_MD ) ) ; + + hd_cb.pm_ctrl_busy = FALSE; +} + +/******************************************************************************* +** +** Function hidd_pm_set_now +** +** Description This drives the BTM for power management settings. +** +** Returns TRUE if Success, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN hidd_pm_set_now( tHID_DEV_PM_PWR_MD *p_req_mode) +{ + tHID_DEV_PM_PWR_MD act_pm = { 0, 0, 0, 0, HCI_MODE_ACTIVE } ; + UINT8 st = BTM_SUCCESS; + + /* Do nothing if already in required state or already performing a pm function */ + if( (hd_cb.pm_ctrl_busy) || + ((p_req_mode->mode == hd_cb.curr_pm.mode) && ( (p_req_mode->mode == HCI_MODE_ACTIVE) || + ((hd_cb.curr_pm.interval >= p_req_mode->min) && (hd_cb.curr_pm.interval <= p_req_mode->max)) )) ) + { + hd_cb.final_pm.mode = 0xff; + return TRUE; + } + + switch( p_req_mode->mode ) + { + case HCI_MODE_ACTIVE: + if( hd_cb.curr_pm.mode == HCI_MODE_SNIFF ) + { +#if BTM_PWR_MGR_INCLUDED == TRUE + st = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, hd_cb.host_addr, (tBTM_PM_PWR_MD *) &act_pm); +#else + st = BTM_CancelSniffMode (hd_cb.host_addr); +#endif + hd_cb.pm_ctrl_busy = TRUE; + } + else if( hd_cb.curr_pm.mode == HCI_MODE_PARK ) + { +#if BTM_PWR_MGR_INCLUDED == TRUE + st = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, hd_cb.host_addr, (tBTM_PM_PWR_MD *) &act_pm); +#else + st = BTM_CancelParkMode (hd_cb.host_addr); +#endif + hd_cb.pm_ctrl_busy = TRUE; + } + break; + + case HCI_MODE_SNIFF: + if( hd_cb.curr_pm.mode != HCI_MODE_ACTIVE ) /* Transition through active state required */ + hidd_pm_set_now (&act_pm); + else + { +#if BTM_PWR_MGR_INCLUDED == TRUE + st = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, hd_cb.host_addr, (tBTM_PM_PWR_MD *) p_req_mode); +#else + st = BTM_SetSniffMode (hd_cb.host_addr, p_req_mode->min, p_req_mode->max, p_req_mode->attempt, p_req_mode->timeout); +#endif + hd_cb.pm_ctrl_busy = TRUE; + } + break; + + case HCI_MODE_PARK: + if( hd_cb.curr_pm.mode != HCI_MODE_ACTIVE ) /* Transition through active state required */ + hidd_pm_set_now (&act_pm); + else + { +#if BTM_PWR_MGR_INCLUDED == TRUE + st = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, hd_cb.host_addr, (tBTM_PM_PWR_MD *) p_req_mode); +#else + st = BTM_SetParkMode (hd_cb.host_addr, p_req_mode->min, p_req_mode->max); +#endif + hd_cb.pm_ctrl_busy = TRUE; + } + break; + + default: + break; + } + + if( st == BTM_SUCCESS || st == BTM_CMD_STARTED ) + return TRUE; + else + { + st += HCI_ERR_MAX_ERR ; + if( hd_cb.callback ) + hd_cb.callback( HID_DEV_EVT_PM_FAILED, hd_cb.conn_substate, (tHID_DEV_CBACK_DATA *) &st ) ; + return FALSE; + } +} + +/******************************************************************************* +** +** Function hidd_pm_set_power_mode +** +** Description This stores the power management setting and calls fn to set +** the power. +** +** Returns TRUE if Success, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN hidd_pm_set_power_mode( tHID_DEV_PM_PWR_MD *p_req_mode) +{ + memcpy( &hd_cb.final_pm, p_req_mode, sizeof( tHID_DEV_PM_PWR_MD ) ) ; + return hidd_pm_set_now( p_req_mode ) ; +} + + +/******************************************************************************* +** +** Function hidd_pm_proc_mode_change +** +** Description This is the callback function, when power mode changes. +** +** Returns void +** +*******************************************************************************/ +void hidd_pm_proc_mode_change( UINT8 hci_status, UINT8 mode, UINT16 interval ) +{ + if (!hd_cb.reg_flag ) + return; + + hd_cb.pm_ctrl_busy = FALSE; + + if( hci_status != HCI_SUCCESS ) + { + if( hd_cb.callback ) + hd_cb.callback( HID_DEV_EVT_PM_FAILED, hd_cb.conn_substate, (tHID_DEV_CBACK_DATA *) &hci_status ) ; + } + else + { + hd_cb.curr_pm.mode = mode; + hd_cb.curr_pm.interval = interval; + + if( hd_cb.final_pm.mode != 0xff ) + { + /* If we haven't reached the final power mode, set it now */ + if( (hd_cb.final_pm.mode != hd_cb.curr_pm.mode) || + ( (hd_cb.final_pm.mode != HCI_MODE_ACTIVE) && + ((hd_cb.curr_pm.interval < hd_cb.final_pm.min) || (hd_cb.curr_pm.interval > hd_cb.final_pm.max)) + ) ) + { + hidd_pm_set_now( &(hd_cb.final_pm) ) ; + } + else + hd_cb.final_pm.mode = 0xff; + } + else + { + if( hd_cb.curr_pm.mode == HCI_MODE_ACTIVE ) + hidd_pm_start(); + } + + if( hd_cb.callback ) + hd_cb.callback( HID_DEV_EVT_MODE_CHG, mode, (tHID_DEV_CBACK_DATA *) &interval) ; + } +} + + +/******************************************************************************* +** +** Function hidd_pm_inact_timeout +** +** Description Called when idle timer expires. +** +** Returns void +** +*******************************************************************************/ +void hidd_pm_inact_timeout (TIMER_LIST_ENT *p_tle) +{ + hidd_pm_set_power_mode ( &(hd_cb.pm_params[HID_DEV_IDLE_CONN_ST])); + hd_cb.conn_substate = HID_DEV_IDLE_CONN_ST; +} + +/******************************************************************************* +** +** Function hidd_pm_start +** +** Description Starts the power management function in a given state. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS hidd_pm_start( void ) +{ + hidd_pm_set_power_mode ( &(hd_cb.pm_params[HID_DEV_BUSY_CONN_ST]) ); + + hd_cb.conn_substate = HID_DEV_BUSY_CONN_ST; + + hd_cb.idle_tle.param = (UINT32) hidd_pm_inact_timeout; + btu_start_timer (&hd_cb.idle_tle, BTU_TTYPE_USER_FUNC, HID_DEV_INACT_TIMEOUT); + + return( HID_SUCCESS ); +} + +/******************************************************************************* +** +** Function hidd_pm_stop +** +** Description Stops the idle timer. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS hidd_pm_stop( void ) +{ + tHID_DEV_PM_PWR_MD p_md = { 0, 0, 0, 0, HCI_MODE_ACTIVE }; + + hidd_pm_set_power_mode( &p_md ); + btu_stop_timer( &hd_cb.idle_tle ) ; + return( HID_SUCCESS ); +} + + +/******************************************************************************* +** +** Function hidd_pm_suspend_evt +** +** Description Called when host suspends the device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS hidd_pm_suspend_evt( void ) +{ + if( hd_cb.conn_substate == HID_DEV_BUSY_CONN_ST ) + btu_stop_timer( &hd_cb.idle_tle ) ; + + hidd_pm_set_power_mode ( &(hd_cb.pm_params[HID_DEV_SUSP_CONN_ST]) ); + hd_cb.conn_substate = HID_DEV_SUSP_CONN_ST; + return( HID_SUCCESS ); +} +#endif /* HID_DEV_PM_INCLUDED == TRUE */ diff --git a/stack/hid/hidh_api.c b/stack/hid/hidh_api.c new file mode 100644 index 0000000..268b405 --- /dev/null +++ b/stack/hid/hidh_api.c @@ -0,0 +1,534 @@ +/*****************************************************************************/ +/* */ +/* Name: hidh_api.c */ +/* */ +/* Description: this file contains the HID HOST API entry points */ +/* */ +/* */ +/* */ +/* Copyright (c) 2002-2010, Broadcom Corp., All Rights Reserved. */ +/* Broadcom Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ + +#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" + +#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] = '\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_max_latency = 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 ; + } + + 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; + } + + 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_HID_SEC_CTRL, + sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) + { + HIDH_TRACE_ERROR0 ("Security Registration 1 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL, + sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) + { + HIDH_TRACE_ERROR0 ("Security Registration 2 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL, + BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) + { + HIDH_TRACE_ERROR0 ("Security Registration 3 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL, + BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) + { + HIDH_TRACE_ERROR0 ("Security Registration 4 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_INTR, + BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) + { + HIDH_TRACE_ERROR0 ("Security Registration 5 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_INTR, + 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 ); +} diff --git a/stack/hid/hidh_conn.c b/stack/hid/hidh_conn.c new file mode 100644 index 0000000..4744e10 --- /dev/null +++ b/stack/hid/hidh_conn.c @@ -0,0 +1,1021 @@ +/***************************************************************************** +** +** Name: hid_conn.c +** +** Description: this file contains the connection interface functions +** +** +** Copyright (c) 2002-2010, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +******************************************************************************/ + +#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" + +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 - 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; + + 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; + int i; + tHID_HOST_DEV_CTB *p_dev; + + HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); + + for( i=0; i < HID_HOST_MAX_DEVICES; i++ ) + { + if( hh_cb.devices[i].in_use && (!memcmp(bd_addr, hh_cb.devices[i].addr, sizeof(BD_ADDR))) ) + break; + } + + if (i >= HID_HOST_MAX_DEVICES) + { + 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 - Rcvd INTR L2CAP conn ind, but no CTL channel"); + bAccept = FALSE; + } + if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd INTR L2CAP conn ind, wrong state: %d", p_hcon->conn_state); + 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 - 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 - 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, 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; + + dhandle = p_dev - &(hh_cb.devices[0]) ; + if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) + { + HIDH_TRACE_EVENT0 ("HID - Originator security pass."); + p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */ + + /* Check if L2CAP started the connection process for interrupt channel */ + if ((p_dev->conn.intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0) + { + HIDH_TRACE_WARNING0 ("HID - INTR Originate failed"); + reason = HID_L2CAP_REQ_FAIL ; + hidh_conn_disconnect (dhandle); + hh_cb.callback( dhandle, 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 - 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, 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 - 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 - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT1 ("HID - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid); + + /* 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, 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 - 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 - 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, 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, 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 - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); + return; + } + + if (ack_needed) + L2CA_DisconnectRsp (l2cap_cid); + + HIDH_TRACE_EVENT1 ("HID - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); + + 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, 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; + + /* 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 - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT1 ("HID - 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, 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 - Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP congestion status, CID: 0x%x Cong: %d", l2cap_cid, congested); + + 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 - 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 - 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, 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, 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, evt, rep_type, p_msg); + break; + + case HID_TRANS_DATAC: + evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ? + HID_HDEV_EVT_INTR_DATC : HID_HDEV_EVT_CTRL_DATC; + hh_cb.callback(dhandle, evt, rep_type, p_msg); + 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_DATA: + cid = p_hcon->intr_cid; + pool_id = HID_INTERRUPT_POOL_ID; + break; + default: + return (HID_ERR_INVALID_PARAM) ; + } + + 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_HID_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_HID_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 - Originate failed"); + dhandle = (p_dev - &(hh_cb.devices[0]))/(sizeof( tHID_HOST_DEV_CTB )) ; + hh_cb.callback( dhandle, 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/hidh_int.h b/stack/hid/hidh_int.h new file mode 100644 index 0000000..8e1a8a2 --- /dev/null +++ b/stack/hid/hidh_int.h @@ -0,0 +1,81 @@ +/****************************************************************************/ +/* */ +/* Name: hidh_int.h */ +/* */ +/* Function: this file contains HID HOST internal definitions */ +/* */ +/* Copyright (c) 2002-2004, WIDCOMM Inc., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/* */ +/****************************************************************************/ + +#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 + |