藍芽speaker配對流程原始碼分析
這篇文章簡單分析一下 藍芽音箱配對流程.現在的音箱基本都支援security simple pairing.所以這裡的流程基本上就是ssp的程式碼流程.
原始碼參考的是 Android 6.0 上面的bluedroid.這裡先介紹一些bluedroid定義的概率.
首先介紹一下 配對的幾個狀態:pairing_cb.state ,這個定義在bluetooth.h裡面.
/** Bluetooth Bond state */ typedef enum { BT_BOND_STATE_NONE, BT_BOND_STATE_BONDING, BT_BOND_STATE_BONDED } bt_bond_state_t;
每次有配對狀態發生改變的時候,通過bond_state_changed 來向上層彙報狀態.
bluetooth.c
static int create_bond(const bt_bdaddr_t *bd_addr, int transport) { /* sanity check */ if (interface_ready() == FALSE) return BT_STATUS_NOT_READY; return btif_dm_create_bond(bd_addr, transport); }
btif_dm.c
/******************************************************************************* ** ** Function btif_dm_create_bond ** ** Description Initiate bonding with the specified device ** ** Returns bt_status_t ** *******************************************************************************/ bt_status_t btif_dm_create_bond(const bt_bdaddr_t *bd_addr, int transport) { btif_dm_create_bond_cb_t create_bond_cb; create_bond_cb.transport = transport; bdcpy(create_bond_cb.bdaddr.address, bd_addr->address); bdstr_t bdstr; BTIF_TRACE_EVENT("%s: bd_addr=%s, transport=%d", __FUNCTION__, bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr)), transport); if (pairing_cb.state != BT_BOND_STATE_NONE) return BT_STATUS_BUSY; btif_transfer_context(btif_dm_generic_evt, BTIF_DM_CB_CREATE_BOND, (char *)&create_bond_cb, sizeof(btif_dm_create_bond_cb_t), NULL); return BT_STATUS_SUCCESS; }
當前的配對狀態是 BT_BOND_STATE_NONE,
btif_dm.c:
/******************************************************************************* ** ** Function btif_dm_generic_evt ** ** Description Executes non-BTA upstream events in BTIF context ** ** Returns void ** *******************************************************************************/ static void btif_dm_generic_evt(UINT16 event, char* p_param) { BTIF_TRACE_EVENT("%s: event=%d", __FUNCTION__, event); switch(event) { case BTIF_DM_CB_DISCOVERY_STARTED: { HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb, BT_DISCOVERY_STARTED); } break; case BTIF_DM_CB_CREATE_BOND: { pairing_cb.timeout_retries = NUM_TIMEOUT_RETRIES; btif_dm_create_bond_cb_t *create_bond_cb = (btif_dm_create_bond_cb_t*)p_param; btif_dm_cb_create_bond(&create_bond_cb->bdaddr, create_bond_cb->transport); } break;
繼續看:
/******************************************************************************* ** ** Function btif_dm_cb_create_bond ** ** Description Create bond initiated from the BTIF thread context ** Special handling for HID devices ** ** Returns void ** *******************************************************************************/ static void btif_dm_cb_create_bond(bt_bdaddr_t *bd_addr, tBTA_TRANSPORT transport) { BOOLEAN is_hid = check_cod(bd_addr, COD_HID_POINTING); bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING); #if BLE_INCLUDED == TRUE int device_type; int addr_type; bdstr_t bdstr; bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr)); if (transport == BT_TRANSPORT_LE) { ... } if((btif_config_get_int((char const *)&bdstr,"DevType", &device_type) && (btif_storage_get_remote_addr_type(bd_addr, &addr_type) == BT_STATUS_SUCCESS) && (device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) || (transport == BT_TRANSPORT_LE)) { BTA_DmAddBleDevice(bd_addr->address, addr_type, device_type); } #endif #if BLE_INCLUDED == TRUE if(is_hid && (device_type & BT_DEVICE_TYPE_BLE) == 0) #else if(is_hid) #endif { int status; status = btif_hh_connect(bd_addr); if(status != BT_STATUS_SUCCESS) bond_state_changed(status, bd_addr, BT_BOND_STATE_NONE); } else { BTA_DmBondByTransport((UINT8 *)bd_addr->address, transport); //非HID,繼續進入到BTA } /* Track originator of bond creation */ pairing_cb.is_local_initiated = TRUE; }
bta_dm_api.c
/******************************************************************************* ** ** Function BTA_DmBondByTransports ** ** Description This function initiates a bonding procedure with a peer ** device ** ** ** Returns void ** *******************************************************************************/ void BTA_DmBondByTransport(BD_ADDR bd_addr, tBTA_TRANSPORT transport) { tBTA_DM_API_BOND *p_msg; if ((p_msg = (tBTA_DM_API_BOND *) GKI_getbuf(sizeof(tBTA_DM_API_BOND))) != NULL) { p_msg->hdr.event = BTA_DM_API_BOND_EVT; bdcpy(p_msg->bd_addr, bd_addr); p_msg->transport = transport; bta_sys_sendmsg(p_msg); } }
關於訊息的傳送流程,這裡就不講了,直接分析所執行的函式:
BTA_DM_API_BOND_EVT // BTA got event 0x107
bta_dm_main.c
BOOLEAN bta_dm_sm_execute(BT_HDR *p_msg) { UINT16 event = p_msg->event & 0x00ff; APPL_TRACE_EVENT("bta_dm_sm_execute event:0x%x", event); /* execute action functions */ if(event < BTA_DM_NUM_ACTIONS) { (*bta_dm_action[event])( (tBTA_DM_MSG*) p_msg); } return TRUE; }
bta_dm_bond, /* 11 BTA_DM_API_BOND_EVT */
到目前為止,transport = BTA_TRANSPORT_UNKNOWN = 0
bta_dm_act.c
/******************************************************************************* ** ** Function bta_dm_bond ** ** Description Bonds with peer device ** ** ** Returns void ** *******************************************************************************/ void bta_dm_bond (tBTA_DM_MSG *p_data) { tBTM_STATUS status; tBTA_DM_SEC sec_event; char *p_name; if (p_data->bond.transport == BTA_TRANSPORT_UNKNOWN) status = BTM_SecBond ( p_data->bond.bd_addr, 0, NULL, 0 );//0 else status = BTM_SecBondByTransport ( p_data->bond.bd_addr, p_data->bond.transport, 0, NULL, 0 ); if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED)) { ... } }
btm_sec.c 進入到stack/btm 了.
/******************************************************************************* ** ** Function BTM_SecBond ** ** Description This function is called to perform bonding with peer device. ** If the connection is already up, but not secure, pairing ** is attempted. If already paired BTM_SUCCESS is returned. ** ** Parameters: bd_addr - Address of the device to bond ** pin_len - length in bytes of the PIN Code ** p_pin - pointer to array with the PIN Code ** trusted_mask - bitwise OR of trusted services (array of UINT32) ** ** Note: After 2.1 parameters are not used and preserved here not to change API *******************************************************************************/ tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) { tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; #if BLE_INCLUDED == TRUE if (BTM_UseLeLink(bd_addr)) transport = BT_TRANSPORT_LE; #endif return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask); }
在btm 裡面同樣對於配對有相應的狀態轉換 btm_cb.pairing_state:
定義在btm_int.h裡面:
/* Pairing State */ enum { BTM_PAIR_STATE_IDLE, /* Idle */ BTM_PAIR_STATE_GET_REM_NAME, /* Getting the remote name (to check for SM4) */ BTM_PAIR_STATE_WAIT_PIN_REQ, /* Started authentication, waiting for PIN req (PIN is pre-fetched) */ BTM_PAIR_STATE_WAIT_LOCAL_PIN, /* Waiting for local PIN code */ BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM, /* Waiting user 'yes' to numeric confirmation */ BTM_PAIR_STATE_KEY_ENTRY, /* Key entry state (we are a keyboard) */ BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP, /* Waiting for local response to peer OOB data */ BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS, /* Waiting for local IO capabilities and OOB data */ BTM_PAIR_STATE_INCOMING_SSP, /* Incoming SSP (got peer IO caps when idle) */ BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, /* All done, waiting authentication cpmplete */ BTM_PAIR_STATE_WAIT_DISCONNECT /* Waiting to disconnect the ACL */ };
這裡先 說一下一般的 btm_cb.pairing_state 裡面的配對狀態轉換的流程:
BTM_PAIR_STATE_IDLE -->BTM_PAIR_STATE_GET_REM_NAME -->BTM_PAIR_STATE_WAIT_PIN_REQ-->BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS(SSP)--->BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM(depends on IO)-->BTM_PAIR_STATE_WAIT_AUTH_COMPLETE-->BTM_PAIR_STATE_IDLE
關於security 的flag 定義在btm_int.h:
#define BTM_SEC_AUTHORIZED BTM_SEC_FLAG_AUTHORIZED /* 0x01 */ #define BTM_SEC_AUTHENTICATED BTM_SEC_FLAG_AUTHENTICATED /* 0x02 */ #define BTM_SEC_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED /* 0x04 */ #define BTM_SEC_NAME_KNOWN 0x08 #define BTM_SEC_LINK_KEY_KNOWN BTM_SEC_FLAG_LKEY_KNOWN /* 0x10 */ #define BTM_SEC_LINK_KEY_AUTHED BTM_SEC_FLAG_LKEY_AUTHED /* 0x20 */ #define BTM_SEC_ROLE_SWITCHED 0x40 #define BTM_SEC_IN_USE 0x80
那麼最終啟動security的時候,就初始化為BTM_SEC_IN_USE = 0x80
/******************************************************************************* ** ** Function btm_sec_bond_by_transport ** ** Description this is the bond function that will start either SSP or SMP. ** ** Parameters: bd_addr - Address of the device to bond ** pin_len - length in bytes of the PIN Code ** p_pin - pointer to array with the PIN Code ** trusted_mask - bitwise OR of trusted services (array of UINT32) ** ** Note: After 2.1 parameters are not used and preserved here not to change API *******************************************************************************/ tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) { tBTM_SEC_DEV_REC *p_dev_rec; tBTM_STATUS status; UINT8 *p_features; UINT8 ii; tACL_CONN *p= btm_bda_to_acl(bd_addr, transport); BTM_TRACE_API ("btm_sec_bond_by_transport BDA: %02x:%02x:%02x:%02x:%02x:%02x", bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); /* Other security process is in progress */ if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) { BTM_TRACE_ERROR ("BTM_SecBond: already busy in state: %s", btm_pair_state_descr(btm_cb.pairing_state)); return(BTM_WRONG_MODE); } if ((p_dev_rec = btm_find_or_alloc_dev (bd_addr)) == NULL)//find from btm_cb.sec_dev_rec { return(BTM_NO_RESOURCES); } BTM_TRACE_DEBUG ("before update sec_flags=0x%x", p_dev_rec->sec_flags);//first time 0x80 /* Finished if connection is active and already paired */ if ( ((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_BR_EDR && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) #if (BLE_INCLUDED == TRUE) ||((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_LE && (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED)) #endif ) { BTM_TRACE_WARNING("BTM_SecBond -> Already Paired"); return(BTM_SUCCESS); } /* Tell controller to get rid of the link key if it has one stored */ if ((BTM_DeleteStoredLinkKey (bd_addr, NULL)) != BTM_SUCCESS)//delete link key return(BTM_NO_RESOURCES); /* Save the PIN code if we got a valid one */ if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) //we have not got one valid { ... } memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN); btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD; p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE;///* Outbound call requires authentication */ 0x10 p_dev_rec->is_originator = TRUE; if (trusted_mask) BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); ... p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED);/*clear the all the flags*/ BTM_TRACE_DEBUG ("after update sec_flags=0x%x", p_dev_rec->sec_flags); //still in use 0x80 if (!controller_get_interface()->supports_simple_pairing()) //local HCI_Read_Local_Extended_Features
{ /* The special case when we authenticate keyboard. Set pin type to fixed */ /* It would be probably better to do it from the application, but it is */ /* complicated */ if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL) && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD) && (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED)) { btm_cb.pin_type_changed = TRUE; btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED); } } for (ii = 0; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++) { p_features = p_dev_rec->features[ii]; BTM_TRACE_EVENT(" remote_features page[%1d] = %02x-%02x-%02x-%02x", ii, p_features[0], p_features[1], p_features[2], p_features[3]); BTM_TRACE_EVENT(" %02x-%02x-%02x-%02x", p_features[4], p_features[5], p_features[6], p_features[7]); } BTM_TRACE_EVENT ("BTM_SecBond: Remote sm4: 0x%x HCI Handle: 0x%04x", p_dev_rec->sm4, p_dev_rec->hci_handle);//have not got remote feature /* If connection already exists... */ if (p && p->hci_handle != BTM_SEC_INVALID_HANDLE)/* then start authentication*/ { if (!btm_sec_start_authentication (p_dev_rec)) return(BTM_NO_RESOURCES); btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); /* Mark lcb as bonding */ l2cu_update_lcb_4_bonding (bd_addr, TRUE); return(BTM_CMD_STARTED); } BTM_TRACE_DEBUG ("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4);//local mode = 4. if (!controller_get_interface()->supports_simple_pairing() || (p_dev_rec->sm4 == BTM_SM4_KNOWN)) // sm4 of remote is not known now { if ( btm_sec_check_prefetch_pin (p_dev_rec) ) return (BTM_CMD_STARTED); } if ((btm_cb.security_mode == BTM_SEC_MODE_SP || btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || btm_cb.security_mode == BTM_SEC_MODE_SC) && BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { /* local is 2.1 and peer is unknown */ if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0) { /* we are not accepting connection request from peer * -> RNR (to learn if peer is 2.1) * RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */ btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);//begin to get rmt name } else { /* We are accepting connection request from peer */ btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); } BTM_TRACE_DEBUG ("State:%s sm4: 0x%x sec_state:%d", btm_pair_state_descr (btm_cb.pairing_state), p_dev_rec->sm4, p_dev_rec->sec_state); return BTM_CMD_STARTED; } /* both local and peer are 2.1 */ status = btm_sec_dd_create_conn(p_dev_rec); if (status != BTM_CMD_STARTED) { btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); } return status; }
從這裡的邏輯看出,如果我們的2.1 並且對方的feature還有獲取到,那麼我們就先進行remote name的獲取.並且此刻btm_cb.pairing_state狀態變為:BTM_PAIR_STATE_GET_REM_NAME
下面我們看看RNR的流程:
/******************************************************************************* ** ** Function BTM_ReadRemoteDeviceName ** ** Description This function initiates a remote device HCI command to the ** controller and calls the callback when the process has completed. ** ** Input Params: remote_bda - device address of name to retrieve ** p_cb - callback function called when BTM_CMD_STARTED ** is returned. ** A pointer to tBTM_REMOTE_DEV_NAME is passed to the ** callback. ** ** Returns ** BTM_CMD_STARTED is returned if the request was successfully sent ** to HCI. ** BTM_BUSY if already in progress ** BTM_UNKNOWN_ADDR if device address is bad ** BTM_NO_RESOURCES if could not allocate resources to start the command ** BTM_WRONG_MODE if the device is not up. ** *******************************************************************************/ tBTM_STATUS BTM_ReadRemoteDeviceName (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb ,tBT_TRANSPORT transport) { tBTM_INQ_INFO *p_cur = NULL; tINQ_DB_ENT *p_i; BTM_TRACE_API ("BTM_ReadRemoteDeviceName: bd addr [%02x%02x%02x%02x%02x%02x]", remote_bda[0], remote_bda[1], remote_bda[2], remote_bda[3], remote_bda[4], remote_bda[5]); /* Use the remote device's clock offset if it is in the local inquiry database */ if ((p_i = btm_inq_db_find (remote_bda)) != NULL) { p_cur = &p_i->inq_info; } BTM_TRACE_API ("no device found in inquiry db"); if (transport == BT_TRANSPORT_LE) { return btm_ble_read_remote_name(remote_bda, p_cur, p_cb); } else return (btm_initiate_rem_name (remote_bda, p_cur, BTM_RMT_NAME_EXT, BTM_EXT_RMT_NAME_TIMEOUT, p_cb)); }
從上面看 這個p_cb的回撥是NULL.
btm_inq.c
/******************************************************************************* ** ** Function btm_initiate_rem_name ** ** Description This function looks initiates a remote name request. It is called ** either by GAP or by the API call BTM_ReadRemoteDeviceName. ** ** Input Params: p_cur - pointer to an inquiry result structure (NULL if nonexistent) ** p_cb - callback function called when BTM_CMD_STARTED ** is returned. ** A pointer to tBTM_REMOTE_DEV_NAME is passed to the ** callback. ** ** Returns ** BTM_CMD_STARTED is returned if the request was sent to HCI. ** BTM_BUSY if already in progress ** BTM_NO_RESOURCES if could not allocate resources to start the command ** BTM_WRONG_MODE if the device is not up. ** *******************************************************************************/ tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, UINT8 origin, UINT32 timeout, tBTM_CMPL_CB *p_cb) { tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; BOOLEAN cmd_ok; ... if (origin == BTM_RMT_NAME_SEC) { ... } /* Make sure there are no two remote name requests from external API in progress */ else if (origin == BTM_RMT_NAME_EXT) { if (p_inq->remname_active) { return (BTM_BUSY); } else { /* If there is no remote name request running,call the callback function and start timer */ p_inq->p_remname_cmpl_cb = p_cb;//NULL memcpy(p_inq->remname_bda, remote_bda, BD_ADDR_LEN); btu_start_timer (&p_inq->rmt_name_timer_ent, BTU_TTYPE_BTM_RMT_NAME, timeout); /* If the database entry exists for the device, use its clock offset */ if (p_cur) { cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, p_cur->results.page_scan_rep_mode, p_cur->results.page_scan_mode, (UINT16)(p_cur->results.clock_offset | BTM_CLOCK_OFFSET_VALID));//start hci command } else /* Otherwise use defaults and mark the clock offset as invalid */ { cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, HCI_PAGE_SCAN_REP_MODE_R1, HCI_MANDATARY_PAGE_SCAN_MODE, 0); } if (cmd_ok) { p_inq->remname_active = TRUE; return BTM_CMD_STARTED; } else return BTM_NO_RESOURCES; } } else { return BTM_ILLEGAL_VALUE; } }
從這裡我們發現,其就已經開始了RNR流程了.
這個cmd 其實不僅僅有獲取名字的功能,當底層獲取了remote 的feature,那麼這個時候也會通過
Event: HCI Remote Host Supported Features Notification
上報.
我們接下來看看這個事件的處理過程:
btu_hcif.c
case HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT: btu_hcif_host_support_evt (p); break;
btm_Sec.c
/******************************************************************************* ** ** Function btm_sec_rmt_host_support_feat_evt ** ** Description This function is called when the ** HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is received ** ** Returns void ** *******************************************************************************/ void btm_sec_rmt_host_support_feat_evt (UINT8 *p) { tBTM_SEC_DEV_REC *p_dev_rec; BD_ADDR bd_addr; /* peer address */ BD_FEATURES features; STREAM_TO_BDADDR (bd_addr, p); p_dev_rec = btm_find_or_alloc_dev (bd_addr); if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { p_dev_rec->sm4 = BTM_SM4_KNOWN;//now sm4 is known STREAM_TO_ARRAY(features, p, HCI_FEATURE_BYTES_PER_PAGE); int xx = 0; for(xx = 0;xx<HCI_FEATURE_BYTES_PER_PAGE;xx++) BTM_TRACE_EVENT("features[%d] = %d libs_liu",xx,features[xx]); if (HCI_SSP_HOST_SUPPORTED(features)) { p_dev_rec->sm4 = BTM_SM4_TRUE;//0x11 } } }
現在就已經知道remote devices 的sm了.支援ssp的話,sm4 = 4
下面我們看看controller 獲取到名字之後host端的處理:
btu_hcif.c
/******************************************************************************* ** ** Function btu_hcif_rmt_name_request_comp_evt ** ** Description Process event HCI_RMT_NAME_REQUEST_COMP_EVT ** ** Returns void ** *******************************************************************************/ static void btu_hcif_rmt_name_request_comp_evt (UINT8 *p, UINT16 evt_len) { UINT8 status; BD_ADDR bd_addr; STREAM_TO_UINT8 (status, p); STREAM_TO_BDADDR (bd_addr, p); evt_len -= (1 + BD_ADDR_LEN); btm_process_remote_name (bd_addr, p, evt_len, status);//獲取名字,但是發現並沒有去儲存 btm_sec_rmt_name_request_complete (bd_addr, p, status);//這裡儲存名字 }
我們先看看 上面那個處理名字的函式:
btm_inq.c
/******************************************************************************* ** ** Function btm_process_remote_name ** ** Description This function is called when a remote name is received from ** the device. If remote names are cached, it updates the inquiry ** database. ** ** Returns void ** *******************************************************************************/ void btm_process_remote_name (BD_ADDR bda, BD_NAME bdn, UINT16 evt_len, UINT8 hci_status) { tBTM_REMOTE_DEV_NAME rem_name; tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; tBTM_CMPL_CB *p_cb = p_inq->p_remname_cmpl_cb;//NULL UINT8 *p_n1; UINT16 temp_evt_len; if (bda != NULL) { BTM_TRACE_EVENT("BDA %02x:%02x:%02x:%02x:%02x:%02x",bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); } ... /* If the inquire BDA and remote DBA are the same, then stop the timer and set the active to false */ if ((p_inq->remname_active ==TRUE)&& (((bda != NULL) && (memcmp(bda, p_inq->remname_bda,BD_ADDR_LEN)==0)) || bda == NULL)) { #if BLE_INCLUDED == TRUE if (BTM_UseLeLink(p_inq->remname_bda)) { if (hci_status == HCI_ERR_UNSPECIFIED) btm_ble_cancel_remote_name(p_inq->remname_bda); } #endif btu_stop_timer (&p_inq->rmt_name_timer_ent);//停 timer p_inq->remname_active = FALSE; /* Clean up and return the status if the command was not successful */ /* Note: If part of the inquiry, the name is not stored, and the */ /* inquiry complete callback is called. */ if (hci_status == HCI_SUCCESS) { /* Copy the name from the data stream into the return structure */ /* Note that even if it is not being returned, it is used as a */ /* temporary buffer. */ p_n1 = (UINT8 *)rem_name.remote_bd_name; rem_name.length = (evt_len < BD_NAME_LEN) ? evt_len : BD_NAME_LEN; rem_name.remote_bd_name[rem_name.length] = 0; rem_name.status = BTM_SUCCESS; temp_evt_len = rem_name.length; while (temp_evt_len > 0) { *p_n1++ = *bdn++; temp_evt_len--; } rem_name.remote_bd_name[rem_name.length] = 0;//temp struction 未返回 } /* If processing a stand alone remote name then report the error in the callback */ else { rem_name.status = BTM_BAD_VALUE_RET; rem_name.length = 0; rem_name.remote_bd_name[0] = 0; } /* Reset the remote BAD to zero and call callback if possible */ memset(p_inq->remname_bda, 0, BD_ADDR_LEN); p_inq->p_remname_cmpl_cb = NULL; if (p_cb)//null (p_cb)((tBTM_REMOTE_DEV_NAME *)&rem_name); } }
我們再看看btm_sec_rmt_name_request_complete的實現:
btm_Sec.c
看名字,應該還是和配對流程相關.
這裡發現remote device的安全轉換狀態也是有一個狀態轉換的.其變數名是p_dev_rec->sec_state
其可以取的值定義在btm_int.h裡面:
#define BTM_SEC_STATE_IDLE 0 #define BTM_SEC_STATE_AUTHENTICATING 1 #define BTM_SEC_STATE_ENCRYPTING 2 #define BTM_SEC_STATE_GETTING_NAME 3 #define BTM_SEC_STATE_AUTHORIZING 4 #define BTM_SEC_STATE_SWITCHING_ROLE 5 #define BTM_SEC_STATE_DISCONNECTING 6 /* disconnecting BR/EDR */ #define BTM_SEC_STATE_DELAY_FOR_ENC 7 /* delay to check for encryption to work around */ /* controller problems */ #define BTM_SEC_STATE_DISCONNECTING_BLE 8 /* disconnecting BLE */ #define BTM_SEC_STATE_DISCONNECTING_BOTH 9 /* disconnecting BR/EDR and BLE */
btm_Sec.c
/******************************************************************************* ** ** Function btm_sec_rmt_name_request_complete ** ** Description This function is called when remote name was obtained from ** the peer device ** ** Returns void ** *******************************************************************************/ void btm_sec_rmt_name_request_complete (UINT8 *p_bd_addr, UINT8 *p_bd_name, UINT8 status) { tBTM_SEC_DEV_REC *p_dev_rec; int i; DEV_CLASS dev_class; UINT8 old_sec_state; BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete"); if (((p_bd_addr == NULL) && !BTM_ACL_IS_CONNECTED(btm_cb.connecting_bda)) || ((p_bd_addr != NULL) && !BTM_ACL_IS_CONNECTED(p_bd_addr))) { btm_acl_resubmit_page(); } /* If remote name request failed, p_bd_addr is null and we need to search */ /* based on state assuming that we are doing 1 at a time */ if (p_bd_addr) p_dev_rec = btm_find_dev (p_bd_addr); else { ... } ... if (p_dev_rec) { old_sec_state = p_dev_rec->sec_state; if (status == HCI_SUCCESS) { BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), (char *)p_bd_name, BTM_MAX_REM_BD_NAME_LEN);//這裡是儲存名字的地方,名字從函式的第二個引數中來 p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN;// ++0x8 = 0x88 BTM_TRACE_EVENT ("setting BTM_SEC_NAME_KNOWN sec_flags:0x%x", p_dev_rec->sec_flags); } else { /* Notify all clients waiting for name to be resolved even if it failed so clients can continue */ p_dev_rec->sec_bd_name[0] = 0; } if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME)//這裡remote sec_state 是idle p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; /* Notify all clients waiting for name to be resolved */ for (i = 0;i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { if (btm_cb.p_rmt_name_callback[i] && p_bd_addr) (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); } } else { ... return; } /* If we were delaying asking UI for a PIN because name was not resolved, ask now */ if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_LOCAL_PIN) && p_bd_addr && (memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) ) { ... return; } /* Check if we were delaying bonding because name was not resolved */ if ( btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME)/*這裡check 是否有配對流程需要繼續*/ { if (p_bd_addr && memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) { BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete() continue bonding sm4: 0x%04x, status:0x%x", p_dev_rec->sm4, status); if(btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_CANCEL_DD)// { btm_sec_bond_cancel_complete(); return; } if (status != HCI_SUCCESS) { btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); if (btm_cb.api.p_auth_complete_callback) (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, status); return; } /* if peer is very old legacy devices, HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is not reported */ if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { /* set the KNOWN flag only if BTM_PAIR_FLAGS_REJECTED_CONNECT is not set.*/ /* If it is set, there may be a race condition */ BTM_TRACE_DEBUG ("btm_sec_rmt_name_request_complete IS_SM4_UNKNOWN Flags:0x%04x", btm_cb.pairing_flags); if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) == 0) { p_dev_rec->sm4 |= BTM_SM4_KNOWN; } } BTM_TRACE_DEBUG("%s, SM4 Value: %x, Legacy:%d,IS SM4:%d, Unknown:%d",__FUNCTION__, p_dev_rec->sm4, BTM_SEC_IS_SM4_LEGACY(p_dev_rec->sm4),//sm4 = known這個變數就認為是legacy BTM_SEC_IS_SM4(p_dev_rec->sm4),BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)); /* BT 2.1 or carkit, bring up the connection to force the peer to request PIN. ** Else prefetch (btm_sec_check_prefetch_pin will do the prefetching if needed) */ if ((p_dev_rec->sm4 != BTM_SM4_KNOWN) || !btm_sec_check_prefetch_pin(p_dev_rec)) //一般走這裡的流程,繼續create connection { /* if we rejected incoming connection request, we have to wait HCI_Connection_Complete event */ /* before originating */ if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) { BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete: waiting HCI_Connection_Complete after rejecting connection"); } /* Both we and the peer are 2.1 - continue to create connection */ else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED)//建立dd connection { BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete: failed to start connection"); btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); if (btm_cb.api.p_auth_complete_callback) (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL); } } return; } ....
下面我們 在簡單看下 btm_sec_dd_create_conn 的實現:
btm_Sec.c
/******************************************************************************* ** ** Function btm_sec_dd_create_conn ** ** Description This function is called to create the ACL connection for ** the dedicated boding process ** ** Returns void ** *******************************************************************************/ static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec) { tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR); if (p_lcb && (p_lcb->link_state == LST_CONNECTED || p_lcb->link_state == LST_CONNECTING))/*Connection already exists */ { ... } /* Make sure an L2cap link control block is available */ if (!p_lcb && (p_lcb = l2cu_allocate_lcb (p_dev_rec->bd_addr, TRUE, BT_TRANSPORT_BR_EDR)) == NULL) { ... return(BTM_NO_RESOURCES); } /* set up the control block to indicated dedicated bonding */ btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE;//0x01 | 0x04 = 0x05 if (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE) { BTM_TRACE_WARNING ("Security Manager: failed create [%02x%02x%02x%02x%02x%02x]", p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); l2cu_release_lcb(p_lcb); return(BTM_NO_RESOURCES); } btm_acl_update_busy_level (BTM_BLI_PAGE_EVT);//update acl BTM_TRACE_DEBUG ("Security Manager: btm_sec_dd_create_conn [%02x%02x%02x%02x%02x%02x]", p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ);//更新 btm_cb.pairing_state = BTM_PAIR_STATE_WAIT_PIN_REQ return(BTM_CMD_STARTED); }
接下來,當controller完成了物理link的建立,配對流程繼續:
btu_hcif.c
/******************************************************************************* ** ** Function btu_hcif_connection_comp_evt ** ** Description Process event HCI_CONNECTION_COMP_EVT ** ** Returns void ** *******************************************************************************/ static void btu_hcif_connection_comp_evt (UINT8 *p) { UINT8 status; UINT16 handle; BD_ADDR bda; UINT8 link_type; UINT8 enc_mode; STREAM_TO_UINT8 (status, p); STREAM_TO_UINT16 (handle, p); STREAM_TO_BDADDR (bda, p); STREAM_TO_UINT8 (link_type, p); STREAM_TO_UINT8 (enc_mode, p); handle = HCID_GET_HANDLE (handle); if (link_type == HCI_LINK_TYPE_ACL) { btm_sec_connected (bda, handle, status, enc_mode); l2c_link_hci_conn_comp (status, handle, bda); }... }
這裡還是兩個函式,我們依次看一下:
首先分析一下btm_sec_connected
/******************************************************************************* ** ** Function btm_sec_connected ** ** Description This function is when a connection to the peer device is ** establsihed ** ** Returns void ** *******************************************************************************/ void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode) { tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); UINT8 res; BOOLEAN is_pairing_device = FALSE; tACL_CONN *p_acl_cb; UINT8 bit_shift = 0; btm_acl_resubmit_page(); if (!p_dev_rec) { ... } else /* Update the timestamp for this device */ { bit_shift = (handle == p_dev_rec->ble_hci_handle) ? 8 :0; p_dev_rec->timestamp = btm_cb.dev_rec_count++; if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) { /* tell L2CAP it's a bonding connection. */ if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) ){ ... } /* always clear the pending flag */ p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND; } } #if BLE_INCLUDED == TRUE p_dev_rec->device_type |= BT_DEVICE_TYPE_BREDR; #endif p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) && (memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN) == 0) ) { /* if we rejected incoming connection from bonding device */ if ((status == HCI_ERR_HOST_REJECT_DEVICE) &&(btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT)) { ... return; } /* wait for incoming connection without resetting pairing state */ else if (status == HCI_ERR_CONNECTION_EXISTS) { BTM_TRACE_WARNING ("Security Manager: btm_sec_connected: Wait for incoming connection"); return; } is_pairing_device = TRUE; } /* If connection was made to do bonding restore link security if changed */ btm_restore_mode(); /* if connection fails during pin request, notify application */ if (status != HCI_SUCCESS) { ... return; } /* If initiated dedicated bonding, return the link key now, and initiate disconnect */ /* If dedicated bonding, and we now have a link key, we are all done */ if ( is_pairing_device && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) ) { ... btm_send_link_key_notif(p_dev_rec); ... p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; ... if (btm_cb.api.p_auth_complete_callback) (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, HCI_SUCCESS); btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); ... return; } p_dev_rec->hci_handle = handle; /* role may not be correct here, it will be updated by l2cap, but we need to */ /* notify btm_acl that link is up, so starting of rmt name request will not */ /* set paging flag up */ p_acl_cb = btm_bda_to_acl(bda, BT_TRANSPORT_BR_EDR); if (p_acl_cb) { /* whatever is in btm_establish_continue() without reporting the BTM_BL_CONN_EVT event */ #if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) /* For now there are a some devices that do not like sending */ /* commands events and data at the same time. */ /* Set the packet types to the default allowed by the device */ btm_set_packet_types (p_acl_cb, btm_cb.btm_acl_pkt_types_supported); if (btm_cb.btm_def_link_policy) BTM_SetLinkPolicy (p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); #endif } btm_acl_created (bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, handle, HCI_ROLE_SLAVE, BT_TRANSPORT_BR_EDR); /* Initialize security flags. We need to do that because some */ /* authorization complete could have come after the connection is dropped */ /* and that would set wrong flag that link has been authorized already */ p_dev_rec->sec_flags &= ~((BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED) << bit_shift);//first clear it if (enc_mode != HCI_ENCRYPT_MODE_DISABLED) p_dev_rec->sec_flags |= ((BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED) << bit_shift); if (btm_cb.security_mode == BTM_SEC_MODE_LINK) p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED << bit_shift); ... p_dev_rec->link_key_changed = FALSE; /* After connection is established we perform security if we do not know */ /* the name, or if we are originator because some procedure can have */ /* been scheduled while connection was down */ BTM_TRACE_DEBUG ("is_originator:%d ", p_dev_rec->is_originator); if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) || p_dev_rec->is_originator) { if ((res = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED)//這裡報包含 Start get name,Start authentication,Start encryption,一直到最後完成access granted btm_sec_dev_rec_cback_event (p_dev_rec, res, FALSE); } return; }
簡單說一下上面函式的要點:
- Allocate acl_db entry :p_acl_cb = btm_bda_to_acl(bda, BT_TRANSPORT_BR_EDR);
- btsnd_hcic_read_rmt_clk_offset (p->hci_handle);//Command: HCI_Read_Clock_Offset
- btsnd_hcic_rmt_ver_req (p->hci_handle);//Command: HCI_Read_Remote_Version_Information
- btm_read_remote_features (p->hci_handle);//Command: HCI_Read_Remote_Supported_Features
- btm_sec_execute_procedure (p_dev_rec)
- RNR 已經做過.
- Start authentication 即將要做
- Start encryption 後續要做
- Start authorization 可能要做
這裡貼一下 btm_bda_to_acl 的程式碼,就不分析了.
/******************************************************************************* ** ** Function btm_acl_created ** ** Description This function is called by L2CAP when an ACL connection ** is created. ** ** Returns void ** *******************************************************************************/ void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, BD_NAME bdn, UINT16 hci_handle, UINT8 link_role, tBT_TRANSPORT transport) { tBTM_SEC_DEV_REC *p_dev_rec = NULL; tACL_CONN *p; UINT8 xx; BTM_TRACE_DEBUG ("btm_acl_created hci_handle=%d link_role=%d transport=%d", hci_handle,link_role, transport); /* Ensure we don't have duplicates */ p = btm_bda_to_acl(bda, transport); //find btm_cb.acl_db ,if found then return . if (p != (tACL_CONN *)NULL) { p->hci_handle = hci_handle; p->link_role = link_role; #if BLE_INCLUDED == TRUE p->transport = transport; #endif BTM_TRACE_DEBUG ("Duplicate btm_acl_created: RemBdAddr: %02x%02x%02x%02x%02x%02x", bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); BTM_SetLinkPolicy(p->remote_addr, &btm_cb.btm_def_link_policy); return; } /* Allocate acl_db entry */ for (xx = 0, p = &btm_cb.acl_db[0]; xx < MAX_L2CAP_LINKS; xx++, p++) { if (!p->in_use) { p->in_use = TRUE; p->hci_handle = hci_handle; p->link_role = link_role; p->link_up_issued = FALSE; memcpy (p->remote_addr, bda, BD_ADDR_LEN); ... /* if BR/EDR do something more */ if (transport == BT_TRANSPORT_BR_EDR) { btsnd_hcic_read_rmt_clk_offset (p->hci_handle);//Command: HCI_Read_Clock_Offset } btsnd_hcic_rmt_ver_req (p->hci_handle);//Command: HCI_Read_Remote_Version_Information p_dev_rec = btm_find_dev_by_handle (hci_handle); ... #if (BLE_INCLUDED == TRUE) /* If here, features are not known yet */ if (p_dev_rec && transport == BT_TRANSPORT_LE) { ... } else #endif { BTM_TRACE_API("%s: begin to btm_read_remote_features", __FUNCTION__); btm_read_remote_features (p->hci_handle);//Command: HCI_Read_Remote_Supported_Features } /* read page 1 - on rmt feature event for buffer reasons */ return; } } }
下面簡單分析一下btm_sec_execute_procedure 的流程:
btm_Sec.c
/****************************************************************** ** S T A T I C F U N C T I O N S *******************************************************************/ /******************************************************************************* ** ** Function btm_sec_execute_procedure ** ** Description This function is called to start required security ** procedure. There is a case when multiplexing protocol ** calls this function on the originating side, connection to **