• 蓝牙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
    #else
        inq_params.mode = BTA_DM_GENERAL_INQUIRY;
    #endif
        inq_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();
    #endif
        if (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);
        }
    #endif
        result.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;//active
        p_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设备的inquiry
            p_inq->state = BTM_INQ_INACTIVE_STATE;
        return (status);
    }

    上面的代码主要做了三件事:

    1. 将一些参数保存在tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars  这个结构。
      1. p_inq->scan_type = INQ_GENERAL;
      2. p_inq->inqparms = *p_inqparms;
      3. p_inq->state = BTM_INQ_ACTIVE_STATE;
      4. p_inq->p_inq_cmpl_cb = p_cmpl_cb;
      5. p_inq->p_inq_results_cb = p_results_cb;
      6. p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */

      7. p_inq->inq_active = p_inqparms->mode = 0x11(BLE|BREDR)
    2. 做BLE 的inquiry :btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK),p_inqparms->duration)
    3. 做BREDR的inquiry:btm_set_inq_event_filter (p_inqparms->filter_cond_type,&p_inqparms->filter_cond)

    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);
    #endif
            p_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);
        else
            return (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 it
           is 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;
            }
            else
                status = BTM_SUCCESS;
    
            /* If the set filter was initiated externally (via BTM_SetInqEventFilter), call the
               callback 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;//设置false
                    btm_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 的命令发送流程就分析到这里。

  • 相关阅读:
    分享AWS网站
    centos7划分vlan
    在docker容器上如何实现代码的版本管理
    在docker私有仓库如何查看有哪些镜像?
    centos7下报错: import requests ImportError: No module named requests
    Unity2018.4.7导出Xcode工程报错解决方案
    1.OpenGL mac开发环境搭建记录
    unity 模板测试 详解
    游戏战争迷雾
    Unity 移动平台自己编写Shader丢失问题
  • 原文地址:https://www.cnblogs.com/libs-liu/p/9229253.html
Copyright © 2020-2023  润新知