蓝牙inquiry流程之命令下发
Android 上面的藍牙inquiry 是在設置界面,打開藍牙就會自動搜索周邊的藍牙設備,其最終調用到協議棧的start_discovery接口,此篇文章分析該接口的調用流程以及與controller交互過程。
static int start_discovery(void) {/* sanity check */if (interface_ready() == FALSE)return BT_STATUS_NOT_READY;return btif_dm_start_discovery(); }?
bt_status_t btif_dm_start_discovery(void) {tBTA_DM_INQ inq_params;tBTA_SERVICE_MASK services = 0;tBTA_DM_BLE_PF_FILT_PARAMS adv_filt_param;#if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))memset(&adv_filt_param, 0, sizeof(tBTA_DM_BLE_PF_FILT_PARAMS));/* Cleanup anything remaining on index 0 */BTA_DmBleScanFilterSetup(BTA_DM_BLE_SCAN_COND_DELETE, 0, &adv_filt_param, NULL,bte_scan_filt_param_cfg_evt, 0);/* Add an allow-all filter on index 0*/adv_filt_param.dely_mode = IMMEDIATE_DELY_MODE;adv_filt_param.feat_seln = ALLOW_ALL_FILTER;adv_filt_param.filt_logic_type = BTA_DM_BLE_PF_FILT_LOGIC_OR;adv_filt_param.list_logic_type = BTA_DM_BLE_PF_LIST_LOGIC_OR;adv_filt_param.rssi_low_thres = LOWEST_RSSI_VALUE;adv_filt_param.rssi_high_thres = LOWEST_RSSI_VALUE;BTA_DmBleScanFilterSetup(BTA_DM_BLE_SCAN_COND_ADD, 0, &adv_filt_param, NULL,bte_scan_filt_param_cfg_evt, 0);/* TODO: Do we need to handle multiple inquiries at the same time? *//* Set inquiry params and call API */inq_params.mode = BTA_DM_GENERAL_INQUIRY|BTA_BLE_GENERAL_INQUIRY;//設置inquiry模式,ble和bredr #elseinq_params.mode = BTA_DM_GENERAL_INQUIRY; #endifinq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;//設置超時時間 inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;//設置接收的最大的response數量inq_params.report_dup = TRUE;inq_params.filter_type = BTA_DM_INQ_CLR;//0/* TODO: Filter device by BDA needs to be implemented here *//* Will be enabled to TRUE once inquiry busy level has been received */btif_dm_inquiry_in_progress = FALSE;/* find nearby devices */BTA_DmSearch(&inq_params, services, bte_search_devices_evt);//執行搜索return BT_STATUS_SUCCESS; }?
上面代碼的主要做的事情是,設置相關的搜索的參數,然后調用BTA_DmSearch(&inq_params, services, bte_search_devices_evt); 進行搜索。
我們這里再次整理一下 傳入BTA_DmSearch的參數:
inq_params.mode = BTA_DM_GENERAL_INQUIRY|BTA_BLE_GENERAL_INQUIRY;inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;inq_params.report_dup = TRUE;inq_params.filter_type = BTA_DM_INQ_CLR;//0 tBTA_SERVICE_MASK services = 0;bte_search_devices_evt 是一個回調函數,實現也是在btif_dm.c,最終會被賦值給bta_dm_search_cb.p_search_cback = bte_search_devices_evt?
我們看看BTA_DmSearch具體的實現,
/******************************************************************************* ** ** Function BTA_DmSearch ** ** Description This function searches for peer Bluetooth devices. It performs ** an inquiry and gets the remote name for devices. Service ** discovery is done if services is non zero ** ** ** Returns void ** *******************************************************************************/ void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback) {tBTA_DM_API_SEARCH *p_msg;if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL){memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH));p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));p_msg->services = services;p_msg->p_cback = p_cback;p_msg->rs_res = BTA_DM_RS_NONE;bta_sys_sendmsg(p_msg);}}?
這里發現就是組裝了一個msg,然后發送給btu 線程處理,這里整理一下發送給btu 線程的參數:
p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ)); 上面賦值的參數
p_msg->services = services;//service = 0
p_msg->p_cback = p_cback;//bte_search_devices_evt
p_msg->rs_res = BTA_DM_RS_NONE;
我們查找一下,?BTA_DM_API_SEARCH_EVT 執行的函數:bta_dm_search_start?
/******************************************************************************* ** ** Function bta_dm_search_start ** ** Description Starts an inquiry ** ** ** Returns void ** *******************************************************************************/ void bta_dm_search_start (tBTA_DM_MSG *p_data) {tBTM_INQUIRY_CMPL result;#if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE)UINT16 len = (UINT16)(sizeof(tBT_UUID) * p_data->search.num_uuid);bta_dm_gattc_register(); #endifif (p_bta_dm_cfg->avoid_scatter &&(p_data->search.rs_res == BTA_DM_RS_NONE) && bta_dm_check_av(BTA_DM_API_SEARCH_EVT)){memcpy(&bta_dm_cb.search_msg, &p_data->search, sizeof(tBTA_DM_API_SEARCH));return;}BTM_ClearInqDb(NULL);/* save search params */bta_dm_search_cb.p_search_cback = p_data->search.p_cback;bta_dm_search_cb.services = p_data->search.services;#if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE)utl_freebuf((void **)&bta_dm_search_cb.p_srvc_uuid);if ((bta_dm_search_cb.num_uuid = p_data->search.num_uuid) != 0 &&p_data->search.p_uuid != NULL)//如果在搜索的時候設置了uuid,那么也會將這個值copy給bta_dm_search_cb.p_srvc_uuid {bta_dm_search_cb.p_srvc_uuid = (tBT_UUID *)GKI_getbuf(len)memcpy(bta_dm_search_cb.p_srvc_uuid, p_data->search.p_uuid, len);} #endifresult.status = BTM_StartInquiry( (tBTM_INQ_PARMS*)&p_data->search.inq_params,bta_dm_inq_results_cb,//掃描到設備調用此函數(tBTM_CMPL_CB*) bta_dm_inq_cmpl_cb);//掃描完成會調用到此函數if (result.status != BTM_CMD_STARTED){result.num_resp = 0;bta_dm_inq_cmpl_cb ((void *)&result);//如果開始失敗,直接調用回調函數匯報 } }?
上面的代碼執行完,我們看看bta_dm_search_cb的各個結構的值:
?bta_dm_search_cb.p_search_cback =?bte_search_devices_evt
bta_dm_search_cb.services = 0;
bta_dm_search_cb.num_uuid? =0;
bta_dm_search_cb.p_srvc_uuid = NULL;
下面繼續看看BTM_StartInquiry 的實現,這里可以簡單分析一下,傳入的三個參數:
第一個參數是,上面組裝的inquiry相關的設置。
第二個參數是 掃描到設備調用
第三個參數是 掃描完成 (inquiry complete)的時候調用
/******************************************************************************* ** ** Function BTM_StartInquiry ** ** Description This function is called to start an inquiry. ** ** Parameters: p_inqparms - pointer to the inquiry information ** mode - GENERAL or LIMITED inquiry, BR/LE bit mask seperately ** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) ** max_resps - maximum amount of devices to search for before ending the inquiry ** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or ** BTM_FILTER_COND_BD_ADDR ** filter_cond - value for the filter (based on filter_cond_type) ** ** p_results_cb - Pointer to the callback routine which gets called ** upon receipt of an inquiry result. If this field is ** NULL, the application is not notified. ** ** p_cmpl_cb - Pointer to the callback routine which gets called ** upon completion. If this field is NULL, the ** application is not notified when completed. ** Returns tBTM_STATUS ** BTM_CMD_STARTED if successfully initiated ** BTM_BUSY if already in progress ** BTM_ILLEGAL_VALUE if parameter(s) are out of range ** BTM_NO_RESOURCES if could not allocate resources to start the command ** BTM_WRONG_MODE if the device is not up. ** *******************************************************************************/ tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, tBTM_INQ_RESULTS_CB *p_results_cb,tBTM_CMPL_CB *p_cmpl_cb) {tBTM_STATUS status = BTM_CMD_STARTED;tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;p_inq->scan_type = INQ_GENERAL; .../* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */p_inq->inqparms = *p_inqparms;//保存inquiry的參數/* Initialize the inquiry variables */p_inq->state = BTM_INQ_ACTIVE_STATE;//activep_inq->p_inq_cmpl_cb = p_cmpl_cb;p_inq->p_inq_results_cb = p_results_cb;p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */p_inq->inq_active = p_inqparms->mode;//0x11/* start LE inquiry here if requested */if ((p_inqparms->mode & BTM_BLE_INQUIRY_MASK)){if ((status = btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK),p_inqparms->duration)) != BTM_CMD_STARTED){BTM_TRACE_ERROR("Err Starting LE Inquiry.");p_inq->inqparms.mode &= ~ BTM_BLE_INQUIRY_MASK;} #if (!defined(BTA_HOST_INTERLEAVE_SEARCH) || BTA_HOST_INTERLEAVE_SEARCH == FALSE)p_inqparms->mode &= ~BTM_BLE_INQUIRY_MASK;//開始inquiry之后,mode參數置0 #endif}/* BR/EDR inquiry portion */ .../* Before beginning the inquiry the current filter must be cleared, so initiate the command */if ((status = btm_set_inq_event_filter (p_inqparms->filter_cond_type,&p_inqparms->filter_cond)) != BTM_CMD_STARTED)//這里沒有看到BRRDR的掃描,而只有這個清楚過濾的函數,那我可以想象,肯定是這個函數做完之后會自動開始bredr設備的inquiryp_inq->state = BTM_INQ_INACTIVE_STATE;return (status); }上面的代碼主要做了三件事:
p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */
btm_ble_start_inquiry
tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration) {tBTM_STATUS status = BTM_CMD_STARTED;tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb;tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; ...if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity))//如果沒有scan 正在進行,開始scan {btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_ACTI,//active scan BTM_BLE_LOW_LATENCY_SCAN_INT,BTM_BLE_LOW_LATENCY_SCAN_WIN,btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type,SP_ADV_ALL); #if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE)/* enable IRK list */btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); #endifp_ble_cb->inq_var.scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE;status = btm_ble_start_scan();//開始scan }else if ((p_ble_cb->inq_var.scan_interval != BTM_BLE_LOW_LATENCY_SCAN_INT) ||(p_ble_cb->inq_var.scan_window != BTM_BLE_LOW_LATENCY_SCAN_WIN)) {//如果已經有正在scan的行為,先停掉當前的scan,啟動新的scan btsnd_hcic_ble_set_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE);btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_ACTI,BTM_BLE_LOW_LATENCY_SCAN_INT,BTM_BLE_LOW_LATENCY_SCAN_WIN,btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type,SP_ADV_ALL);btsnd_hcic_ble_set_scan_enable(BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE);}if (status == BTM_CMD_STARTED){p_inq->inq_active |= mode;//置位p_ble_cb->scan_activity |= mode;if (duration != 0){/* start inquiry timer */btu_start_timer (&p_inq->inq_timer_ent, BTU_TTYPE_BLE_INQUIRY, duration);//開始定時器,當定時器時間到的時候會 disable scan }}return status; }?上面的代碼的邏輯非常的簡單,需要留意一下就是?p_inq->inq_active |= mode 和?p_ble_cb->scan_activity |= mode;
現在Ble 的掃描就開始了,持續時間10s = 10*1000ms,定時器超時時間到了之后會取消scan。
從上面我們還可以看出從inquiry 下發的ble scan的優先級還是很高,它會停掉當前的scan的行為。如果此時后臺有observe 行為,也會被停掉。
btm_set_inq_event_filter
static tBTM_STATUS btm_set_inq_event_filter (UINT8 filter_cond_type,tBTM_INQ_FILT_COND *p_filt_cond) { ...btm_cb.btm_inq_vars.inqfilt_active = TRUE;設置了標志位/* Filter the inquiry results for the specified condition type and value */if (btsnd_hcic_set_event_filter(HCI_FILTER_INQUIRY_RESULT, filter_cond_type,p_cond, condition_length))return (BTM_CMD_STARTED);elsereturn (BTM_NO_RESOURCES); }?上面主要是發送了HCI_FILTER_INQUIRY_RESULT ,以及設置了狀態位:btm_cb.btm_inq_vars.inqfilt_active = TRUE
搜索的行為肯定是在這個函數的完成事件中進行的。
static void btu_hcif_hdl_command_complete (UINT16 opcode, UINT8 *p, UINT16 evt_len,void *p_cplt_cback) {switch (opcode){case HCI_INQUIRY_CANCEL:/* Tell inquiry processing that we are done */btm_process_cancel_complete(HCI_SUCCESS, BTM_BR_INQUIRY_MASK);break;case HCI_SET_EVENT_FILTER:btm_event_filter_complete (p);//執行此函數break; void btm_event_filter_complete (UINT8 *p) {UINT8 hci_status;tBTM_STATUS status;tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;tBTM_CMPL_CB *p_cb = p_inq->p_inqfilter_cmpl_cb; .../* Only process the inquiry filter; Ignore the connection filter until itis used by the upper layers */if (p_inq->inqfilt_active == TRUE ) //在命令下發的時候已經設置 {/* Extract the returned status from the buffer */STREAM_TO_UINT8 (hci_status, p);if (hci_status != HCI_SUCCESS){/* If standalone operation, return the error status; if embedded in the inquiry, continue the inquiry */BTM_TRACE_WARNING ("BTM Warning: Set Event Filter Failed (HCI returned 0x%x)", hci_status);status = BTM_ERR_PROCESSING;}elsestatus = BTM_SUCCESS;/* If the set filter was initiated externally (via BTM_SetInqEventFilter), call thecallback function to notify the initiator that it has completed */if (p_inq->state == BTM_INQ_INACTIVE_STATE)//此時是active狀態 {p_inq->inqfilt_active = FALSE;if (p_cb)(*p_cb) (&status);}else /* An inquiry is active (the set filter command was internally generated),process the next state of the process (Set a new filter or start the inquiry). */{if(status != BTM_SUCCESS){/* Process the inquiry complete (Error Status) */btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK));/* btm_process_inq_complete() does not restore the following settings on periodic inquiry */p_inq->inqfilt_active = FALSE;p_inq->inq_active = BTM_INQUIRY_INACTIVE;p_inq->state = BTM_INQ_INACTIVE_STATE;return;}/* Check to see if a new filter needs to be set up */if (p_inq->state == BTM_INQ_CLR_FILT_STATE){if ((status = btm_set_inq_event_filter (p_inq->inqparms.filter_cond_type, &p_inq->inqparms.filter_cond)) == BTM_CMD_STARTED){p_inq->state = BTM_INQ_SET_FILT_STATE;}else /* Error setting the filter: Call the initiator's callback function to indicate a failure */{p_inq->inqfilt_active = FALSE;/* Process the inquiry complete (Error Status) */btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK));}}else /* Initiate the Inquiry or Periodic Inquiry */{p_inq->state = BTM_INQ_ACTIVE_STATE;//處于該狀態p_inq->inqfilt_active = FALSE;//設置falsebtm_initiate_inquiry (p_inq);//開始掃描 }}} }?
上面代碼的做的主要的事情:
p_inq->state = BTM_INQ_ACTIVE_STATE;
p_inq->inqfilt_active = FALSE;
btm_initiate_inquiry (p_inq);
繼續看btm_initiate_inquiry的實現:
static void btm_initiate_inquiry (tBTM_INQUIRY_VAR_ST *p_inq) {const LAP *lap;tBTM_INQ_PARMS *p_inqparms = &p_inq->inqparms;btm_acl_update_busy_level (BTM_BLI_INQ_EVT); ...lap = (p_inq->inq_active & BTM_LIMITED_INQUIRY_ACTIVE) ? &limited_inq_lap : &general_inq_lap;//設置LAP {btm_clr_inq_result_flt();//清數據庫/* Allocate memory to hold bd_addrs responding */if ((p_inq->p_bd_db = (tINQ_BDADDR *)GKI_getbuf(GKI_MAX_BUF_SIZE)) != NULL){p_inq->max_bd_entries = (UINT16)(GKI_MAX_BUF_SIZE / sizeof(tINQ_BDADDR));memset(p_inq->p_bd_db, 0, GKI_MAX_BUF_SIZE); /* BTM_TRACE_DEBUG("btm_initiate_inquiry: memory allocated for %d bdaddrs",p_inq->max_bd_entries); */}if (!btsnd_hcic_inquiry(*lap, p_inqparms->duration, 0))//發送inquiry 命令btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK));} }?
?上面代碼中,重點就是btsnd_hcic_inquiry(*lap, p_inqparms->duration, 0)?
其實現就是向controller 發送inquiry 的命令。這里注意其duration參數是10,對應的實際的值是10*1.28s,在12.8秒之后會有inquiry complete 事件傳上來。
對于inquiry 的命令發送流程就分析到這里。
轉載于:https://www.cnblogs.com/libs-liu/p/9229253.html
總結
以上是生活随笔為你收集整理的蓝牙inquiry流程之命令下发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十三年,情如初见
- 下一篇: php服务器支付宝的pcm,php -