蓝牙inquiry流程之Advertising Report
setting 界面开始搜索的时候,通常也会同时进行le scan,这一点在inquiry流程之命令下发中已经讲述。此篇文章主要是分析一下对于controller 搜索到的广播包的处理。这里以Android O的bluedroid的代码作为分析对象。
void btu_hci_msg_process(BT_HDR* p_msg) {
/* Determine the input message type. */
switch (p_msg->event & BT_EVT_MASK) {
case BT_EVT_TO_BTU_HCI_ACL:
/* All Acl Data goes to L2CAP */
l2c_rcv_acl_data(p_msg);
break; case BT_EVT_TO_BTU_L2C_SEG_XMIT:
/* L2CAP segment transmit complete */
l2c_link_segments_xmitted(p_msg);
break; case BT_EVT_TO_BTU_HCI_SCO:
#if (BTM_SCO_INCLUDED == TRUE)
btm_route_sco_data(p_msg);
break;
#endif case BT_EVT_TO_BTU_HCI_EVT:
btu_hcif_process_event((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
osi_free(p_msg);
break; case BT_EVT_TO_BTU_HCI_CMD:
btu_hcif_send_cmd((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
break; default:
osi_free(p_msg);
break;
}
}
上面可以看出 btu_hci_msg_process 的所有的处理对象。hci event 的处理函数 是btu_hcif_process_event((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
void btu_hcif_process_event(UNUSED_ATTR uint8_t controller_id, BT_HDR* p_msg) {
uint8_t* p = (uint8_t*)(p_msg + ) + p_msg->offset;
uint8_t hci_evt_code, hci_evt_len;
uint8_t ble_sub_code;
STREAM_TO_UINT8(hci_evt_code, p);
STREAM_TO_UINT8(hci_evt_len, p); switch (hci_evt_code) {
case HCI_INQUIRY_COMP_EVT:
btu_hcif_inquiry_comp_evt(p);
break;
case HCI_INQUIRY_RESULT_EVT:
btu_hcif_inquiry_result_evt(p);
break;
case HCI_INQUIRY_RSSI_RESULT_EVT:
btu_hcif_inquiry_rssi_result_evt(p);
break;
...
case HCI_BLE_EVENT: { //le 相关的event
STREAM_TO_UINT8(ble_sub_code, p); uint8_t ble_evt_len = hci_evt_len - ;
switch (ble_sub_code) { //判断子event
case HCI_BLE_ADV_PKT_RPT_EVT: /* result of inquiry */
HCI_TRACE_EVENT("HCI_BLE_ADV_PKT_RPT_EVT");
btm_ble_process_adv_pkt(ble_evt_len, p);//处理广播包
break;
case HCI_BLE_CONN_COMPLETE_EVT:
btu_ble_ll_conn_complete_evt(p, hci_evt_len);
break;
case HCI_BLE_LL_CONN_PARAM_UPD_EVT:
btu_ble_ll_conn_param_upd_evt(p, hci_evt_len);
break;
...
从上面可以看出 btu_hcif_process_event可以处理的event 的类型,并且可以看出处理广播包的函数 是 btm_ble_process_adv_pkt(ble_evt_len, p);从函数名称可以看出此时函数已经进入到btm 模块,其实现在btm_ble_gap.cc
/**
* This function is called when advertising report event is received. It updates
* the inquiry database. If the inquiry database is full, the oldest entry is
* discarded.
*/
void btm_ble_process_adv_pkt(uint8_t data_len, uint8_t* data) {
BD_ADDR bda;
uint8_t* p = data;
uint8_t legacy_evt_type, addr_type, num_reports, pkt_data_len;
int8_t rssi; /* Extract the number of reports in this event. */
STREAM_TO_UINT8(num_reports, p);//一个包里面可能有多个event,但是通常只有一个event while (num_reports--) { /* Extract inquiry results */
STREAM_TO_UINT8(legacy_evt_type, p);//event_type
STREAM_TO_UINT8(addr_type, p);//地址类型
STREAM_TO_BDADDR(bda, p);//地址
STREAM_TO_UINT8(pkt_data_len, p);//数据长度 uint8_t* pkt_data = p;
p += pkt_data_len; /* Advance to the the rssi byte */ STREAM_TO_INT8(rssi, p);//此时指针指向数据末尾的rssi btm_ble_process_adv_addr(bda, addr_type);//处理地址相关 uint16_t event_type;
if (legacy_evt_type == 0x00) { // ADV_IND;
event_type = 0x0013;
} else if (legacy_evt_type == 0x01) { // ADV_DIRECT_IND;
event_type = 0x0015;
} else if (legacy_evt_type == 0x02) { // ADV_SCAN_IND;
event_type = 0x0012;
} else if (legacy_evt_type == 0x03) { // ADV_NONCONN_IND;
event_type = 0x0010;
} else if (legacy_evt_type == 0x04) { // SCAN_RSP;
// We can't distinguish between "SCAN_RSP to an ADV_IND", and "SCAN_RSP to
// an ADV_SCAN_IND", so always return "SCAN_RSP to an ADV_IND"
event_type = 0x001B;
}
btm_ble_process_adv_pkt_cont(
event_type, addr_type, bda, PHY_LE_1M, PHY_LE_NO_PACKET, NO_ADI_PRESENT,
TX_POWER_NOT_PRESENT, rssi, 0x00 /* no periodic adv */, pkt_data_len,
pkt_data);//开始处理数据包
}
}
可以看出主要的 主要的处理流程是执行btm_ble_process_adv_pkt_cont,我们看看具体的实现:
/*******************************************************************************
**
** Function btm_ble_process_adv_pkt_cont
**
** Description This function is called after random address resolution is
** done, and proceed to process adv packet.
**
** Parameters
**
** Returns void
**
*******************************************************************************/
static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt_type, UINT8 *p)
{
tINQ_DB_ENT *p_i;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb;
tBTM_INQ_RESULTS_CB *p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb;
tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var;
BOOLEAN update = TRUE;
UINT8 result = ; p_i = btm_inq_db_find (bda);
/* Check if this address has already been processed for this inquiry */
if (btm_inq_find_bdaddr(bda))//check 这个地址的设备是否已经被处理过
{
/* never been report as an LE device */
if (p_i &&
(!(p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BLE) ||
/* scan repsonse to be updated */
(!p_i->scan_rsp)))//这里update的判断条件是device type是否已经定位以及是否有scan response
{
update = TRUE;
}
else if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity))
{
update = FALSE;
}
else
{
/* if yes, skip it */
return; /* assumption: one result per event */
}
}
/* If existing entry, use that, else get a new one (possibly reusing the oldest) */
if (p_i == NULL)//没有entry,重新分配
{
if ((p_i = btm_inq_db_new (bda)) != NULL)
{
p_inq->inq_cmpl_info.num_resp++;
}
else
return;
}
else if (p_i->inq_count != p_inq->inq_counter) /* first time seen in this inquiry */ /*在这一次的inquiry 中第一次见到该设备*/
{
p_inq->inq_cmpl_info.num_resp++;
}
/* update the LE device information in inquiry database */
if (!btm_ble_update_inq_result(p_i, addr_type, evt_type, p))//更新数据库
return; if ((result = btm_ble_is_discoverable(bda, evt_type, p)) == )//检测adv包中标志位
{
LOG_WARN("%s device is no longer discoverable so discarding advertising packet pkt",
__func__);
return;
}
if (!update)
result &= ~BTM_BLE_INQ_RESULT;
...
/* background connection in selective connection mode */
BTM_TRACE_WARNING("btm_cb.ble_ctr_cb.bg_conn_type = %d libs_liu",btm_cb.ble_ctr_cb.bg_conn_type);
if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE)
{
if (result & BTM_BLE_SEL_CONN_RESULT)
btm_send_sel_conn_callback(bda, evt_type, p, addr_type);
else
{
BTM_TRACE_DEBUG("None LE device, can not initiate selective connection");
}
}
else
{
if (p_inq_results_cb && (result & BTM_BLE_INQ_RESULT))
{
(p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache);//处理结果
}
if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT))
{
(p_obs_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache);
}
}
}
从上面处理流程来看,主要是做了四件事:
- 查询设备数据库,并且判断该广播信息已经被处理过。
- btm_ble_update_inq_result(p_i, addr_type, evt_type, p) 更新设备数据库中的设备信息
- btm_ble_is_discoverable(bda, evt_type, p) 判断设备信息标志位,判断是否是可以发现的类型。
- 调用p_inq_results_cb 来处理 设备信息。这个在BTM_StartInquiry的时候传入参数bta_dm_inq_results_cb,调用的也就是这个回调函数。
下面主要分析一下2,3,4三个点:
2.btm_ble_update_inq_result(p_i, addr_type, evt_type, p)
/*******************************************************************************
**
** Function btm_ble_update_inq_result
**
** Description Update adv packet information into inquiry result.
**
** Parameters
**
** Returns void
**
*******************************************************************************/
BOOLEAN btm_ble_update_inq_result(tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p)
{
BOOLEAN to_report = TRUE;
tBTM_INQ_RESULTS *p_cur = &p_i->inq_info.results;
UINT8 len;
UINT8 *p_flag;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
UINT8 data_len, rssi;
tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var;
UINT8 *p1;
UINT8 *p_uuid16; STREAM_TO_UINT8 (data_len, p); btm_ble_cache_adv_data(p_cur, data_len, p, evt_type);// cache adv data p1 = (p + data_len);
STREAM_TO_UINT8 (rssi, p1); /* Save the info */
p_cur->inq_result_type = BTM_INQ_RESULT_BLE;
p_cur->ble_addr_type = addr_type;
p_cur->rssi = rssi;
/* active scan, always wait until get scan_rsp to report the result */
if ((btm_cb.ble_ctr_cb.inq_var.scan_type == BTM_BLE_SCAN_MODE_ACTI && //如果是active scan,那么要等scan response 上来之后才会一起report,一个单独的BTM_BLE_CONNECT_EVT或者BTM_BLE_DISCOVER_EVT是不会上报的
(evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_DISCOVER_EVT)))
{
BTM_TRACE_DEBUG("btm_ble_update_inq_result scan_rsp=false, to_report=false,\
scan_type_active=%d", btm_cb.ble_ctr_cb.inq_var.scan_type);
p_i->scan_rsp = FALSE;
to_report = FALSE;
}
else
p_i->scan_rsp = TRUE;//拿到scan response if (p_i->inq_count != p_inq->inq_counter)
p_cur->device_type = BT_DEVICE_TYPE_BLE;//这次inquiry的第一次处理
else
p_cur->device_type |= BT_DEVICE_TYPE_BLE;//if(p_i->inq_count == p_inq->inq_counter) indicated that has been updated ,至少是BLE if (evt_type != BTM_BLE_SCAN_RSP_EVT)
p_cur->ble_evt_type = evt_type; p_i->inq_count = p_inq->inq_counter; /* Mark entry for current inquiry */ if (p_le_inq_cb->adv_len != )
{
if ((p_flag = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_FLAG, &len)) != NULL)//读取flag
p_cur->flag = * p_flag;
} if (p_le_inq_cb->adv_len != )
{
/* Check to see the BLE device has the Appearance UUID in the advertising data. If it does
* then try to convert the appearance value to a class of device value Bluedroid can use.
* Otherwise fall back to trying to infer if it is a HID device based on the service class.
*/
p_uuid16 = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_APPEARANCE, &len);
if (p_uuid16 && len == )
{
btm_ble_appearance_to_cod((UINT16)p_uuid16[] | (p_uuid16[] << ), p_cur->dev_class);
}
else
{
if ((p_uuid16 = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache,
BTM_BLE_AD_TYPE_16SRV_CMPL, &len)) != NULL)
{
UINT8 i;
for (i = ; i + <= len; i = i + )
{
/* if this BLE device support HID over LE, set HID Major in class of device */
if ((p_uuid16[i] | (p_uuid16[i+] << )) == UUID_SERVCLASS_LE_HID)
{
p_cur->dev_class[] = ;
p_cur->dev_class[] = BTM_COD_MAJOR_PERIPHERAL;
p_cur->dev_class[] = ;
break;
}
}
}
}
} /* if BR/EDR not supported is not set, assume is a DUMO device */
if ((p_cur->flag & BTM_BLE_BREDR_NOT_SPT) == &&
evt_type != BTM_BLE_CONNECT_DIR_EVT)
{
if (p_cur->ble_addr_type != BLE_ADDR_RANDOM)
{
BTM_TRACE_DEBUG("BR/EDR NOT support bit not set, treat as DUMO");
p_cur->device_type |= BT_DEVICE_TYPE_DUMO;
} else {
BTM_TRACE_DEBUG("Random address, treating device as LE only");
}
}
else
{
BTM_TRACE_DEBUG("BR/EDR NOT SUPPORT bit set, LE only device");
}
return to_report;
}
这里主要就是更新设备的信息。读取adv 包中的 flag等值,判断这个包是否要上报,以及更新device type。另外会根据BTM_BLE_AD_TYPE_APPEARANCE = 0x19 来判断device class-->btm_ble_appearance_to_cod
3.btm_ble_is_discoverable(bda, evt_type, p)
/*******************************************************************************
**
** Function btm_ble_is_discoverable
**
** Description check ADV flag to make sure device is discoverable and match
** the search condition
**
** Parameters
**
** Returns void
**
*******************************************************************************/
UINT8 btm_ble_is_discoverable(BD_ADDR bda, UINT8 evt_type, UINT8 *p)
{
UINT8 *p_flag, flag = , rt = ;
UINT8 data_len;
tBTM_INQ_PARMS *p_cond = &btm_cb.btm_inq_vars.inqparms;
tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; /* for observer, always "discoverable */
if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity))//这里注意,如果是observation,那么总是可以发现的
rt |= BTM_BLE_OBS_RESULT; if (BTM_BLE_IS_SEL_CONN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity) &&
(evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_CONNECT_DIR_EVT))
rt |= BTM_BLE_SEL_CONN_RESULT; if (p_le_inq_cb->adv_len != )
{
if ((p_flag = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache,
BTM_BLE_AD_TYPE_FLAG, &data_len)) != NULL)//读取标志位并且判断
{
flag = * p_flag; if ((btm_cb.btm_inq_vars.inq_active & BTM_BLE_GENERAL_INQUIRY) &&
(flag & (BTM_BLE_LIMIT_DISC_FLAG|BTM_BLE_GEN_DISC_FLAG)) != )
{
BTM_TRACE_DEBUG("Find Generable Discoverable device");
rt |= BTM_BLE_INQ_RESULT;
} else if (btm_cb.btm_inq_vars.inq_active & BTM_BLE_LIMITED_INQUIRY &&
(flag & BTM_BLE_LIMIT_DISC_FLAG) != )
{
BTM_TRACE_DEBUG("Find limited discoverable device");
rt |= BTM_BLE_INQ_RESULT;
}
}
}
return rt;
}
上面代码主要作用就是解析adv包中的标志位,然后做解析。并且决定是否要继续处理这个包,如果rt = 0 ,那么就不会继续处理这个包。这里注意如果是observation ,那么这个包总是可以发现的。
4.bta_dm_inq_results_cb
下面来分析这个 最重要的函数:
/*******************************************************************************
**
** Function bta_dm_inq_results_cb
**
** Description Inquiry results callback from BTM
**
** Returns void
**
*******************************************************************************/
static void bta_dm_inq_results_cb (tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir)
{ tBTA_DM_SEARCH result;//使用这个结果上报结果
tBTM_INQ_INFO *p_inq_info;
UINT16 service_class; bdcpy(result.inq_res.bd_addr, p_inq->remote_bd_addr);//填充address
memcpy(result.inq_res.dev_class, p_inq->dev_class, DEV_CLASS_LEN);//填充device class
BTM_COD_SERVICE_CLASS(service_class, p_inq->dev_class);
result.inq_res.is_limited = (service_class & BTM_COD_SERVICE_LMTD_DISCOVER)?TRUE:FALSE;
result.inq_res.rssi = p_inq->rssi;//rssi #if (BLE_INCLUDED == TRUE)
result.inq_res.ble_addr_type = p_inq->ble_addr_type;
result.inq_res.inq_result_type = p_inq->inq_result_type;
result.inq_res.device_type = p_inq->device_type;
result.inq_res.flag = p_inq->flag;//填充flag
#endif /* application will parse EIR to find out remote device name */
result.inq_res.p_eir = p_eir; if((p_inq_info = BTM_InqDbRead(p_inq->remote_bd_addr)) != NULL)
{
/* initialize remt_name_not_required to FALSE so that we get the name by default */
result.inq_res.remt_name_not_required = FALSE;
} if(bta_dm_search_cb.p_search_cback)//回调
bta_dm_search_cb.p_search_cback(BTA_DM_INQ_RES_EVT, &result); if(p_inq_info)
{
/* application indicates if it knows the remote name, inside the callback
copy that to the inquiry data base*/
if(result.inq_res.remt_name_not_required)
p_inq_info->appl_knows_rem_name = TRUE;//如果application已经知道名字 } }
这里的逻辑也比较简单,构造了一个tBTA_DM_SEARCH来上报事件,上报的事件的函数就是bta_dm_search_cb.p_search_cback = bte_search_devices_evt,我们来看看其具体的实现:
/*******************************************************************************
**
** Function bte_search_devices_evt
**
** Description Switches context from BTE to BTIF for DM search events
**
** Returns void
**
*******************************************************************************/
static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
{
UINT16 param_len = ;
if (p_data)
param_len += sizeof(tBTA_DM_SEARCH);
/* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
switch (event)
{
case BTA_DM_INQ_RES_EVT:
{
if (p_data->inq_res.p_eir)
param_len += HCI_EXT_INQ_RESPONSE_LEN;
}
break;
...
/* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */
if (event == BTA_DM_INQ_RES_EVT){
p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);//这里check data 里面是否有名字,如果有的话,设置result 的标志位,协议栈就不会去查询名字
}
btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len,
(param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
}
这里主要就是check 名字。然后transfer 到btif 线程去执行,我们继续看:
/******************************************************************************
**
** Function btif_dm_search_devices_evt
**
** Description Executes search devices callback events in btif context
**
** Returns void
**
******************************************************************************/
static void btif_dm_search_devices_evt (UINT16 event, char *p_param)
{
tBTA_DM_SEARCH *p_search_data;
BTIF_TRACE_EVENT("%s event=%s", __FUNCTION__, dump_dm_search_event(event)); switch (event)
{
...
case BTA_DM_INQ_RES_EVT:
{
/* inquiry result */
UINT32 cod;
bt_bdname_t bdname;
bt_bdaddr_t bdaddr;
UINT8 remote_name_len;
tBTA_SERVICE_MASK services = ;
bdstr_t bdstr; p_search_data = (tBTA_DM_SEARCH *)p_param;//获得数据
bdcpy(bdaddr.address, p_search_data->inq_res.bd_addr);//获得地址 bdname.name[] = ; cod = devclass2uint (p_search_data->inq_res.dev_class);//cod if (!check_eir_remote_name(p_search_data, bdname.name, &remote_name_len))
check_cached_remote_name(p_search_data, bdname.name, &remote_name_len);//如果data里面没有名字,那么就查找cache里面的名字 /* Check EIR for remote name and services */
if (p_search_data->inq_res.p_eir)
{
BTA_GetEirService(p_search_data->inq_res.p_eir, &services);
BTIF_TRACE_DEBUG("%s()EIR BTA services = %08X", __FUNCTION__, (UINT32)services);
/* TODO: Get the service list and check to see which uuids we got and send it back to the client. */
} {
bt_property_t properties[];
bt_device_type_t dev_type;
uint32_t num_properties = ;
bt_status_t status;
int addr_type = ; memset(properties, , sizeof(properties));
/* BD_ADDR */
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_BDADDR, sizeof(bdaddr), &bdaddr);//保存地址
num_properties++;
/* BD_NAME */
/* Don't send BDNAME if it is empty */
if (bdname.name[])
{
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_BDNAME,
strlen((char *)bdname.name), &bdname);//保存名字
num_properties++;
} /* DEV_CLASS */
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod), &cod);//保存cod
num_properties++;
/* DEV_TYPE */
#if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
/* FixMe: Assumption is that bluetooth.h and BTE enums match */ /* Verify if the device is dual mode in NVRAM */
int stored_device_type = ;
if (btif_get_device_type(bdaddr.address, &stored_device_type) &&
((stored_device_type == BT_DEVICE_TYPE_BLE &&
p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BREDR) ||
(stored_device_type == BT_DEVICE_TYPE_BREDR &&
p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE))) {
dev_type = BT_DEVICE_TYPE_DUMO;
} else {
dev_type = p_search_data->inq_res.device_type;
} if (p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE)
addr_type = p_search_data->inq_res.ble_addr_type;
#else
dev_type = BT_DEVICE_TYPE_BREDR;
#endif
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_TYPE_OF_DEVICE, sizeof(dev_type), &dev_type);//保存dev type
num_properties++;
/* RSSI */
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_REMOTE_RSSI, sizeof(int8_t),
&(p_search_data->inq_res.rssi));//保存rssi
num_properties++; status = btif_storage_add_remote_device(&bdaddr, num_properties, properties);//将各个属性保存在文件系统中 #if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
status = btif_storage_set_remote_addr_type(&bdaddr, addr_type);
ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote addr type (inquiry)", status);
#endif
/* Callback to notify upper layer of device */
HAL_CBACK(bt_hal_cbacks, device_found_cb,
num_properties, properties);//向上汇报
}
}
break;
...
这里注意btif_storage_add_remote_device 是将各个属性保存在系统的配置文件中。然后 通过HAL_CBACK(bt_hal_cbacks, device_found_cb,num_properties, properties); 来上面五个属性:设备地址、设备名字、设备类、设备类型、设备rssi
协议栈对于ble 设备广播包的处理就分析到这里。
蓝牙inquiry流程之Advertising Report的更多相关文章
- 蓝牙inquiry流程之HCI_Inquiry_Result_With_RSSI和HCI Extended Inquiry Result处理
首先介绍一下和inquiry的相关的流程. inquiry是从协议栈下发的一个HCI命令.其格式如下: 这里简单介绍下第二个参数,是inquiry的持续时间, 从上图看出 inquiry持续的时间是 ...
- 蓝牙inquiry流程之Inquiry Complete处理
inquiry流程一般持续有12s多,当inquiry完成的时候,设备端会上报一个Event: Inquiry Complete 上来,那协议栈是如何把这个事件上传到应用层的呢?本篇文章来分析一下其具 ...
- activiti自定义流程之Spring整合activiti-modeler5.16实例(九):历史任务查询
注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建 (2)创建流程模型:activiti自定义流程之Spring ...
- activiti自定义流程之Spring整合activiti-modeler5.16实例(八):完成个人任务
注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建 (2)创建流程模型:activiti自定义流程之Spring ...
- activiti自定义流程之Spring整合activiti-modeler5.16实例(七):任务列表展示
注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建 (2)创建流程模型:activiti自定义流程之Spring ...
- activiti自定义流程之Spring整合activiti-modeler5.16实例(六):启动流程
注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建 (2)创建流程模型:activiti自定义流程之Spring ...
- activiti自定义流程之Spring整合activiti-modeler5.16实例(五):流程定义列表
注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建 (2)创建流程模型:activiti自定义流程之Spring ...
- activiti自定义流程之Spring整合activiti-modeler5.16实例(四):部署流程定义
注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建 (2)创建流程模型:activiti自定义流程之Spring ...
- activiti自定义流程之Spring整合activiti-modeler5.16实例(三):流程模型列表展示
注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建 (2)创建流程模型:activiti自定义流程之Spring ...
随机推荐
- CSS 实例之滚动的图片栏
在一些网站上可以经常看到有一些图片进行持续不断的滚动,这个效果可以通过css的动画效果来实现.具体效果如下 主要原理是通过动画向左移动. 首先给出两组一样的图片(同一行上),让整体图片向左移动一组图片 ...
- 白盒测试实践-day....
时间:2017.12.22 地点:软件学院 参与人员:张双双.张玉.周静.赵天宇 会议内容:对代码测试的内容进行总结,总结任务由赵天宇完成,其余同学提出自己的看法和意见. 总结内容分为了五个阶段,不同 ...
- 《图解HTTP》
第一章.了解web及网络基础 1.2 http的诞生 HTTP于1990年问世,那时候HTTP并没有作为正式的标准被建立,被称为HTTP/0.9 HTTP正式作为标准被公布是在1996年5月,版本被命 ...
- 常用内置方法之:__str__,__repr__
class Test(object): def __init__(self): pass def __str__(self): return "test" test = Test( ...
- PHP剔除删除掉危险字符
本文出至:新太潮流网络博客 /** * [剔除掉危险字符] * @E-mial wuliqiang_aa@163.com * @TIME 2017-04-07 * @WEB http://blog.i ...
- Prometheus Node_exporter 之 Disk Datail /proc/diskstats
Disk Datail /proc/diskstats 1. Disk IOps Completed type: GraphUnit: I/O ops/sec(iops)Label: IO read( ...
- [CENTOS7] 将域群组加入到Sudoer里
文章来源:https://derflounder.wordpress.com/2012/12/14/adding-ad-domain-groups-to-etcsudoers/ Adding AD d ...
- 【Redis数据库】再有人问你CAP理论是什么,就把这篇文章发给他
CAP是Consistency(一致性),Availability(可用性),Partition tolerance(分区容错性)的缩写.在学习redis过程中看到这个名词,查找各位大佬的文章发现这篇 ...
- OpenGL超级宝典笔记——贝塞尔曲线和曲面(转)
http://my.oschina.net/sweetdark/blog/183721 参数方程表现形式 在中学的时候,我们都学习过直线的参数方程:y = kx + b;其中k表示斜率,b表示截距(即 ...
- Docker容器学习与分享09
Docker容器之间的相互通信 先新建两个不同的网段,就用分享08里的两个网段作为新建的网段. [root@promote ~]# docker network ls NETWORK ID NAME ...