藍牙disable流程簡述


藍牙關閉的流程比打開流程要簡單,主要就是一些profile的斷連以及協議棧相關結構的釋放。

這里簡單說一下其流程,就直接從協議棧的disable的接口說起了。

static int disable(void) {
  if (!interface_ready())//檢查hal層的callback
    return BT_STATUS_NOT_READY;
  stack_manager_get_interface()->shut_down_stack_async();
  return BT_STATUS_SUCCESS;
}

簡單看下stack_manager_get_interface :

static const stack_manager_t interface = {
  init_stack,
  start_up_stack_async,
  shut_down_stack_async,
  clean_up_stack_async,

  get_stack_is_running
};

const stack_manager_t *stack_manager_get_interface() {
  ensure_manager_initialized();
  return &interface;
}

看shut_down_stack_async的具體實現:

static void shut_down_stack_async(void) {
  thread_post(management_thread, event_shut_down_stack, NULL);
}

這里就是把關閉的函數 post 到management_thread 里面來執行。繼續執行的函數還是event_shut_down_stack

// Synchronous function to shut down the stack
static void event_shut_down_stack(UNUSED_ATTR void *context) {
...
  hack_future = future_new();//為了同步用的
  stack_is_running = false;

  btif_disable_bluetooth();//真正的disable的接口
  module_shut_down(get_module(BTIF_CONFIG_MODULE));//釋放btif模塊

  future_await(hack_future);//獲得了信號量之后才能執行到這里
  module_shut_down(get_module(CONTROLLER_MODULE)); // Doesn't do any work, just puts it in a restartable state

  LOG_DEBUG("%s finished.", __func__);
  btif_thread_post(event_signal_stack_down, NULL);//通知上層stack down
}

這里簡單說一下這個hack_future,其在這里的用途類似二值信號量,如果這個信號量不可用,就會一直等在future_await 這里,和其對應的一個函數叫future_ready,

void future_ready(future_t *future, void *value) {
  assert(future != NULL);
  assert(future->ready_can_be_called);

  future->ready_can_be_called = false;
  future->result = value;
  semaphore_post(future->semaphore);
}

這個函數會讓這個信號量變得可讀,future_await 就可以順利返回繼續執行。其實就是為了disable 過程的同步性。看了一下,其會在btif_disable_bluetooth_evt 這個函數中釋放信號量,看函數名字應該是disable 完成的一個事件,這也符合我們的預期。關於這個信號量,就分析到這里。

下面主要看btif_disable_bluetooth :

/*******************************************************************************
**
** Function         btif_disable_bluetooth
**
** Description      Inititates shutdown of Bluetooth system.
**                  Any active links will be dropped and device entering
**                  non connectable/discoverable mode
**
** Returns          void
**
*******************************************************************************/
bt_status_t btif_disable_bluetooth(void)
{
    BTIF_TRACE_DEBUG("BTIF DISABLE BLUETOOTH");
    btif_dm_on_disable();
    /* cleanup rfcomm & l2cap api */
    btif_sock_cleanup();
    btif_pan_cleanup();
    BTA_DisableBluetooth();
    return BT_STATUS_SUCCESS;
}

這里的注釋非常重要,可以幫助我們很好的理解函數,其語義是:關閉藍牙系統,所有的link 都要斷開,設備進入不可發現和不可連接的狀態。

我這里的分析的case 就是當前藍牙還連接着一個hid 設備的時候的disable 流程。

這里的重點是BTA_DisableBluetooth();,我這里先簡單分析下其他的幾個函數:

void btif_dm_on_disable()
{
    /* cancel any pending pairing requests */
    if (pairing_cb.state == BT_BOND_STATE_BONDING)
    {
        bt_bdaddr_t bd_addr;
        bdcpy(bd_addr.address, pairing_cb.bd_addr);
        btif_dm_cancel_bond(&bd_addr);//如果設備處於配對狀態,那么取消配對
    }
}

這個很簡單,如果設備處於配對狀態,這個時候關閉設備,它的確應該取消配對,符合預期。

另外兩個函數 一個和sock有關,另一個和pan有關,暫時都沒有用到這些模塊,暫時不分析。那么重點還是看BTA_DisableBluetooth();

/*******************************************************************************
**
** Function         BTA_DisableBluetooth
**
** Description      Disables bluetooth service.  This function is called when
**                  the application no longer needs bluetooth service
**
** Returns          void
**
*******************************************************************************/
tBTA_STATUS BTA_DisableBluetooth(void)
{
    BT_HDR    *p_msg;
    if ((p_msg = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
    {
        p_msg->event = BTA_DM_API_DISABLE_EVT;//向DM模塊發送disable 
        bta_sys_sendmsg(p_msg);
    }
...
    return BTA_SUCCESS;
}

這個函數就是向設備管理模塊發送BTA_DM_API_DISABLE_EVT ,然后由設備管理模塊繼續進一步的操作。這里使用bta_sys_sendmsg,涉及到進程間通信,其他的文章中已經講過,這里不贅述。最終調用的函數是在bta_dm_act.c中的bta_dm_disable:

/*******************************************************************************
**
** Function         bta_dm_disable
**
** Description      Disables the BT device manager
**
**
** Returns          void
**
*******************************************************************************/
void bta_dm_disable (tBTA_DM_MSG *p_data)
{
    UNUSED(p_data);

    /* Set l2cap idle timeout to 0 (so BTE immediately disconnects ACL link after last channel is closed) */
    L2CA_SetIdleTimeoutByBdAddr((UINT8 *)BT_BD_ANY, 0, BT_TRANSPORT_BR_EDR);//設置idletimeout時間,設置為0
    L2CA_SetIdleTimeoutByBdAddr((UINT8 *)BT_BD_ANY, 0, BT_TRANSPORT_LE);

    /* disable all active subsystems */
    bta_sys_disable(BTA_SYS_HW_BLUETOOTH);//關閉藍牙相關的各個子模塊。這里涉及到短線等操作

    BTM_SetDiscoverability(BTM_NON_DISCOVERABLE, 0, 0);//不可發現
    BTM_SetConnectability(BTM_NON_CONNECTABLE, 0, 0);//不可連接

    bta_dm_disable_pm();//電源管理相關
    bta_dm_disable_search_and_disc();//disable 搜索和服務發現的工作
    bta_dm_cb.disabling = TRUE;//設置正在斷開的標志位

#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE
    BTM_BleClearBgConnDev();//清除whitelist
#endif

    if(BTM_GetNumAclLinks()==0)//如果已經沒有link存在了,那么調用回調函數報告上層
    {
#if (defined(BTA_DISABLE_DELAY) && BTA_DISABLE_DELAY > 0)

        bta_sys_stop_timer(&bta_dm_cb.disable_timer);
        bta_dm_cb.disable_timer.p_cback = (TIMER_CBACK*)&bta_dm_disable_conn_down_timer_cback;
        bta_sys_start_timer(&bta_dm_cb.disable_timer, 0, BTA_DISABLE_DELAY);
#else
        bta_dm_disable_conn_down_timer_cback(NULL);
#endif
    }
    else//如果還有link存在,啟動一個定時器,如果5s之后還有link存在,那么執行定時器回調函數
    {
        bta_dm_cb.disable_timer.p_cback = (TIMER_CBACK*)&bta_dm_disable_timer_cback;
        bta_dm_cb.disable_timer.param = 0;
        bta_sys_start_timer(&bta_dm_cb.disable_timer, 0, 5000);
    }

}

 上面的函數的重點是藍牙關閉各個子模塊的函數bta_sys_disable(BTA_SYS_HW_BLUETOOTH) ,簡單分析下另外的函數。

首先看L2CA_SetIdleTimeoutByBdAddr這里的參數是任何設備,timeout 時間是0,其語義就像其注釋“Set l2cap idle timeout to 0 (so BTE immediately disconnects ACL link after last channel is closed) ”當l2cap上面沒有channel存在的時候,acl link會被立即斷開。

BTM_SetDiscoverability和BTM_SetConnectability,這兩個函數也是比較簡單,告訴藍牙控制器不要被發現,也不可被連接,最終控制器的做法是關閉scan。

bta_dm_disable_search_and_disc,這個函數的作用也和它的名字一樣,肯定是有disable search和disable discovery兩步。簡單說一下,如果當前設備在搜索,那么調用bta_dm_search_cancel 來把這個行為cancel掉,根據具體情況,這里涉及到的可能的case包括:cancel 掉inquiry、cancel掉獲取名字的流程、cancel掉GATT的流程以及向Bluetooth 應用匯報底層狀態的行為。

 下面我們看下BTM_BleClearBgConnDev:

/*******************************************************************************
**
** Function         BTM_BleClearBgConnDev
**
** Description      This function is called to clear the whitelist,
**                  end any pending whitelist connections,
*                   and reset the local bg device list.
**
** Parameters       void
**
** Returns          void
**
*******************************************************************************/
void BTM_BleClearBgConnDev(void)
{
    btm_ble_start_auto_conn(FALSE);
    btm_ble_clear_white_list();
    gatt_reset_bgdev_list();
}

 

 我們還是看一下其注釋,可知這個函數的主要作用就是清除whitelist,以及pending的連接,並且清空了bg device list

/*******************************************************************************
**
** Function         btm_ble_start_auto_conn
**
** Description      This function is to start/stop auto connection procedure.
**
** Parameters       start: TRUE to start; FALSE to stop.
**
** Returns          void
**
*******************************************************************************/
BOOLEAN btm_ble_start_auto_conn(BOOLEAN start)
{
    tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;
    BD_ADDR dummy_bda = {0};
    BOOLEAN exec = TRUE;
    UINT16 scan_int;
    UINT16 scan_win;
    UINT8 own_addr_type = p_cb->addr_mgnt_cb.own_addr_type;
    UINT8 peer_addr_type = BLE_ADDR_PUBLIC;
    if (start)
    {
    ...
    }    
else
    {
        if (p_cb->conn_state == BLE_BG_CONN)
        {
            btsnd_hcic_ble_create_conn_cancel();//如果已經init了BG conn,那么就取消
            btm_ble_set_conn_st (BLE_CONN_CANCEL);//清除init狀態
            p_cb->wl_state &= ~BTM_BLE_WL_INIT;//清楚wl的init狀態
        }
        else
        {
            BTM_TRACE_DEBUG("conn_st = %d, not in auto conn state, cannot stop", p_cb->conn_state);
            exec = FALSE;
        }
    }
    return exec;
}

 注釋里面的“end any pending whitelist connections” 對應的就是btm_ble_start_auto_conn(FALSE); 函數的行為,下面我們看看btm_ble_clear_white_list:

void btm_ble_clear_white_list (void)
{
    BTM_TRACE_EVENT ("btm_ble_clear_white_list");
    btsnd_hcic_ble_clear_white_list();
    background_connections_clear();
}

 這個很簡單,就是下了一個HCI命令告知控制器要把wl清除掉,另外其把本地的BG connection list也清除了。關於BTM_BleClearBgConnDev 就說到這里。

接下來看一下BTM_GetNumAclLinks,這個函數是統計當前的ACL link的數量,如果是已經沒有acl link了,那么BTA_DISABLE_DELAY 時間之后就可以調用bta_dm_disable_conn_down_timer_cback 來向上層匯報BTA_SYS_API_DISABLE_EVT這個行為。該文章分析的是有link存在的情況,是不走這個流程,但是我們可以猜想,等link 斷開之后,應該還會走bta_dm_disable_conn_down_timer_cback 這個流程來上報事件。

最后說一下bta_dm_disable_timer_cback,這個函數 當超過5s,還有link沒有斷開的時候就會執行該函數來斷開,其核心流程就是btm_remove_acl ,這里不做詳細分析了。(只有當所有的link都斷開了,這個定時器才會被取消)

我們現在看一下disable 的重點:關閉各個子模塊:bta_sys_disable(BTA_SYS_HW_BLUETOOTH);

/*******************************************************************************
**
** Function         bta_sys_disable
**
** Description      For each registered subsystem execute its disable function.
**
** Returns          void
**
*******************************************************************************/
void bta_sys_disable(tBTA_SYS_HW_MODULE module)
{
    int bta_id = 0;
    int bta_id_max = 0;
    switch( module )
    {
        case BTA_SYS_HW_BLUETOOTH:
            bta_id = BTA_ID_DM;
            bta_id_max = BTA_ID_BLUETOOTH_MAX;
            break;
        default:
            APPL_TRACE_WARNING("bta_sys_disable: unkown module");
            return;
    }

    for ( ; bta_id <= bta_id_max; bta_id++)
    {
        if (bta_sys_cb.reg[bta_id] != NULL)
        {
            if (bta_sys_cb.is_reg[bta_id] == TRUE  &&  bta_sys_cb.reg[bta_id]->disable != NULL)
            {
                (*bta_sys_cb.reg[bta_id]->disable)();
            }
        }
    }
}

 

這個函數雖然執行的任務非常的多,但是其很好理解,就是調用每一個每一個子模塊先前注冊的handler的disable 函數,主要是做一些清理的工作。

下面對這些模塊做一個簡單的說明:

總共有哪些模塊呢?如下:

/* SW sub-systems */
#define BTA_ID_SYS          0            /* system manager */
/* BLUETOOTH PART - from 0 to BTA_ID_BLUETOOTH_MAX */
#define BTA_ID_DM           1            /* device manager */
#define BTA_ID_DM_SEARCH    2            /* device manager search */
#define BTA_ID_DM_SEC       3            /* device manager security */
#define BTA_ID_DG           4            /* data gateway */
#define BTA_ID_AG           5            /* audio gateway */
#define BTA_ID_OPC          6            /* object push client */
#define BTA_ID_OPS          7            /* object push server */
#define BTA_ID_FTS          8            /* file transfer server */
#define BTA_ID_CT           9            /* cordless telephony terminal */
#define BTA_ID_FTC          10           /* file transfer client */
#define BTA_ID_SS           11           /* synchronization server */
#define BTA_ID_PR           12           /* Printer client */
#define BTA_ID_BIC          13           /* Basic Imaging Client */
#define BTA_ID_PAN          14           /* Personal Area Networking */
#define BTA_ID_BIS          15           /* Basic Imaging Server */
#define BTA_ID_ACC          16           /* Advanced Camera Client */
#define BTA_ID_SC           17           /* SIM Card Access server */
#define BTA_ID_AV           18           /* Advanced audio/video */
#define BTA_ID_AVK          19           /* Audio/video sink */
#define BTA_ID_HD           20           /* HID Device */
#define BTA_ID_CG           21           /* Cordless Gateway */
#define BTA_ID_BP           22           /* Basic Printing Client */
#define BTA_ID_HH           23           /* Human Interface Device Host */
#define BTA_ID_PBS          24           /* Phone Book Access Server */
#define BTA_ID_PBC          25           /* Phone Book Access Client */
#define BTA_ID_JV           26           /* Java */
#define BTA_ID_HS           27           /* Headset */
#define BTA_ID_MSE          28           /* Message Server Equipment */
#define BTA_ID_MCE          29           /* Message Client Equipment */
#define BTA_ID_HL           30           /* Health Device Profile*/
#define BTA_ID_GATTC        31           /* GATT Client */
#define BTA_ID_GATTS        32           /* GATT Client */
#define BTA_ID_SDP          33           /* SDP Client */
#define BTA_ID_BLUETOOTH_MAX   34        /* last BT profile */

 

前面幾個是系統相關的:system manager、device manager、device manager search等,后面都是BT 的profile 模塊。

那這些模塊在哪里注冊的呢?這里也簡單介紹幾個實例:

首先看看系統管理模塊:

/*******************************************************************************
**
** Function         bta_sys_init
**
** Description      BTA initialization; called from task initialization.
**
**
** Returns          void
**
*******************************************************************************/
void bta_sys_init(void)
{
...
    appl_trace_level = APPL_INITIAL_TRACE_LEVEL;
    /* register BTA SYS message handler */
    bta_sys_register( BTA_ID_SYS,  &bta_sys_hw_reg);
...
}

 

發現系統管理模塊在bta_sys_init 的時候就注冊了,這也符合預期,看看其handler實現:

static const tBTA_SYS_REG bta_sys_hw_reg =
{
    bta_sys_sm_execute,
    NULL   //disable函數為NULL
};

 

再看看device manager和device manager search模塊的注冊情況:

/*******************************************************************************
**
** Function         BTA_EnableBluetooth
**
** Description      Enables bluetooth service.  This function must be
**                  called before any other functions in the BTA API are called.
**
**
** Returns          tBTA_STATUS
**
*******************************************************************************/
tBTA_STATUS BTA_EnableBluetooth(tBTA_DM_SEC_CBACK *p_cback)
{
...
    bta_sys_register (BTA_ID_DM, &bta_dm_reg );
    bta_sys_register (BTA_ID_DM_SEARCH, &bta_dm_search_reg );
...
}

 

這兩個是藍牙enable的時候注冊的BTA_EnableBluetooth,看看他們的handler:

static const tBTA_SYS_REG bta_dm_reg =
{
    bta_dm_sm_execute,
    bta_dm_sm_disable
};

static const tBTA_SYS_REG bta_dm_search_reg =
{
    bta_dm_search_sm_execute,
    bta_dm_search_sm_disable
};

 

他們的disable函數也非常的簡單,就是一個deregister的過程:

void bta_dm_search_sm_disable( )
{
    bta_sys_deregister( BTA_ID_DM_SEARCH );

}

 

下面我們找兩個profile 看看:A2dp、GATT和HID

void BTA_AvEnable(tBTA_SEC sec_mask, tBTA_AV_FEAT features, tBTA_AV_CBACK *p_cback)
{
    tBTA_AV_API_ENABLE  *p_buf;
    /* register with BTA system manager */
    bta_sys_register(BTA_ID_AV, &bta_av_reg);在AVenable的時候注冊
static const tBTA_SYS_REG bta_av_reg =
{
    bta_av_hdl_event,
    BTA_AvDisable
};

 

看看disable的內容:

/*******************************************************************************
**
** Function         BTA_AvDisable
**
** Description      Disable the advanced audio/video service.
**
** Returns          void
**
*******************************************************************************/
void BTA_AvDisable(void)
{
    BT_HDR  *p_buf;
    bta_sys_deregister(BTA_ID_AV);
    if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
    {
        p_buf->event = BTA_AV_API_DISABLE_EVT;//會繼續處理這個事件相關的東西
        bta_sys_sendmsg(p_buf);
    }
}

 

看看GATT的情況:

void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb)
{
    tBTA_GATTC_API_REG  *p_buf;

    if (bta_sys_is_register(BTA_ID_GATTC) == FALSE)
    {
        bta_sys_register(BTA_ID_GATTC, &bta_gattc_reg);
    }
static const tBTA_SYS_REG bta_gattc_reg =
{
    bta_gattc_hdl_event,
    BTA_GATTC_Disable
};
/*******************************************************************************
**
** Function         BTA_GATTC_Disable
**
** Description      This function is called to disable GATTC module
**
** Parameters       None.
**
** Returns          None
**
*******************************************************************************/
void BTA_GATTC_Disable(void)
{
    BT_HDR  *p_buf;

    if (bta_sys_is_register(BTA_ID_GATTC) == FALSE)
    {
        APPL_TRACE_WARNING("GATTC Module not enabled/already disabled");
        return;
    }
    if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
    {
        p_buf->event = BTA_GATTC_API_DISABLE_EVT;
        bta_sys_sendmsg(p_buf);
    }
    bta_sys_deregister(BTA_ID_GATTC);

}

 

這邊我們發現套路是一樣的,都會去bta_sys_deregister,然后會發送事件相應的disable事件。

下面重點分析一下HID的情況:

/*******************************************************************************
**
** Function         BTA_HhEnable
**
** Description      Enable the HID host.  This function must be called before
**                  any other functions in the HID host API are called. When the
**                  enable operation is complete the callback function will be
**                  called with BTA_HH_ENABLE_EVT.
**
**
** Returns          void
**
*******************************************************************************/
void BTA_HhEnable(tBTA_SEC sec_mask, tBTA_HH_CBACK *p_cback)
{
    tBTA_HH_API_ENABLE *p_buf;

    /* register with BTA system manager */
    bta_sys_register(BTA_ID_HH, &bta_hh_reg);
...
static const tBTA_SYS_REG bta_hh_reg =
{
    bta_hh_hdl_event,
    BTA_HhDisable
};
/*******************************************************************************
**
** Function         BTA_HhDisable
**
** Description      Disable the HID host. If the server is currently
**                  connected, the connection will be closed.
**
** Returns          void
**
*******************************************************************************/
void BTA_HhDisable(void)
{
    BT_HDR  *p_buf;

    bta_sys_deregister(BTA_ID_HH);
    if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR))) != NULL)
    {
        p_buf->event = BTA_HH_API_DISABLE_EVT;
        bta_sys_sendmsg(p_buf);
    }
}

 

這里注意一下,函數的注釋,“如果當前的hid server是已經連接的,那么連接會被中斷”,那我們可以想象,藍牙都關閉了,和它連着的設備肯定也會斷開的。

/*******************************************************************************
**
** Function         bta_hh_hdl_event
**
** Description      HID host main event handling function.
**
**
** Returns          void
**
*******************************************************************************/
BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg)
{
    UINT8           index = BTA_HH_IDX_INVALID;
    tBTA_HH_DEV_CB *p_cb = NULL;

    switch (p_msg->event)
    {
        case BTA_HH_API_ENABLE_EVT:
            bta_hh_api_enable((tBTA_HH_DATA *) p_msg);
            break;

        case BTA_HH_API_DISABLE_EVT:
            bta_hh_api_disable();
            break;

 

繼續看bta_hh_api_disable:

/*******************************************************************************
**
** Function         bta_hh_api_disable
**
** Description      Perform necessary operations to disable HID host.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hh_api_disable(void)
{
    UINT8 xx;

    /* service is not enabled */
    if (bta_hh_cb.p_cback == NULL)
        return;

    /* no live connection, signal DISC_CMPL_EVT directly */
    if (!bta_hh_cb.cnt_num)
    {
        bta_hh_disc_cmpl();
    }
    else /* otherwise, disconnect all live connections */ //如果有連接斷開所有的連接
    {
        bta_hh_cb.w4_disable = TRUE;

        for(xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++)
        {
            /* send API_CLOSE event to every connected device */
            if ( bta_hh_cb.kdev[xx].state == BTA_HH_CONN_ST )
            {
                /* disconnect all connected devices */
                bta_hh_sm_execute(&bta_hh_cb.kdev[xx],
                                BTA_HH_API_CLOSE_EVT,
                                NULL);
            }
        }
    }

    return;
}

 

這里分析的重點 就是bta_hh_sm_execute(&bta_hh_cb.kdev[xx],BTA_HH_API_CLOSE_EVT,NULL); 的流程

 

/*******************************************************************************
**
** Function         bta_hh_sm_execute
**
** Description      State machine event handling function for HID Host
**
**
** Returns          void
**
*******************************************************************************/
void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA * p_data)
{
    tBTA_HH_ST_TBL  state_table;
    UINT8           action;
    tBTA_HH         cback_data;
    tBTA_HH_EVT     cback_event = 0;
#if BTA_HH_DEBUG == TRUE
    tBTA_HH_STATE   in_state ;
    UINT16          debug_event = event;
#endif

    memset(&cback_data, 0, sizeof(tBTA_HH));
...
else
    {
        if ((p_cb->state == BTA_HH_NULL_ST) || (p_cb->state >= BTA_HH_INVALID_ST))
        {
            APPL_TRACE_ERROR("bta_hh_sm_execute: Invalid state State = 0x%x, Event = %d",
                              p_cb->state,event);
            return;
        }
        state_table = bta_hh_st_tbl[p_cb->state - 1];
        event &= 0xff;
        p_cb->state = state_table[event][BTA_HH_NEXT_STATE] ;

        if ((action = state_table[event][BTA_HH_ACTION]) != BTA_HH_IGNORE)
        {
            (*bta_hh_action[action])(p_cb, p_data);
        }
    }

    return;
}

 

上面的函數涉及到狀態機,看似復雜,其實就是根據當前狀態找到狀態轉換表,根據這張表就可以知道下一步做什么動作,然后根據這個動作就可以找到相應的函數:

當前狀態是已經連接狀態:

const UINT8 bta_hh_st_connected[][BTA_HH_NUM_COLS] =
{
/* Event                          Action                 Next state */
/* BTA_HH_API_OPEN_EVT      */    {BTA_HH_IGNORE,        BTA_HH_CONN_ST    },
/* BTA_HH_API_CLOSE_EVT     */    {BTA_HH_API_DISC_ACT,  BTA_HH_CONN_ST    },
/* BTA_HH_INT_OPEN_EVT      */    {BTA_HH_OPEN_ACT,      BTA_HH_CONN_ST    },
/* BTA_HH_INT_CLOSE_EVT     */    {BTA_HH_CLOSE_ACT,     BTA_HH_IDLE_ST    },
/* BTA_HH_INT_DATA_EVT      */    {BTA_HH_DATA_ACT,      BTA_HH_CONN_ST    },
/* BTA_HH_INT_CTRL_DATA     */    {BTA_HH_CTRL_DAT_ACT,  BTA_HH_CONN_ST    },
/* BTA_HH_INT_HANDSK_EVT    */    {BTA_HH_HANDSK_ACT,    BTA_HH_CONN_ST    },
/* BTA_HH_SDP_CMPL_EVT      */    {BTA_HH_IGNORE,         BTA_HH_CONN_ST       },
/* BTA_HH_API_WRITE_DEV_EVT */    {BTA_HH_WRITE_DEV_ACT, BTA_HH_CONN_ST    },
/* BTA_HH_API_GET_DSCP_EVT  */    {BTA_HH_GET_DSCP_ACT,  BTA_HH_CONN_ST    },
/* BTA_HH_API_MAINT_DEV_EVT */    {BTA_HH_MAINT_DEV_ACT, BTA_HH_CONN_ST    },
/* BTA_HH_OPEN_CMPL_EVT        */    {BTA_HH_IGNORE,         BTA_HH_CONN_ST    }
#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE)
/* BTA_HH_GATT_CLOSE_EVT    */    ,{BTA_HH_GATT_CLOSE,    BTA_HH_IDLE_ST    }
/* BTA_HH_GATT_OPEN_EVT    */    ,{BTA_HH_IGNORE,        BTA_HH_CONN_ST    }
/* BTA_HH_START_ENC_EVT    */    ,{BTA_HH_IGNORE,        BTA_HH_CONN_ST     }
/* BTA_HH_ENC_CMPL_EVT     */    ,{BTA_HH_IGNORE,        BTA_HH_CONN_ST     }
/* READ_CHAR_CMPL_EVT */         ,{BTA_HH_LE_READ_CHAR,  BTA_HH_CONN_ST     }
/* WRITE_CHAR_CMPL_EVT*/         ,{BTA_HH_LE_WRITE,      BTA_HH_CONN_ST     }
/* READ_DESCR_CMPL_EVT */        ,{BTA_HH_LE_READ_DESCR, BTA_HH_CONN_ST     }   /* do not currently read any descr when connection up */
/* WRITE_DESCR_CMPL_EVT */       ,{BTA_HH_WRITE_DESCR,   BTA_HH_CONN_ST     }   /* do not currently write any descr when connection up */
/* SCPP_UPDATE_EVT */            ,{BTA_HH_LE_UPDATE_SCPP,  BTA_HH_CONN_ST   }
/* BTA_HH_GATT_ENC_CMPL_EVT */   ,{BTA_HH_IGNORE,        BTA_HH_CONN_ST     }
#endif
}

 

根據這張表我們知道,下一個狀態依然是已連接狀態,下一個要執行的行為是BTA_HH_API_DISC_ACT,看這個行為對應的函數 :

/*******************************************************************************
**
** Function         bta_hh_api_disc_act
**
** Description      HID Host initiate a disconnection.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hh_api_disc_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
{
    tBTA_HH_CBDATA    disc_dat;
    tHID_STATUS     status;

    if (p_cb->is_le_device)
        bta_hh_le_api_disc_only_act(p_cb);//這里le 沒有active
    else
    {
        /* found an active connection */
        disc_dat.handle = p_data ?(UINT8)p_data->hdr.layer_specific :p_cb->hid_handle;
        disc_dat.status = BTA_HH_ERR;
        status = HID_HostCloseDev(disc_dat.handle);斷開連接
        if (status)
            (* bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, (tBTA_HH *)&disc_dat);//成功之后上報close事件
    }
    return;
}

 

 這里看看HID_HostCloseDev的實現:

/*******************************************************************************
**
** Function         HID_HostCloseDev
**
** Description      This function disconnects the device.
**
** Returns          void
**
*******************************************************************************/
tHID_STATUS HID_HostCloseDev( UINT8 dev_handle )
{
    if( !hh_cb.reg_flag )
        return (HID_ERR_NOT_REGISTERED);

    if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) )
        return HID_ERR_INVALID_PARAM;

    hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1;
    btu_stop_timer( &(hh_cb.devices[dev_handle].conn.timer_entry) ) ;

    if( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED )
        return HID_ERR_NO_CONNECTION;

    hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1;
    return hidh_conn_disconnect( dev_handle );//執行這里
}
/*******************************************************************************
**
** Function         hidh_conn_disconnect
**
** Description      This function disconnects a connection.
**
** Returns          TRUE if disconnect started, FALSE if already disconnected
**
*******************************************************************************/
tHID_STATUS hidh_conn_disconnect (UINT8 dhandle)
{
    tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn;

    if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0))
    {
        p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING;

        /* Set l2cap idle timeout to 0 (so ACL link is disconnected
         * immediately after last channel is closed) */
        L2CA_SetIdleTimeoutByBdAddr(hh_cb.devices[dhandle].addr, 0, BT_TRANSPORT_BR_EDR);
        /* Disconnect both interrupt and control channels */
        if (p_hcon->intr_cid)
            L2CA_DisconnectReq (p_hcon->intr_cid);
        else if (p_hcon->ctrl_cid)
            L2CA_DisconnectReq (p_hcon->ctrl_cid);
    }
    else
    {
        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
    }
    return (HID_SUCCESS);
}

 我們可以看出主要執行的函數是hidh_conn_disconnect ,發現這個 hid host應該是有兩個通道,一個中斷通道,一個控制通道

 這邊結合btsnoop 看了一下發現,中斷通道是用來傳播數據的。控制通道是用來傳輸控制信令的。具體可能還要研究下hid profile

 

我們繼續看,這里通過L2CA_SetIdleTimeoutByBdAddr設置了設備的idle timeout時間 為0,其實這個在一開始的disable 的函數的入口就已經設置過了。

從代碼的邏輯可以看出,是優先釋放中斷通道,這個也好理解,因為中斷通道是傳輸數據的。傳輸信令的通道肯定是后釋放。下面看l2cap 層面channel的釋放過程:

以下簡述l2cap的釋放過程,不感興趣的可以直接略過。


 

這里已經走到了l2cap層:

/*******************************************************************************
**
** Function         L2CA_DisconnectReq
**
** Description      Higher layers call this function to disconnect a channel.
**
** Returns          TRUE if disconnect sent, else FALSE
**
*******************************************************************************/
BOOLEAN L2CA_DisconnectReq (UINT16 cid)
{
    tL2C_CCB        *p_ccb;
    counter_add("l2cap.disconn.req", 1);
    /* Find the channel control block. We don't know the link it is on. */
    if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL)
    {
        L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_disc_req, CID: %d", cid);
        return (FALSE);
    }
    l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);對於該channel 執行操作
    return (TRUE);
}

 

這里會先根據cid 來找這個channel control block,然后 調用l2c_csm_execute 繼續執行:

/*******************************************************************************
**
** Function         l2c_csm_execute
**
** Description      This function executes the state machine.
**
** Returns          void
**
*******************************************************************************/
void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
{
    switch (p_ccb->chnl_state)
    {
    case CST_CLOSED:
        l2c_csm_closed (p_ccb, event, p_data);
        break;
...
    case CST_OPEN:
        l2c_csm_open (p_ccb, event, p_data);//當前是open狀態
        break;
...

 

繼續看l2c_csm_open:

static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
{
    UINT16                  local_cid = p_ccb->local_cid;
    tL2CAP_CFG_INFO         *p_cfg;
    tL2C_CHNL_STATE         tempstate;
    UINT8                   tempcfgdone;
    UINT8                   cfg_result;
...
    switch (event)
    {
...
    case L2CEVT_L2CA_DISCONNECT_REQ:                 /* Upper wants to disconnect */
        /* Make sure we are not in sniff mode */
        {
            tBTM_PM_PWR_MD settings;
            memset((void*)&settings, 0, sizeof(settings));
            settings.mode = BTM_PM_MD_ACTIVE;
            BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);//HCI_Exit_Sniff_Mode

        }

        l2cu_send_peer_disc_req (p_ccb);//發送給對端設備
        p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;//設置當前狀態
        btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
        break;
...

 

接下來就會收到對端設備對於斷開channel 的回復,我們執行的函數還是

void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
{
    switch (p_ccb->chnl_state)
    {
    case CST_CLOSED:
        l2c_csm_closed (p_ccb, event, p_data);
        break;
...
    case CST_W4_L2CAP_DISCONNECT_RSP:
        l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data);
        break;
...

 

static void l2c_csm_w4_l2cap_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
{
    tL2CA_DISCONNECT_CFM_CB *disconnect_cfm = p_ccb->p_rcb->api.pL2CA_DisconnectCfm_Cb;
    UINT16                  local_cid = p_ccb->local_cid;
    switch (event)
    {
    case L2CEVT_L2CAP_DISCONNECT_RSP:                /* Peer disconnect response */
        l2cu_release_ccb (p_ccb);
        if (disconnect_cfm)
        {
            (*disconnect_cfm)(local_cid, L2CAP_DISC_OK);
        }
        break;
...

 

接收到對端的回復之后,可以直接調用l2cu_release_ccb (p_ccb);來釋放掉channel 了,

我們看看具體的處理:

/*******************************************************************************
**
** Function         l2cu_release_ccb
**
** Description      This function releases a Channel Control Block. The timer
**                  is stopped, any attached buffers freed, and the CCB is removed
**                  from the link control block.
**
** Returns          void
**
*******************************************************************************/
void l2cu_release_ccb (tL2C_CCB *p_ccb)
{
    tL2C_LCB    *p_lcb = p_ccb->p_lcb;
    tL2C_RCB    *p_rcb = p_ccb->p_rcb;

    if (p_rcb && (p_rcb->psm != p_rcb->real_psm))
    {
        btm_sec_clr_service_by_psm(p_rcb->psm);
    }
    if (p_ccb->should_free_rcb)
    {
        osi_free(p_rcb);
        p_ccb->p_rcb = NULL;
        p_ccb->should_free_rcb = false;
    }
    btm_sec_clr_temp_auth_service (p_lcb->remote_bd_addr);

    /* Stop the timer */
    btu_stop_timer (&p_ccb->timer_entry);

    while (!GKI_queue_is_empty(&p_ccb->xmit_hold_q))
        GKI_freebuf (GKI_dequeue (&p_ccb->xmit_hold_q));

    l2c_fcr_cleanup (p_ccb);

    /* Channel may not be assigned to any LCB if it was just pre-reserved */
    if ( (p_lcb) &&
         ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID)
#if (L2CAP_UCD_INCLUDED == TRUE)
         ||(p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID)
#endif
         )
       )
    {
        l2cu_dequeue_ccb (p_ccb);//通過ccb找到lcb里面的ccb隊列,並移除
        /* Delink the CCB from the LCB */
        p_ccb->p_lcb = NULL;//ccb不再保存指向lcb的指針
    }

    /* Put the CCB back on the free pool */
    if (!l2cb.p_free_ccb_first)//把ccb放到l2cb.p_free_ccb_queue 隊列
    {
        l2cb.p_free_ccb_first = p_ccb;
        l2cb.p_free_ccb_last  = p_ccb;
        p_ccb->p_next_ccb     = NULL;
        p_ccb->p_prev_ccb     = NULL;
    }
    else
    {
        p_ccb->p_next_ccb  = NULL;
        p_ccb->p_prev_ccb  = l2cb.p_free_ccb_last;
        l2cb.p_free_ccb_last->p_next_ccb = p_ccb;
        l2cb.p_free_ccb_last  = p_ccb;
    }
    /* Flag as not in use */
    p_ccb->in_use = FALSE;
    /* If no channels on the connection, start idle timeout */
    if ((p_lcb) && p_lcb->in_use && (p_lcb->link_state == LST_CONNECTED))
    {
        if (!p_lcb->ccb_queue.p_first_ccb)//如果沒有ccb 存在
        {
            l2cu_no_dynamic_ccbs (p_lcb);//這里主要是斷開link
        }
        else
        {
            /* Link is still active, adjust channel quotas. */
            l2c_link_adjust_chnl_allocation ();
        }
    }
}

 

做的事情主要就是從lcb里面移除ccb,如果link上面沒有ccb存在了,那么斷開link。簡單看一下l2cu_no_dynamic_ccbs的實現:

/*******************************************************************************
**
** Function         l2cu_no_dynamic_ccbs
**
** Description      Handles the case when there are no more dynamic CCBs. If there
**                  are any fixed CCBs, start the longest of the fixed CCB timeouts,
**                  otherwise start the default link idle timeout or disconnect.
**
** Returns          void
**
*******************************************************************************/
void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb)
{
    tBTM_STATUS     rc;
    UINT16          timeout = p_lcb->idle_timeout;

#if (L2CAP_NUM_FIXED_CHNLS > 0)
    int         xx;

    for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)//在p_fixed_ccbs中找idletimeout最大的
    {
        if ( (p_lcb->p_fixed_ccbs[xx] != NULL) && (p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout > timeout) )
            timeout = p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout;
    }
#endif

    /* If the link is pairing, do not mess with the timeouts */
    if (p_lcb->is_bonding)
        return;

    if (timeout == 0)
    {
        rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER);//直接斷開
        if (rc == BTM_CMD_STARTED)
        {
            l2cu_process_fixed_disc_cback(p_lcb);
            p_lcb->link_state = LST_DISCONNECTING;
            timeout = L2CAP_LINK_DISCONNECT_TOUT;
        }
        else if (rc == BTM_SUCCESS)
        {
            l2cu_process_fixed_disc_cback(p_lcb);
            /* BTM SEC will make sure that link is release (probably after pairing is done) */
            p_lcb->link_state = LST_DISCONNECTING;
            timeout = 0xFFFF;
        }
        else if ( (p_lcb->is_bonding)
            &&   (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) )
        {
            l2cu_process_fixed_disc_cback(p_lcb);
            p_lcb->link_state = LST_DISCONNECTING;
            timeout = L2CAP_LINK_DISCONNECT_TOUT;
        }
        else
        {
            /* probably no buffer to send disconnect */
            timeout = BT_1SEC_TIMEOUT;
        }
    }
    if (timeout != 0xFFFF)
    {
        btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout);
    }
    else
    {
        btu_stop_timer(&p_lcb->timer_entry);
    }
}

 

這里我們還要回到l2c_csm_w4_l2cap_disconnect_rsp函數中,我們已經分析了:l2cu_release_ccb的流程

    case L2CEVT_L2CAP_DISCONNECT_RSP:                /* Peer disconnect response */
        l2cu_release_ccb (p_ccb);
        if (disconnect_cfm)
        {
            (*disconnect_cfm)(local_cid, L2CAP_DISC_OK);
        }
        break;

 

下面我們繼續分析一下disconnect_cfm  的流程:

tL2CA_DISCONNECT_CFM_CB *disconnect_cfm = p_ccb->p_rcb->api.pL2CA_DisconnectCfm_Cb;  從該語句,我們可以猜想到這個函數是從l2cap的上層注冊到l2cap的,那具體是在哪里注冊的呢?

是在bta_hh_api_enable  里面一步一步注冊的:

/*****************************************************************************
**  Action Functions
*****************************************************************************/
/*******************************************************************************
**
** Function         bta_hh_api_enable
**
** Description      Perform necessary operations to enable HID host.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hh_api_enable(tBTA_HH_DATA *p_data)
{
    tBTA_HH_STATUS      status = BTA_HH_ERR;
    UINT8               xx;

    /* initialize BTE HID */
    HID_HostInit();

    memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB));

    HID_HostSetSecurityLevel("", p_data->api_enable.sec_mask);

    /* Register with L2CAP */
    if ( HID_HostRegister (bta_hh_cback) == HID_SUCCESS) //注冊到l2cap
...

 Register with L2CAP使用的是hidh_conn_reg,另外注意到這里還傳入了一個參數,注冊了一個hh_cb.callback = bta_hh_cback ; 后面上報事件的時候會調用到

/*******************************************************************************
**
** Function         HID_HostRegister
**
** Description      This function registers HID-Host with lower layers
**
** Returns          tHID_STATUS
**
*******************************************************************************/
tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback)
{
    tHID_STATUS st;

    if( hh_cb.reg_flag )
        return HID_ERR_ALREADY_REGISTERED;

    if( dev_cback == NULL )
        return HID_ERR_INVALID_PARAM;

    /* Register with L2CAP */
    if( (st = hidh_conn_reg()) != HID_SUCCESS )
    {
    return st;
  }
hh_cb.callback = dev_cback ;
hh_cb.reg_flag = TRUE;
return (HID_SUCCESS);
 
        
/*******************************************************************************
**
** Function         hidh_l2cif_reg
**
** Description      This function initializes the SDP unit.
**
** Returns          void
**
*******************************************************************************/
tHID_STATUS hidh_conn_reg (void)
{
    int xx;

    /* Initialize the L2CAP configuration. We only care about MTU and flush */
    memset(&hh_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO));

    hh_cb.l2cap_cfg.mtu_present          = TRUE;
    hh_cb.l2cap_cfg.mtu                  = HID_HOST_MTU;
    hh_cb.l2cap_cfg.flush_to_present     = TRUE;
    hh_cb.l2cap_cfg.flush_to             = HID_HOST_FLUSH_TO;

    /* Now, register with L2CAP */
    if (!L2CA_Register (HID_PSM_CONTROL, (tL2CAP_APPL_INFO *) &hst_reg_info))
    {
        HIDH_TRACE_ERROR ("HID-Host Control Registration failed");
        return (HID_ERR_L2CAP_FAILED) ;
    }
    if (!L2CA_Register (HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *) &hst_reg_info))
    {
...

 

這里我們看到了L2CA_Register了,

/*******************************************************************************
**
** Function         L2CA_Register
**
** Description      Other layers call this function to register for L2CAP
**                  services.
**
** Returns          PSM to use or zero if error. Typically, the PSM returned
**                  is the same as was passed in, but for an outgoing-only
**                  connection to a dynamic PSM, a "virtual" PSM is returned
**                  and should be used in the calls to L2CA_ConnectReq(),
**                  L2CA_ErtmConnectReq() and L2CA_Deregister()
**
*******************************************************************************/
UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info)
{
    tL2C_RCB    *p_rcb;
    UINT16      vpsm = psm;

    L2CAP_TRACE_API ("L2CAP - L2CA_Register() called for PSM: 0x%04x", psm);

    /* Verify that the required callback info has been filled in
    **      Note:  Connection callbacks are required but not checked
    **             for here because it is possible to be only a client
    **             or only a server.
    */
    if ((!p_cb_info->pL2CA_ConfigCfm_Cb)
     || (!p_cb_info->pL2CA_ConfigInd_Cb)
     || (!p_cb_info->pL2CA_DataInd_Cb)
     || (!p_cb_info->pL2CA_DisconnectInd_Cb))
    {
        L2CAP_TRACE_ERROR ("L2CAP - no cb registering PSM: 0x%04x", psm);
        return (0);
    }

    /* Verify PSM is valid */
    if (L2C_INVALID_PSM(psm))
    {
        L2CAP_TRACE_ERROR ("L2CAP - invalid PSM value, PSM: 0x%04x", psm);
        return (0);
    }

    /* Check if this is a registration for an outgoing-only connection to */
    /* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */
    if ( (psm >= 0x1001) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL) )
    {
        for (vpsm = 0x1002; vpsm < 0x8000; vpsm += 2)
        {
            if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL)
                break;
        }

        L2CAP_TRACE_API ("L2CA_Register - Real PSM: 0x%04x  Virtual PSM: 0x%04x", psm, vpsm);
    }

    /* If registration block already there, just overwrite it */
    if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL)
    {
        if ((p_rcb = l2cu_allocate_rcb (vpsm)) == NULL)
        {
            L2CAP_TRACE_WARNING ("L2CAP - no RCB available, PSM: 0x%04x  vPSM: 0x%04x", psm, vpsm);
            return (0);
        }
    }

    p_rcb->api      = *p_cb_info;
    p_rcb->real_psm = psm;

    return (vpsm);
}

 

我們看看hst_reg_info這個結構:

static const tL2CAP_APPL_INFO hst_reg_info =
{
    hidh_l2cif_connect_ind,
    hidh_l2cif_connect_cfm,
    NULL,
    hidh_l2cif_config_ind,
    hidh_l2cif_config_cfm,
    hidh_l2cif_disconnect_ind,
    hidh_l2cif_disconnect_cfm,
    NULL,
    hidh_l2cif_data_ind,
    hidh_l2cif_cong_ind,
    NULL                        /* tL2CA_TX_COMPLETE_CB */
}

 

現在我們知道disconnect_cfm = hidh_l2cif_disconnect_cfm 

/*******************************************************************************
**
** Function         hidh_l2cif_disconnect_cfm
**
** Description      This function handles a disconnect confirm event from L2CAP.
**
** Returns          void
**
*******************************************************************************/
static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result)
{
    UINT8 dhandle;
    tHID_CONN    *p_hcon = NULL;
    UNUSED(result);

    /* Find CCB based on CID */
    if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
        p_hcon = &hh_cb.devices[dhandle].conn;

    if (p_hcon == NULL)
    {
        HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid);
        return;
    }

    if (l2cap_cid == p_hcon->ctrl_cid)
        p_hcon->ctrl_cid = 0;
    else
    {
        p_hcon->intr_cid = 0;
        if (p_hcon->ctrl_cid)
        {
            L2CA_DisconnectReq (p_hcon->ctrl_cid);//控制通道沒有斷開,繼續斷開
        }
    }

    if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0))
    {
        hh_cb.devices[dhandle].state = HID_DEV_NO_CONN;
        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
        hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ;//調用回調上報事件
    }
}

接下來的斷開控制channel 和斷開中斷channel的流程是一樣的。這里就不贅述了,通道都斷開之后,link就會被斷開。
link全部斷開之后會調用hh_cb.callback向上匯報事情:這個回調函數就是bta_hh_cback,看看其做的事情:

/*****************************************************************************
**  Static Function
*****************************************************************************/
/*******************************************************************************
**
** Function         bta_hh_cback
**
** Description      BTA HH callback function.
**
**
** Returns          void
**
*******************************************************************************/
static void bta_hh_cback (UINT8 dev_handle, BD_ADDR addr, UINT8 event,
                        UINT32 data, BT_HDR *pdata)
{
    tBTA_HH_CBACK_DATA    *p_buf = NULL;
    UINT16  sm_event = BTA_HH_INVALID_EVT;
    UINT8   xx = 0;

#if BTA_HH_DEBUG
    APPL_TRACE_DEBUG("bta_hh_cback::HID_event [%s]", bta_hh_hid_event_name(event));
#endif

    switch (event)
    {
    case HID_HDEV_EVT_OPEN:
        sm_event = BTA_HH_INT_OPEN_EVT;
        break;
    case HID_HDEV_EVT_CLOSE:
        sm_event = BTA_HH_INT_CLOSE_EVT;
        break;
...
    }

    if (sm_event != BTA_HH_INVALID_EVT &&
        (p_buf = (tBTA_HH_CBACK_DATA *)GKI_getbuf(sizeof(tBTA_HH_CBACK_DATA) +
                    sizeof(BT_HDR))) != NULL)
    {
        p_buf->hdr.event  = sm_event;
        p_buf->hdr.layer_specific = (UINT16)dev_handle;
        p_buf->data       = data;
        bdcpy(p_buf->addr, addr);
        p_buf->p_data     = pdata;

        bta_sys_sendmsg(p_buf);//發送事件到bt_work_queue
    }

}

 

從代碼中可以看到這里發送的事件 是BTA_HH_INT_CLOSE_EVT:看看其處理:

HH的event 會交給bta_hh_hdl_event 來處理,然后根據事件類型路由到bta_hh_sm_execute,在這里處理的也很簡單,都是一樣的套路:

        state_table = bta_hh_st_tbl[p_cb->state - 1];//找到狀態轉換表
        event &= 0xff;//取出子事件
        p_cb->state = state_table[event][BTA_HH_NEXT_STATE] ;//設置下一個狀態
        if ((action = state_table[event][BTA_HH_ACTION]) != BTA_HH_IGNORE)
        {
            (*bta_hh_action[action])(p_cb, p_data);//執行action
        }

 

現在處於已經連接狀態:

const UINT8 bta_hh_st_connected[][BTA_HH_NUM_COLS] =
{
/* Event                          Action                 Next state */
/* BTA_HH_API_OPEN_EVT      */    {BTA_HH_IGNORE,        BTA_HH_CONN_ST    },
/* BTA_HH_API_CLOSE_EVT     */    {BTA_HH_API_DISC_ACT,  BTA_HH_CONN_ST    },
/* BTA_HH_INT_OPEN_EVT      */    {BTA_HH_OPEN_ACT,      BTA_HH_CONN_ST    },
/* BTA_HH_INT_CLOSE_EVT     */    {BTA_HH_CLOSE_ACT,     BTA_HH_IDLE_ST    },
/* BTA_HH_INT_DATA_EVT      */    {BTA_HH_DATA_ACT,      BTA_HH_CONN_ST    },

 我們發現下一個狀態是BTA_HH_IDLE_ST ,當前要執行的操作是BTA_HH_CLOSE_ACT:對應的行為是bta_hh_close_act

 

/*******************************************************************************
**
** Function         bta_hh_close_act
**
** Description      HID Host process a close event
**
**
** Returns          void
**
*******************************************************************************/
void bta_hh_close_act (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
{
    tBTA_HH_CONN            conn_dat ;
    tBTA_HH_CBDATA          disc_dat = {BTA_HH_OK, 0};
    UINT32                  reason = p_data->hid_cback.data;    /* Reason for closing (32-bit) */
    UINT16     event = p_cb->vp ? BTA_HH_VC_UNPLUG_EVT : BTA_HH_CLOSE_EVT;

    disc_dat.handle = p_cb->hid_handle;
    disc_dat.status = p_data->hid_cback.data;

    /* Check reason for closing */
    if ((reason & (HID_L2CAP_CONN_FAIL|HID_L2CAP_REQ_FAIL)) ||  /* Failure to initialize connection (page timeout or l2cap error) */
        (reason == HID_ERR_AUTH_FAILED) ||                      /* Authenication error (while initiating) */
        (reason == HID_ERR_L2CAP_FAILED))                       /* Failure creating l2cap connection */
    {
...
    }
    /* otherwise report CLOSE/VC_UNPLUG event */
    else
    {
        /* finaliza device driver */
        bta_hh_co_close(p_cb->hid_handle, p_cb->app_id);//close
        /* inform role manager */
        bta_sys_conn_close( BTA_ID_HH ,p_cb->app_id, p_cb->addr);
        /* update total conn number */
        bta_hh_cb.cnt_num --;

        if (disc_dat.status)
            disc_dat.status = BTA_HH_ERR;

        (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&disc_dat);
...
    }

    /* clean up control block, but retain SDP info and device handle */
    p_cb->vp            = FALSE;
    p_cb->w4_evt        = 0;

    /* if no connection is active and HH disable is signaled, disable service */
    if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable)
    {
        bta_hh_disc_cmpl();
    }
    return;
}

 

上面的過程主要做了 bta_hh_co_close 和 bta_hh_cb.p_cback 以及bta_hh_disc_cmpl()

bta_hh_co_close 主要做的是close 通信的節點,以及一些和driver 有關的事情,和結束poll線程的行為btif_hh_close_poll_thread

bta_hh_cb.p_cback 這個是一個回調函數,從這個函數的名字上來看,應該是應用層傳下來的callback,這邊搜索了一下代碼,是bte_hh_evt這個函數,他是在哪里注冊的呢?

bt_status_t btif_hh_execute_service(BOOLEAN b_enable)
{
     if (b_enable)
     {
          /* Enable and register with BTA-HH */
          BTA_HhEnable(BTUI_HH_SECURITY, bte_hh_evt);

 這個回調函數做的事情也就是 向上層匯報disable的事件:

/*******************************************************************************
**
** Function         bte_hh_evt
**
** Description      Switches context from BTE to BTIF for all HH events
**
** Returns          void
**
*******************************************************************************/

static void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH *p_data)
{
    bt_status_t status;
    int param_len = 0;

    if (BTA_HH_ENABLE_EVT == event)
        param_len = sizeof(tBTA_HH_STATUS);
    else if (BTA_HH_OPEN_EVT == event)
        param_len = sizeof(tBTA_HH_CONN);
    else if (BTA_HH_DISABLE_EVT == event)
        param_len = sizeof(tBTA_HH_STATUS);
    else if (BTA_HH_CLOSE_EVT == event)
        param_len = sizeof(tBTA_HH_CBDATA);
    else if (BTA_HH_GET_DSCP_EVT == event)
        param_len = sizeof(tBTA_HH_DEV_DSCP_INFO);
    else if ((BTA_HH_GET_PROTO_EVT == event) || (BTA_HH_GET_RPT_EVT == event)|| (BTA_HH_GET_IDLE_EVT == event))
        param_len = sizeof(tBTA_HH_HSDATA);
    else if ((BTA_HH_SET_PROTO_EVT == event) || (BTA_HH_SET_RPT_EVT == event) || (BTA_HH_VC_UNPLUG_EVT == event) || (BTA_HH_SET_IDLE_EVT == event))
        param_len = sizeof(tBTA_HH_CBDATA);
    else if ((BTA_HH_ADD_DEV_EVT == event) || (BTA_HH_RMV_DEV_EVT == event) )
        param_len = sizeof(tBTA_HH_DEV_INFO);
    else if (BTA_HH_API_ERR_EVT == event)
        param_len = 0;
    /* switch context to btif task context (copy full union size for convenience) */
    status = btif_transfer_context(btif_hh_upstreams_evt, (uint16_t)event, (void*)p_data, param_len, NULL);//通過btif_hh_upstreams_evt 上報

    /* catch any failed context transfers */
    ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
}

 

 簡單看下:

static void btif_hh_upstreams_evt(UINT16 event, char* p_param)
{
    tBTA_HH *p_data = (tBTA_HH *)p_param;
    btif_hh_device_t *p_dev = NULL;
    int i;
    int len, tmplen;

    switch (event)
    {
...
case BTA_HH_CLOSE_EVT:
            p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle);
            if (p_dev != NULL) {
                if(p_dev->vup_timer_active)
                    btif_hh_stop_vup_timer(&(p_dev->bd_addr));
                if (p_dev->fd >= 0) {
                    bta_hh_co_destroy(p_dev->fd);//銷毀節點
                    p_dev->fd = -1;
                }
                btif_hh_cb.status = BTIF_HH_DEV_DISCONNECTED;
                p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;
                HAL_CBACK(bt_hh_callbacks, connection_state_cb,&(p_dev->bd_addr), p_dev->dev_status);繼續向上匯報狀態
            }
...

 

這是定義在JNI層的函數com_android_bluetooth_hid.cpp;

static bthh_callbacks_t sBluetoothHidCallbacks = {
    sizeof(sBluetoothHidCallbacks),
    connection_state_callback,//會一直調用到java層
    NULL,
    get_protocol_mode_callback,
    NULL,
    get_report_callback,
    virtual_unplug_callback,
    handshake_callback
};

 

bta_hh_disc_cmpl()的  實現很簡單:

/*******************************************************************************
**
** Function         bta_hh_disc_cmpl
**
** Description      All connections have been closed, disable service.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hh_disc_cmpl(void)
{
    tBTA_HH_STATUS  status = BTA_HH_OK;

    /* Deregister with lower layer */
    if (HID_HostDeregister() != HID_SUCCESS)
        status = BTA_HH_ERR;

#if (BTA_HH_LE_INCLUDED == TRUE)
    bta_hh_le_deregister();
    UNUSED(status);
#else
    bta_hh_cleanup_disable(status);//會再次發送一個事件BTA_HH_DISABLE_EVT到btif_hh_upstreams_evt
 #endif }

 

做的事情就是清空各種結構體:

case BTA_HH_DISABLE_EVT:
            btif_hh_cb.status = BTIF_HH_DISABLED;
            if (p_data->status == BTA_HH_OK) {
                int i;
                //Clear the control block
                memset(&btif_hh_cb, 0, sizeof(btif_hh_cb));
                for (i = 0; i < BTIF_HH_MAX_HID; i++){
                    btif_hh_cb.devices[i].dev_status = BTHH_CONN_STATE_UNKNOWN;
                }
            }

 到此,關於hid 設備的在藍牙disable 過程中所走的流程就分析完了。

最后說一下 bta_dm_disable_conn_down_timer_cback  的執行時機,在disable 的初期,如果沒有藍牙link存在,那么會直接執行,如果有藍牙link,那么會將所有的藍牙link斷開,然后去執行。

當藍牙link 全部斷開,會有斷開完成事件上來,然后通過l2cu_release_lcb清除lcb,然后告訴BTM Acl management  這個link的已經被移除了(btm_acl_removed),接着通過btm_cb.p_bl_changed_cb = bta_dm_bl_change_cback 的回調,

/*******************************************************************************
**
** Function         bta_dm_bl_change_cback
**
** Description      Callback from btm when acl connection goes up or down
**
**
** Returns          void
**
*******************************************************************************/
static void bta_dm_bl_change_cback (tBTM_BL_EVENT_DATA *p_data)
{
    tBTA_DM_ACL_CHANGE * p_msg;

    if ((p_msg = (tBTA_DM_ACL_CHANGE *) GKI_getbuf(sizeof(tBTA_DM_ACL_CHANGE))) != NULL)
    {
        p_msg->event = p_data->event;
        p_msg->is_new = FALSE;//不是new

        switch(p_msg->event)
        {
        case BTM_BL_CONN_EVT:
            p_msg->is_new = TRUE;
            bdcpy(p_msg->bd_addr, p_data->conn.p_bda);
#if BLE_INCLUDED == TRUE
            p_msg->transport = p_data->conn.transport;
            p_msg->handle = p_data->conn.handle;
#endif
            break;
        case BTM_BL_DISCN_EVT:
            bdcpy(p_msg->bd_addr, p_data->discn.p_bda);
#if BLE_INCLUDED == TRUE
            p_msg->transport = p_data->discn.transport;
            p_msg->handle = p_data->discn.handle;
#endif
            break;
        case BTM_BL_UPDATE_EVT:
            p_msg->busy_level = p_data->update.busy_level;
            p_msg->busy_level_flags = p_data->update.busy_level_flags;
            break;
...
        }

        p_msg->hdr.event = BTA_DM_ACL_CHANGE_EVT;//發送event
        bta_sys_sendmsg(p_msg);

    }

}

 

最終會處理兩個事件BTM_BL_DISCN_EVT和BTM_BL_UPDATE_EVT,看該函數的注釋也可以看出,它會在link 狀態改變的時候被調用。最終會發送一個BTA_DM_ACL_CHANGE_EVT 到device manager

執行函數實現在bta_dm_act.c里面的bta_dm_acl_change ,

/*******************************************************************************
**
** Function         bta_dm_acl_change
**
** Description      Process BTA_DM_ACL_CHANGE_EVT
**
**
** Returns          void
**
*******************************************************************************/
void bta_dm_acl_change(tBTA_DM_MSG *p_data)
{

    UINT8 i;
    UINT8 *p;
    tBTA_DM_SEC conn;
    BOOLEAN is_new = p_data->acl_change.is_new;
    BD_ADDR_PTR     p_bda = p_data->acl_change.bd_addr;
    BOOLEAN         need_policy_change = FALSE;
    BOOLEAN         issue_unpair_cb = FALSE;

    tBTA_DM_PEER_DEVICE *p_dev;
    memset(&conn, 0, sizeof(tBTA_DM_SEC));
...
    if(is_new)
    {
...
    } else {
        for(i=0; i<bta_dm_cb.device_list.count; i++)
        {
            if (bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_bda)
#if BLE_INCLUDED == TRUE
                 ||bta_dm_cb.device_list.peer_device[i].transport != p_data->acl_change.transport
#endif
               )
                continue;

            if( bta_dm_cb.device_list.peer_device[i].conn_state == BTA_DM_UNPAIRING )//如果解配狀態,那么開始解配相關的操作
            {
                if (BTM_SecDeleteDevice(bta_dm_cb.device_list.peer_device[i].peer_bdaddr)){
                    issue_unpair_cb = TRUE;
                }
            }

            conn.link_down.is_removed = bta_dm_cb.device_list.peer_device[i].remove_dev_pending;

            for(; i<bta_dm_cb.device_list.count ; i++)
            {
                memcpy(&bta_dm_cb.device_list.peer_device[i], &bta_dm_cb.device_list.peer_device[i+1], sizeof(bta_dm_cb.device_list.peer_device[i]));
            }
            break;
        }
        if(bta_dm_cb.device_list.count)
            bta_dm_cb.device_list.count--;
#if BLE_INCLUDED == TRUE
        if ((p_data->acl_change.transport == BT_TRANSPORT_LE) &&
             (bta_dm_cb.device_list.le_count))
            bta_dm_cb.device_list.le_count--;
        conn.link_down.link_type = p_data->acl_change.transport;
#endif

        if(bta_dm_search_cb.wait_disc && !bdcmp(bta_dm_search_cb.peer_bdaddr, p_bda))
        {
            bta_dm_search_cb.wait_disc = FALSE;
            if(bta_dm_search_cb.sdp_results)
            {
                bta_sys_stop_timer(&bta_dm_search_cb.search_timer);
                bta_dm_discover_next_device();
            }

        }
if(bta_dm_cb.disabling) { if(!BTM_GetNumAclLinks()) { bta_sys_stop_timer(&bta_dm_cb.disable_timer); bta_dm_cb.disable_timer.p_cback = (TIMER_CBACK*)&bta_dm_disable_conn_down_timer_cback;//如果沒有link,那么1s之后執行回調 /* * Start a timer to make sure that the profiles * get the disconnect event. */ bta_sys_start_timer(&bta_dm_cb.disable_timer, 0, 1000); } } if (conn.link_down.is_removed) { BTM_SecDeleteDevice(p_bda); #if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE) /* need to remove all pending background connection */ BTA_GATTC_CancelOpen(0, p_bda, FALSE); /* remove all cached GATT information */ BTA_GATTC_Refresh(p_bda); #endif } bdcpy(conn.link_down.bd_addr, p_bda); conn.link_down.status = (UINT8) btm_get_acl_disc_reason_code(); if( bta_dm_cb.p_sec_cback ) { bta_dm_cb.p_sec_cback(BTA_DM_LINK_DOWN_EVT, &conn);//向上層匯報狀態 if( issue_unpair_cb ) bta_dm_cb.p_sec_cback(BTA_DM_DEV_UNPAIRED_EVT, &conn); } } bta_dm_adjust_roles(TRUE); }

 

這里需要分析的點有處:

1.bta_dm_disable_conn_down_timer_cback

2.bta_dm_cb.p_sec_cback = bte_dm_evt(BTA_DM_LINK_DOWN_EVT, &conn)  的執行流程

首先看看.bta_dm_disable_conn_down_timer_cback 的執行

/*******************************************************************************
**
** Function         bta_dm_disable_conn_down_timer_cback
**
** Description      Sends disable event to application
**
**
** Returns          void
**
*******************************************************************************/
static void bta_dm_disable_conn_down_timer_cback (TIMER_LIST_ENT *p_tle)
{
    UNUSED(p_tle);
    tBTA_SYS_HW_MSG *sys_enable_event;

    /* disable the power managment module */
    bta_dm_disable_pm();

    /* register our callback to SYS HW manager */
    bta_sys_hw_register( BTA_SYS_HW_BLUETOOTH, bta_dm_sys_hw_cback );//注冊callback

    /* send a message to BTA SYS */
    if ((sys_enable_event = (tBTA_SYS_HW_MSG *) GKI_getbuf(sizeof(tBTA_SYS_HW_MSG))) != NULL)
    {
        sys_enable_event->hdr.event = BTA_SYS_API_DISABLE_EVT;//發送系統管理模塊disable的事件
        sys_enable_event->hw_module = BTA_SYS_HW_BLUETOOTH;
        bta_sys_sendmsg(sys_enable_event);
    }

    bta_dm_cb.disabling = FALSE;

}

 

看看這個 disable event 在 系統管理模塊中是怎么處理的:

BOOLEAN bta_sys_sm_execute(BT_HDR *p_msg)
{
    BOOLEAN freebuf = TRUE;
    tBTA_SYS_ST_TBL      state_table;
    UINT8               action;
    int                 i;

    /* look up the state table for the current state */
    state_table = bta_sys_st_tbl[bta_sys_cb.state];
    /* update state */
    bta_sys_cb.state = state_table[p_msg->event & 0x00ff][BTA_SYS_NEXT_STATE];
    /* execute action functions */
    for (i = 0; i < BTA_SYS_ACTIONS; i++)
    {
        if ((action = state_table[p_msg->event & 0x00ff][i]) != BTA_SYS_IGNORE)
        {
            (*bta_sys_action[action])( (tBTA_SYS_HW_MSG*) p_msg);
        }
        else
        {
            break;
        }
    }
    return freebuf;

}

 

這里發現 下一個狀態還是BTA_SYS_HW_ON ,執行的動作是BTA_SYS_HW_API_DISABLE,執行的函數是bta_sys_hw_api_disable ,這里發現它的先一個狀態不少stopped,說明還有事件要交互,先看disable的動作:

/*******************************************************************************
**
** Function         bta_sys_hw_disable
**
** Description     if no other module is using the HW, this function will call ( if defined ) a user-macro to turn off the HW
**
**
** Returns          success or failure
**
*******************************************************************************/
void bta_sys_hw_api_disable(tBTA_SYS_HW_MSG *p_sys_hw_msg)
{
    /* make sure the related SW blocks were stopped */
    bta_sys_disable( p_sys_hw_msg->hw_module );//再次確認已經關閉子模塊

    /* register which module we turn off */
    bta_sys_cb.sys_hw_module_active &=  ~((UINT32)1 << p_sys_hw_msg->hw_module );//清除標志

    /* if there are still some SW modules using the HW, just provide an answer to the calling */
    if( bta_sys_cb.sys_hw_module_active != 0  )
    {
        /*  if there are still some SW modules using the HW,  directly notify the caller */
        if( bta_sys_cb.sys_hw_cback[p_sys_hw_msg->hw_module ]!= NULL )
            bta_sys_cb.sys_hw_cback[p_sys_hw_msg->hw_module ](  BTA_SYS_HW_OFF_EVT   );
    }
    else
    {
        /* manually update the state of our system */
        bta_sys_cb.state = BTA_SYS_HW_STOPPING;//設置狀態

        tBTA_SYS_HW_MSG *p_msg;
        if ((p_msg = (tBTA_SYS_HW_MSG *) GKI_getbuf(sizeof(tBTA_SYS_HW_MSG))) != NULL)
        {
            p_msg->hdr.event = BTA_SYS_EVT_DISABLED_EVT;//繼續發送disabled的消息
            p_msg->hw_module = p_sys_hw_msg->hw_module;
            bta_sys_sendmsg(p_msg);
        }
    }

}

當前的狀態已經是 bta_sys_cb.state = BTA_SYS_HW_STOPPING;,其狀態轉換圖也不同:

const UINT8 bta_sys_hw_stopping[][BTA_SYS_NUM_COLS] =
{
/* Event                    Action 1                   Action 2               Next State */
/* API_ENABLE    */  {BTA_SYS_IGNORE,               BTA_SYS_IGNORE,         BTA_SYS_HW_STARTING}, /* change state, and wait for completion event to enable */
/* EVT_ENABLED   */  {BTA_SYS_HW_EVT_ENABLED,       BTA_SYS_IGNORE,         BTA_SYS_HW_STOPPING}, /* successive enable/disable: finish the enable before disabling */
/* STACK_ENABLED */  {BTA_SYS_HW_EVT_STACK_ENABLED, BTA_SYS_HW_API_DISABLE, BTA_SYS_HW_STOPPING}, /* successive enable/disable: notify, then stop */
/* API_DISABLE   */  {BTA_SYS_IGNORE,               BTA_SYS_IGNORE,         BTA_SYS_HW_STOPPING}, /* wait for completion event */
/* EVT_DISABLED  */  {BTA_SYS_HW_EVT_DISABLED,      BTA_SYS_IGNORE,         BTA_SYS_HW_OFF},
/* EVT_ERROR     */  {BTA_SYS_HW_API_DISABLE,       BTA_SYS_IGNORE,         BTA_SYS_HW_STOPPING}
};

 

可知下一個狀態是BTA_SYS_HW_OFF,另外執行的action是BTA_SYS_HW_EVT_DISABLED,執行的函數 是

void bta_sys_hw_evt_disabled(tBTA_SYS_HW_MSG *p_sys_hw_msg)
{
    UINT8 hw_module_index;

    for (hw_module_index = 0; hw_module_index < BTA_SYS_MAX_HW_MODULES; hw_module_index++)
    {
        if (bta_sys_cb.sys_hw_cback[hw_module_index] != NULL)
            bta_sys_cb.sys_hw_cback[hw_module_index] (BTA_SYS_HW_OFF_EVT);
    }
}

 

這里執行之前注冊到HW manager的函數 bta_sys_hw_register( BTA_SYS_HW_BLUETOOTH, bta_dm_sys_hw_cback ),看其實現:

/*******************************************************************************
**
** Function         bta_dm_sys_hw_cback
**
** Description     callback register to SYS to get HW status updates
**
**
** Returns          void
**
*******************************************************************************/
static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status )
{
    DEV_CLASS   dev_class;
    tBTA_DM_SEC_CBACK           *temp_cback;
#if BLE_INCLUDED == TRUE
    UINT8                   key_mask = 0;
    BT_OCTET16              er;
    tBTA_BLE_LOCAL_ID_KEYS  id_key;
#endif
...
    if( status == BTA_SYS_HW_OFF_EVT )
    {
        if( bta_dm_cb.p_sec_cback != NULL )
            bta_dm_cb.p_sec_cback(BTA_DM_DISABLE_EVT, NULL);//繼續向上層匯報事件

        /* reinitialize the control block */
        memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));

        /* unregister from SYS */
        bta_sys_hw_unregister( BTA_SYS_HW_BLUETOOTH );
        /* notify BTA DM is now unactive */
        bta_dm_cb.is_bta_dm_active = FALSE;
    }
...

 

這邊主要分析bta_dm_cb.p_sec_cback 的兩個事件 BTA_DM_LINK_DOWN_EVT和BTA_DM_DISABLE_EVT 

static void btif_dm_upstreams_evt(UINT16 event, char* p_param)
{
    tBTA_DM_SEC *p_data = (tBTA_DM_SEC*)p_param;
    tBTA_SERVICE_MASK service_mask;
    uint32_t i;
    bt_bdaddr_t bd_addr;

    switch (event)
    {
...
        case BTA_DM_DISABLE_EVT:
            /* for each of the enabled services in the mask, trigger the profile
             * disable */
            service_mask = btif_get_enabled_services_mask();
            for (i=0; i <= BTA_MAX_SERVICE_ID; i++)
            {
                if (service_mask &
                    (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(i)))
                {
                    btif_in_execute_service_request(i, FALSE);//關閉沒有關閉的服務
                }
            }
            btif_disable_bluetooth_evt();//繼續向上匯報狀態
            break;
...
        case BTA_DM_BUSY_LEVEL_EVT:
        {

            if (p_data->busy_level.level_flags & BTM_BL_INQUIRY_PAGING_MASK)
            {
                if (p_data->busy_level.level_flags == BTM_BL_INQUIRY_STARTED)
                {
                       HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb,
                                                BT_DISCOVERY_STARTED);
                       btif_dm_inquiry_in_progress = TRUE;
                }
                else if (p_data->busy_level.level_flags == BTM_BL_INQUIRY_CANCELLED)
                {
                       HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb,
                                                BT_DISCOVERY_STOPPED);
                       btif_dm_inquiry_in_progress = FALSE;
                }
                else if (p_data->busy_level.level_flags == BTM_BL_INQUIRY_COMPLETE)
                {
                       btif_dm_inquiry_in_progress = FALSE;
                }
            }
        }break;
...
        case BTA_DM_LINK_DOWN_EVT:
            bdcpy(bd_addr.address, p_data->link_down.bd_addr);
            btm_set_bond_type_dev(p_data->link_down.bd_addr, BOND_TYPE_UNKNOWN);
            BTIF_TRACE_DEBUG("BTA_DM_LINK_DOWN_EVT. Sending BT_ACL_STATE_DISCONNECTED");
            HAL_CBACK(bt_hal_cbacks, acl_state_changed_cb, BT_STATUS_SUCCESS,
                      &bd_addr, BT_ACL_STATE_DISCONNECTED);
            break;
...

 

這里簡單分析下btif_disable_bluetooth_evt:

/*******************************************************************************
**
** Function         btif_disable_bluetooth_evt
**
** Description      Event notifying BT disable is now complete.
**                  Terminates main stack tasks and notifies HAL
**                  user with updated BT state.
**
** Returns          void
**
*******************************************************************************/

void btif_disable_bluetooth_evt(void)
{
#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE)
    bte_main_enable_lpm(FALSE);
#endif

#if (BLE_INCLUDED == TRUE)
     BTA_VendorCleanup();
#endif
     bte_main_disable();//關閉hci模塊和btsnoop模塊,關閉BTU,
    /* callback to HAL */
    future_ready(stack_manager_get_hack_future(), FUTURE_SUCCESS);//發送信號量
}

 

還記得在文章的開頭event_shut_down_stack 里面會等待一個信號量,這里,這個信號量終於發出來了,那么event_shut_down_stack這個函數也得以繼續執行:

static void event_shut_down_stack(UNUSED_ATTR void *context) {
...
  hack_future = future_new();
  stack_is_running = false;

  btif_disable_bluetooth();//文章從這里分析
  module_shut_down(get_module(BTIF_CONFIG_MODULE));

  future_await(hack_future);//獲得信號量繼續執行
  module_shut_down(get_module(CONTROLLER_MODULE)); // Doesn't do any work, just puts it in a restartable state

  btif_thread_post(event_signal_stack_down, NULL);
}

 

static void event_signal_stack_down(UNUSED_ATTR void *context) {
  HAL_CBACK(bt_hal_cbacks, adapter_state_changed_cb, BT_STATE_OFF);
}

 最后的這個函數 就是往上層匯報BT_STATE_OFF的狀態的。

關於藍牙disable的流程就先分析到這里。

最后說一點,藍牙關閉的流程還包括各個profile的關閉,這里只是介紹了協議棧對於各個模塊的關閉流程。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM