一、 基礎知識介紹
1.縮略語
BTIF: Bluetooth Interface
BTU : Bluetooth Upper Layer
BTM: Bluetooth Manager
BTE: Bluetooth embedded system
BTA :Blueetooth application layer
CO: call out\CI: call in
HF : Handsfree Profile
HH: HID Host Profile
HL: Health Device Profile
V:audio\vidio
ag: audio gateway
r: audio/video registration
gattc: GATT client
BLE: Bluetooth Low Energy
2.藍牙協議棧框架圖:
1.基帶層(BB)提供了兩種不同的物理鏈路(同步面向連接鏈路SCO Synchronous Connection Oriented和異步無連接鏈路ACL Asynchronous Connection Less),負責跳頻和藍牙數據及信息幀的傳輸,且對所有類型的數據包提供了不同層次的前向糾錯碼(FEC Frequency Error Correction)或循環沉余度差錯校驗(CTC Cyclic Redundancy Check);
2.LMP層負責兩個或多個設備鏈路的建立和拆除及鏈路的安全和控制,如鑒權和加密、控制和協商基帶包的大小等,它為上層軟件模塊提供了不同的訪問入口;
3.藍牙主機控制器接口HCI (Host Controller Interface)由基帶控制器、連接管理器、控制和事件寄存器等組成。它是藍牙協議中軟硬件之間的接口,它提供了一個調用下層BB、LM、狀態和控制寄存器等硬件的統一命令,上、下兩個模塊接口之間的消息和數據的傳遞必須通過HCI的解釋才能進行。HCI層以上的協議軟件實體運行在主機上,而HCI以下的功能由藍牙設備耒完成,二者之間通過傳輸層進行交互。
4.中間協議層由邏輯鏈路控制與適配協議L2CAP (Logical Link Control and Adaptation Protocol)、服務發現協議 SDP (Service Discovery Protocol)、串口仿真協議或稱線纜替換協議 RFCOM 和二進制電話控制協議 TCS (Telephony Control protocol Spectocol)組成。
L2CAP 是藍牙協議棧的核心組成部分,也是其它協議實現的基礎。它位於基帶之上,向上層提供面向連接的和無連接的數據服務。它主要完成數據的拆裝、服務質量控制,協議的復用、分組的分割和重組(Segmentation And Reassembly)及組提取等功能。L2CAP允許高達64KB的數據分組。
5.SDP是一個基於客戶/服務器結構的協議。它工作在 L2CAP層之上,為上層應用程序提供一種機制來發現可用的服務及其屬性,而服務的屬性包括服務的類型及該服務所需的機制或協議信息。
6.RFCOMM 是一個仿真有線鏈路的無線數據仿真協議,符合ETSI 標准的 TS 07.10串口仿真協議。它在藍牙基帶上仿真RS-232的控制和數據信號,為原先使用串行連接的上層業務提供傳送能力。
7.TCS是一個基於 ITU-T Q.931 建議的采用面向比特的協議,它定義了用於藍牙設備之間建立語音和數據呼叫的控制信令(Call Control Signalling),並負責處理藍牙設備組的移動管理過程。
整個bluedroid可以分為兩大模塊:BTIF,BTE
BTIF:提供bluedroid對外的接口
BTE:bluedroid的內部處理,又細分為BTA,BTU,BTM和HCI
BTA:bluedroid中各profile的邏輯實現和處理
BTU:承接BTA與HCI
BTM:藍牙配對與鏈路管理
HCI:讀取或寫入數據到藍牙hw
二、代碼分析(寫hidraw節點數據流程):
1.初始化:
external\bluetooth\bluedroid\btif\src\bluetooth.c
static const bt_interface_t bluetoothInterface = { sizeof(bluetoothInterface), init, enable, disable, cleanup, get_adapter_properties, get_adapter_property, set_adapter_property, get_remote_device_properties, get_remote_device_property, set_remote_device_property, get_remote_service_record, get_remote_services, start_discovery, cancel_discovery, create_bond, remove_bond, cancel_bond, get_connection_state, pin_reply, ssp_reply, get_profile_interface, //根據profile獲得對應的接口 dut_mode_configure, dut_mode_send, #if BLE_INCLUDED == TRUE le_test_mode, #else NULL, #endif config_hci_snoop_log, set_os_callouts, read_energy_info, };
......
if (is_profile(profile_id, BT_PROFILE_HIDHOST_ID))
return btif_hh_get_interface(); //獲得HID Host Profile
external\bluetooth\bluedroid\btif\src\btif_hh.c
static const bthh_interface_t bthhInterface = { sizeof(bthhInterface), init, connect, disconnect, virtual_unplug, set_info, get_protocol, set_protocol, // get_idle_time, // set_idle_time, get_report, set_report, send_data, cleanup, };
init函數里注冊傳入的回調函數:
/******************************************************************************* ** ** Function btif_hh_init ** ** Description initializes the hh interface ** ** Returns bt_status_t ** *******************************************************************************/ static bt_status_t init( bthh_callbacks_t* callbacks ) { UINT32 i; BTIF_TRACE_EVENT("%s", __FUNCTION__); bt_hh_callbacks = callbacks; 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; } /* Invoke the enable service API to the core to set the appropriate service_id */ btif_enable_service(BTA_HID_SERVICE_ID); return BT_STATUS_SUCCESS; }
external\bluetooth\bluedroid\btif\src\btif_core.c
/******************************************************************************* ** ** Function btif_enable_service ** ** Description Enables the service 'service_ID' to the service_mask. ** Upon BT enable, BTIF core shall invoke the BTA APIs to ** enable the profiles ** ** Returns bt_status_t ** *******************************************************************************/ bt_status_t btif_enable_service(tBTA_SERVICE_ID service_id) { tBTA_SERVICE_ID *p_id = &service_id; /* If BT is enabled, we need to switch to BTIF context and trigger the * enable for that profile * * Otherwise, we just set the flag. On BT_Enable, the DM will trigger * enable for the profiles that have been enabled */ btif_enabled_services |= (1 << service_id); BTIF_TRACE_DEBUG("%s: current services:0x%x", __FUNCTION__, btif_enabled_services); if (btif_is_enabled()) { //注冊回調,發送消息 btif_transfer_context(btif_dm_execute_service_request, BTIF_DM_ENABLE_SERVICE, (char*)p_id, sizeof(tBTA_SERVICE_ID), NULL); } return BT_STATUS_SUCCESS; }
2.創建線程和准備啟動調度:
/******************************************************************************* ** ** Function btif_init_bluetooth ** ** Description Creates BTIF task and prepares BT scheduler for startup ** ** Returns bt_status_t ** *******************************************************************************/ bt_status_t btif_init_bluetooth() { UINT8 status; btif_config_init(); bte_main_boot_entry(); /* As part of the init, fetch the local BD ADDR */ memset(&btif_local_bd_addr, 0, sizeof(bt_bdaddr_t)); btif_fetch_local_bdaddr(&btif_local_bd_addr); /* start btif task */ status = GKI_create_task(btif_task, BTIF_TASK, BTIF_TASK_STR, (UINT16 *) ((UINT8 *)btif_task_stack + BTIF_TASK_STACK_SIZE), sizeof(btif_task_stack)); if (status != GKI_SUCCESS) return BT_STATUS_FAIL; return BT_STATUS_SUCCESS; }
處理線程函數:
/******************************************************************************* ** ** Function btif_task ** ** Description BTIF task handler managing all messages being passed ** Bluetooth HAL and BTA. ** ** Returns void ** *******************************************************************************/ static void btif_task(UINT32 params) { UINT16 event; BT_HDR *p_msg; UNUSED(params); BTIF_TRACE_DEBUG("btif task starting"); btif_associate_evt(); for(;;) { /* wait for specified events */ event = GKI_wait(0xFFFF, 0); /* * Wait for the trigger to init chip and stack. This trigger will * be received by btu_task once the UART is opened and ready */ if (event == BT_EVT_TRIGGER_STACK_INIT) { BTIF_TRACE_DEBUG("btif_task: received trigger stack init event"); #if (BLE_INCLUDED == TRUE) btif_dm_load_ble_local_keys(); #endif BTA_EnableBluetooth(bte_dm_evt); } /* * Failed to initialize controller hardware, reset state and bring * down all threads */ if (event == BT_EVT_HARDWARE_INIT_FAIL) { BTIF_TRACE_DEBUG("btif_task: hardware init failed"); bte_main_disable(); btif_queue_release(); GKI_task_self_cleanup(BTIF_TASK); bte_main_shutdown(); btif_dut_mode = 0; btif_core_state = BTIF_CORE_STATE_DISABLED; HAL_CBACK(bt_hal_cbacks,adapter_state_changed_cb,BT_STATE_OFF); break; } if (event & EVENT_MASK(GKI_SHUTDOWN_EVT)) break; if(event & TASK_MBOX_1_EVT_MASK) { while((p_msg = GKI_read_mbox(BTU_BTIF_MBOX)) != NULL) //讀取消息 { BTIF_TRACE_VERBOSE("btif task fetched event %x", p_msg->event); switch (p_msg->event) { case BT_EVT_CONTEXT_SWITCH_EVT: btif_context_switched(p_msg); //傳遞消息給注冊的回調函數 break; default: BTIF_TRACE_ERROR("unhandled btif event (%d)", p_msg->event & BT_EVT_MASK); break; } GKI_freebuf(p_msg); } } } btif_disassociate_evt(); BTIF_TRACE_DEBUG("btif task exiting"); }
之前先開啟了藍牙服務:
/******************************************************************************* ** ** 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) { tBTA_DM_API_ENABLE *p_msg; /* Bluetooth disabling is in progress */ if (bta_dm_cb.disabling) return BTA_FAILURE; memset(&bta_dm_cb, 0, sizeof(bta_dm_cb)); bta_sys_register (BTA_ID_DM, &bta_dm_reg ); bta_sys_register (BTA_ID_DM_SEARCH, &bta_dm_search_reg ); /* if UUID list is not provided as static data */ bta_sys_eir_register(bta_dm_eir_update_uuid); if ((p_msg = (tBTA_DM_API_ENABLE *) GKI_getbuf(sizeof(tBTA_DM_API_ENABLE))) != NULL) { p_msg->hdr.event = BTA_DM_API_ENABLE_EVT; p_msg->p_sec_cback = p_cback; bta_sys_sendmsg(p_msg); return BTA_SUCCESS; } return BTA_FAILURE; }
external\bluetooth\bluedroid\btif\src\btif_dm.c
根據消息請求對應服務:
void btif_dm_execute_service_request(UINT16 event, char *p_param) { BOOLEAN b_enable = FALSE; bt_status_t status; if (event == BTIF_DM_ENABLE_SERVICE) { b_enable = TRUE; } status = btif_in_execute_service_request(*((tBTA_SERVICE_ID*)p_param), b_enable); //執行服務請求 if (status == BT_STATUS_SUCCESS) { bt_property_t property; bt_uuid_t local_uuids[BT_MAX_NUM_UUIDS]; /* Now send the UUID_PROPERTY_CHANGED event to the upper layer */ BTIF_STORAGE_FILL_PROPERTY(&property, BT_PROPERTY_UUIDS, sizeof(local_uuids), local_uuids); btif_storage_get_adapter_property(&property); HAL_CBACK(bt_hal_cbacks, adapter_properties_cb, BT_STATUS_SUCCESS, 1, &property); } return; }
執行A2DP/HID/HFP等服務
bt_status_t btif_in_execute_service_request(tBTA_SERVICE_ID service_id, BOOLEAN b_enable) { /* Check the service_ID and invoke the profile's BT state changed API */ switch (service_id) { case BTA_HFP_SERVICE_ID: case BTA_HSP_SERVICE_ID: { btif_hf_execute_service(b_enable); }break; case BTA_A2DP_SERVICE_ID: { btif_av_execute_service(b_enable); }break; case BTA_HID_SERVICE_ID: { btif_hh_execute_service(b_enable); }break; case BTA_HFP_HS_SERVICE_ID: { btif_hf_client_execute_service(b_enable); }break; case BTA_MAP_SERVICE_ID: { btif_mce_execute_service(b_enable); }break; default: BTIF_TRACE_ERROR("%s: Unknown service being enabled", __FUNCTION__); return BT_STATUS_FAIL; } return BT_STATUS_SUCCESS; }
external\bluetooth\bluedroid\btif\src\btif_hh.c
啟動/關閉 HID服務
/******************************************************************************* ** ** Function btif_hh_execute_service ** ** Description Initializes/Shuts down the service ** ** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise ** *******************************************************************************/ bt_status_t btif_hh_execute_service(BOOLEAN b_enable) { if (b_enable) { /* Enable and register with BTA-HH */ BTA_HhEnable(BTA_SEC_ENCRYPT, bte_hh_evt); } else { /* Disable HH */ BTA_HhDisable(); } return BT_STATUS_SUCCESS; }
external\bluetooth\bluedroid\bta\hh\bta_hh_api.c
/******************************************************************************* ** ** 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); //注冊主處理函數 APPL_TRACE_ERROR("Calling BTA_HhEnable"); p_buf = (tBTA_HH_API_ENABLE *)GKI_getbuf((UINT16)sizeof(tBTA_HH_API_ENABLE)); if (p_buf != NULL) { memset(p_buf, 0, sizeof(tBTA_HH_API_ENABLE)); p_buf->hdr.event = BTA_HH_API_ENABLE_EVT; p_buf->p_cback = p_cback; p_buf->sec_mask = sec_mask; bta_sys_sendmsg(p_buf); } }
external\bluetooth\bluedroid\btif\co\bta_hh_co.c
HID Host Profile 部分初始化,創建HID事件監聽線程:btif_hh_poll_event_thread
/******************************************************************************* ** ** Function bta_hh_co_open ** ** Description When connection is opened, this call-out function is executed ** by HH to do platform specific initialization. ** ** Returns void. *******************************************************************************/ void bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class, tBTA_HH_ATTR_MASK attr_mask, UINT8 app_id) { UINT32 i; btif_hh_device_t *p_dev = NULL; if (dev_handle == BTA_HH_INVALID_HANDLE) { APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __FUNCTION__, dev_handle); return; } for (i = 0; i < BTIF_HH_MAX_HID; i++) { p_dev = &btif_hh_cb.devices[i]; if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN && p_dev->dev_handle == dev_handle) { // We found a device with the same handle. Must be a device reconnected. APPL_TRACE_WARNING("%s: Found an existing device with the same handle " "dev_status = %d",__FUNCTION__, p_dev->dev_status); APPL_TRACE_WARNING("%s: bd_addr = [%02X:%02X:%02X:%02X:%02X:]", __FUNCTION__, p_dev->bd_addr.address[0], p_dev->bd_addr.address[1], p_dev->bd_addr.address[2], p_dev->bd_addr.address[3], p_dev->bd_addr.address[4]); APPL_TRACE_WARNING("%s: attr_mask = 0x%04x, sub_class = 0x%02x, app_id = %d", __FUNCTION__, p_dev->attr_mask, p_dev->sub_class, p_dev->app_id); if(p_dev->fd<0) { p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC); if (p_dev->fd < 0){ APPL_TRACE_ERROR("%s: Error: failed to open uhid, err:%s", __FUNCTION__,strerror(errno)); }else APPL_TRACE_DEBUG("%s: uhid fd = %d", __FUNCTION__, p_dev->fd); } p_dev->hh_keep_polling = 1; p_dev->hh_poll_thread_id = create_thread(btif_hh_poll_event_thread, p_dev); break; } p_dev = NULL; } if (p_dev == NULL) { // Did not find a device reconnection case. Find an empty slot now. for (i = 0; i < BTIF_HH_MAX_HID; i++) { if (btif_hh_cb.devices[i].dev_status == BTHH_CONN_STATE_UNKNOWN) { p_dev = &btif_hh_cb.devices[i]; p_dev->dev_handle = dev_handle; p_dev->attr_mask = attr_mask; p_dev->sub_class = sub_class; p_dev->app_id = app_id; p_dev->local_vup = FALSE; btif_hh_cb.device_num++; // This is a new device,open the uhid driver now. p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC); if (p_dev->fd < 0){ APPL_TRACE_ERROR("%s: Error: failed to open uhid, err:%s", __FUNCTION__,strerror(errno)); }else{ APPL_TRACE_DEBUG("%s: uhid fd = %d", __FUNCTION__, p_dev->fd); p_dev->hh_keep_polling = 1; p_dev->hh_poll_thread_id = create_thread(btif_hh_poll_event_thread, p_dev); } break; } } } if (p_dev == NULL) { APPL_TRACE_ERROR("%s: Error: too many HID devices are connected", __FUNCTION__); return; } p_dev->dev_status = BTHH_CONN_STATE_CONNECTED; APPL_TRACE_DEBUG("%s: Return device status %d", __FUNCTION__, p_dev->dev_status); }
poll監聽HID驅動的事件:
/******************************************************************************* ** ** Function btif_hh_poll_event_thread ** ** Description the polling thread which polls for event from UHID driver ** ** Returns void ** *******************************************************************************/ static void *btif_hh_poll_event_thread(void *arg) { btif_hh_device_t *p_dev = arg; APPL_TRACE_DEBUG("%s: Thread created fd = %d", __FUNCTION__, p_dev->fd); struct pollfd pfds[1]; int ret; pfds[0].fd = p_dev->fd; pfds[0].events = POLLIN; while(p_dev->hh_keep_polling){ ret = poll(pfds, 1, 50); if (ret < 0) { APPL_TRACE_ERROR("%s: Cannot poll for fds: %s\n", __FUNCTION__, strerror(errno)); break; } if (pfds[0].revents & POLLIN) { APPL_TRACE_DEBUG("btif_hh_poll_event_thread: POLLIN"); ret = uhid_event(p_dev); if (ret){ break; } } } p_dev->hh_poll_thread_id = -1; return 0; }
解析HID驅動的事件:
/* Internal function to parse the events received from UHID driver*/ static int uhid_event(btif_hh_device_t *p_dev) { struct uhid_event ev; ssize_t ret; memset(&ev, 0, sizeof(ev)); if(!p_dev) { APPL_TRACE_ERROR("%s: Device not found",__FUNCTION__) return -1; } ret = read(p_dev->fd, &ev, sizeof(ev)); if (ret == 0) { APPL_TRACE_ERROR("%s: Read HUP on uhid-cdev %s", __FUNCTION__, strerror(errno)); return -EFAULT; } else if (ret < 0) { APPL_TRACE_ERROR("%s:Cannot read uhid-cdev: %s", __FUNCTION__, strerror(errno)); return -errno; } else if (ret != sizeof(ev)) { APPL_TRACE_ERROR("%s:Invalid size read from uhid-dev: %ld != %lu", __FUNCTION__, ret, sizeof(ev)); return -EFAULT; } switch (ev.type) { case UHID_START: APPL_TRACE_DEBUG("UHID_START from uhid-dev\n"); break; case UHID_STOP: APPL_TRACE_DEBUG("UHID_STOP from uhid-dev\n"); break; case UHID_OPEN: APPL_TRACE_DEBUG("UHID_OPEN from uhid-dev\n"); break; case UHID_CLOSE: APPL_TRACE_DEBUG("UHID_CLOSE from uhid-dev\n"); break; case UHID_OUTPUT: APPL_TRACE_DEBUG("UHID_OUTPUT: Report type = %d, report_size = %d" ,ev.u.output.rtype, ev.u.output.size); //Send SET_REPORT with feature report if the report type in output event is FEATURE if(ev.u.output.rtype == UHID_FEATURE_REPORT) btif_hh_setreport(p_dev,BTHH_FEATURE_REPORT,ev.u.output.size,ev.u.output.data); else if(ev.u.output.rtype == UHID_OUTPUT_REPORT) btif_hh_setreport(p_dev,BTHH_OUTPUT_REPORT,ev.u.output.size,ev.u.output.data); else btif_hh_setreport(p_dev,BTHH_INPUT_REPORT,ev.u.output.size,ev.u.output.data); break; case UHID_OUTPUT_EV: APPL_TRACE_DEBUG("UHID_OUTPUT_EV from uhid-dev\n"); break; case UHID_FEATURE: APPL_TRACE_DEBUG("UHID_FEATURE from uhid-dev\n"); break; case UHID_FEATURE_ANSWER: APPL_TRACE_DEBUG("UHID_FEATURE_ANSWER from uhid-dev\n"); break; default: APPL_TRACE_DEBUG("Invalid event from uhid-dev: %u\n", ev.type); } return 0; }
--->
/******************************************************************************* ** ** Function btif_btif_hh_setreport ** ** Description setreport initiated from the BTIF thread context ** ** Returns void ** *******************************************************************************/ #define COMMAND_PATCH void btif_hh_setreport(btif_hh_device_t *p_dev, bthh_report_type_t r_type, UINT16 size, UINT8* report) { BT_HDR* p_buf = create_pbuf(size, report); if (p_buf == NULL) { APPL_TRACE_ERROR("%s: Error, failed to allocate RPT buffer, size = %d", __FUNCTION__, size); return; } #ifdef COMMAND_PATCH if(report[0] != 0x5B) /*判斷report id!=0x5B,執行默認的request,需要response*/ BTA_HhSetReport(p_dev->dev_handle, r_type, p_buf); else{ /*判斷report id==0x5B,發送command,不需要response*/ BD_ADDR* bda = (BD_ADDR*)&p_dev->bd_addr; BTIF_TRACE_DEBUG("Send Command Size %",size); p_buf->layer_specific = BTA_HH_RPTT_OUTPUT; BTA_HhSendData(p_dev->dev_handle,*bda,p_buf); } #else BTA_HhSetReport(p_dev->dev_handle, r_type, p_buf); #endif }
發送數據到HID設備:
/******************************************************************************* ** ** Function BTA_HhSendData ** ** Description This function send DATA transaction to HID device. ** ** Parameter dev_handle: device handle ** dev_bda: remote device address ** p_data: data to be sent in the DATA transaction; or ** the data to be write into the Output Report of a LE HID ** device. The report is identified the report ID which is ** the value of the byte (UINT8 *)(p_buf + 1) + p_buf->offset. ** p_data->layer_specific needs to be set to the report type, ** it can be OUTPUT report, or FEATURE report. ** ** Returns void ** *******************************************************************************/ void BTA_HhSendData(UINT8 dev_handle, BD_ADDR dev_bda, BT_HDR *p_data) { UNUSED(dev_bda); #if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) if (p_data->layer_specific != BTA_HH_RPTT_OUTPUT) { APPL_TRACE_ERROR("ERROR! Wrong report type! Write Command only valid for output report!"); return; } #endif bta_hh_snd_write_dev(dev_handle, HID_TRANS_DATA, (UINT8)p_data->layer_specific, 0, 0, p_data); }
-->
/******************************************************************************* ** ** Function bta_hh_snd_write_dev ** *******************************************************************************/ static void bta_hh_snd_write_dev(UINT8 dev_handle, UINT8 t_type, UINT8 param, UINT16 data, UINT8 rpt_id, BT_HDR *p_data) { tBTA_HH_CMD_DATA *p_buf; UINT16 len = (UINT16) (sizeof(tBTA_HH_CMD_DATA) ); if ((p_buf = (tBTA_HH_CMD_DATA *)GKI_getbuf(len))!= NULL) { memset(p_buf, 0, sizeof(tBTA_HH_CMD_DATA)); p_buf->hdr.event = BTA_HH_API_WRITE_DEV_EVT; p_buf->hdr.layer_specific = (UINT16) dev_handle; p_buf->t_type = t_type; p_buf->data = data; p_buf->param = param; p_buf->p_data = p_data; p_buf->rpt_id = rpt_id; bta_sys_sendmsg(p_buf); //發送數據到hid處理進程 } }
external\bluetooth\bluedroid\bta\hh\bta_hh_main.c
BTA_HhEnable時注冊的HID主處理函數進行數據接收和處理:
/******************************************************************************* ** ** 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; case BTA_HH_DISC_CMPL_EVT: /* disable complete */ bta_hh_disc_cmpl(); break; default: /* all events processed in state machine need to find corresponding CB before proceed */ if (p_msg->event == BTA_HH_API_OPEN_EVT) { index = bta_hh_find_cb(((tBTA_HH_API_CONN *)p_msg)->bd_addr); } else if (p_msg->event == BTA_HH_API_MAINT_DEV_EVT) { /* if add device */ if (((tBTA_HH_MAINT_DEV *)p_msg)->sub_event == BTA_HH_ADD_DEV_EVT) { index = bta_hh_find_cb(((tBTA_HH_MAINT_DEV *)p_msg)->bda); } else /* else remove device by handle */ { index = bta_hh_dev_handle_to_cb_idx((UINT8)p_msg->layer_specific); // btla-specific ++ /* If BT disable is done while the HID device is connected and Link_Key uses unauthenticated combination * then we can get into a situation where remove_bonding is called with the index set to 0 (without getting * cleaned up). Only when VIRTUAL_UNPLUG is called do we cleanup the index and make it MAX_KNOWN. * So if REMOVE_DEVICE is called and in_use is FALSE then we should treat this as a NULL p_cb. Hence we * force the index to be IDX_INVALID */ if ((index != BTA_HH_IDX_INVALID) && (bta_hh_cb.kdev[index].in_use == FALSE)) { index = BTA_HH_IDX_INVALID; } // btla-specific -- } } else if (p_msg->event == BTA_HH_INT_OPEN_EVT) { index = bta_hh_find_cb(((tBTA_HH_CBACK_DATA *)p_msg)->addr); } else index = bta_hh_dev_handle_to_cb_idx((UINT8)p_msg->layer_specific); if (index != BTA_HH_IDX_INVALID) p_cb = &bta_hh_cb.kdev[index]; #if BTA_HH_DEBUG APPL_TRACE_DEBUG("bta_hh_hdl_event:: handle = %d dev_cb[%d] ", p_msg->layer_specific, index); #endif bta_hh_sm_execute(p_cb, p_msg->event, (tBTA_HH_DATA *) p_msg); //狀態機處理函數 } return (TRUE); }
HID狀態機事件處理函數
/******************************************************************************* ** ** 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)); /* handle exception, no valid control block was found */ if (!p_cb) { /* BTA HH enabled already? otherwise ignore the event although it's bad*/ if (bta_hh_cb.p_cback != NULL) { switch (event) { /* no control block available for new connection */ case BTA_HH_API_OPEN_EVT: cback_event = BTA_HH_OPEN_EVT; /* build cback data */ bdcpy(cback_data.conn.bda, ((tBTA_HH_API_CONN *)p_data)->bd_addr); cback_data.conn.status = BTA_HH_ERR_DB_FULL; cback_data.conn.handle = BTA_HH_INVALID_HANDLE; break; /* DB full, BTA_HhAddDev */ case BTA_HH_API_MAINT_DEV_EVT: cback_event = p_data->api_maintdev.sub_event; if (p_data->api_maintdev.sub_event == BTA_HH_ADD_DEV_EVT) { bdcpy(cback_data.dev_info.bda, p_data->api_maintdev.bda); cback_data.dev_info.status = BTA_HH_ERR_DB_FULL; cback_data.dev_info.handle = BTA_HH_INVALID_HANDLE; } else { cback_data.dev_info.status = BTA_HH_ERR_HDL; cback_data.dev_info.handle = (UINT8)p_data->api_maintdev.hdr.layer_specific; } break; case BTA_HH_API_WRITE_DEV_EVT: cback_event = (p_data->api_sndcmd.t_type - BTA_HH_FST_BTE_TRANS_EVT) + BTA_HH_FST_TRANS_CB_EVT; if (p_data->api_sndcmd.p_data != NULL) { GKI_freebuf(p_data->api_sndcmd.p_data); } if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL || p_data->api_sndcmd.t_type == HID_TRANS_SET_REPORT || p_data->api_sndcmd.t_type == HID_TRANS_SET_IDLE) { cback_data.dev_status.status = BTA_HH_ERR_HDL; cback_data.dev_status.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; } else if (p_data->api_sndcmd.t_type != HID_TRANS_DATA && p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) { cback_data.hs_data.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; cback_data.hs_data.status = BTA_HH_ERR_HDL; /* hs_data.rsp_data will be all zero, which is not valid value */ } else if (p_data->api_sndcmd.t_type == HID_TRANS_CONTROL && p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) { cback_data.status = BTA_HH_ERR_HDL; cback_event = BTA_HH_VC_UNPLUG_EVT; } else cback_event = 0; break; case BTA_HH_API_CLOSE_EVT: cback_event = BTA_HH_CLOSE_EVT; cback_data.dev_status.status = BTA_HH_ERR_HDL; cback_data.dev_status.handle = (UINT8)p_data->api_sndcmd.hdr.layer_specific; break; default: /* invalid handle, call bad API event */ APPL_TRACE_ERROR("wrong device handle: [%d]", p_data->hdr.layer_specific); /* Free the callback buffer now */ if (p_data != NULL && p_data->hid_cback.p_data != NULL) { GKI_freebuf(p_data->hid_cback.p_data); p_data->hid_cback.p_data = NULL; } break; } if (cback_event) (* bta_hh_cb.p_cback)(cback_event, &cback_data); } } /* corresponding CB is found, go to state machine */ else { #if BTA_HH_DEBUG == TRUE in_state = p_cb->state; APPL_TRACE_EVENT("bta_hh_sm_execute: State 0x%02x [%s], Event [%s]", in_state, bta_hh_state_code(in_state), bta_hh_evt_code(debug_event)); #endif 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); //各事件處理函數列表,寫節點為action(8)-> bta_hh_write_dev_act } #if BTA_HH_DEBUG == TRUE if (in_state != p_cb->state) { APPL_TRACE_DEBUG("HH State Change: [%s] -> [%s] after Event [%s]", bta_hh_state_code(in_state), bta_hh_state_code(p_cb->state), bta_hh_evt_code(debug_event)); } #endif } return; }
--->
/******************************************************************************* ** ** Function bta_hh_write_dev_act ** ** Description Write device action. can be SET/GET/DATA transaction. ** ** Returns void ** *******************************************************************************/ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { tBTA_HH_CBDATA cbdata = {BTA_HH_OK, 0}; UINT16 event = (p_data->api_sndcmd.t_type - BTA_HH_FST_BTE_TRANS_EVT) + BTA_HH_FST_TRANS_CB_EVT; #if BTA_HH_LE_INCLUDED == TRUE if (p_cb->is_le_device){
//調用到這里t_type為10(HID_TRANS_DATA)即開始通過 BTA_HhSendData 函數發送數據方式: APPL_TRACE_DEBUG("bta_hh_le_write_dev_act : p_data->api_sndcmd.t_type = %d ",p_data->api_sndcmd.t_type); bta_hh_le_write_dev_act(p_cb, p_data); } else #endif { cbdata.handle = p_cb->hid_handle; /* match up BTE/BTA report/boot mode def */ if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL) { p_data->api_sndcmd.param = ( p_data->api_sndcmd.param == BTA_HH_PROTO_RPT_MODE) ?\ HID_PAR_PROTOCOL_REPORT :HID_PAR_PROTOCOL_BOOT_MODE; } if (HID_HostWriteDev (p_cb->hid_handle, p_data->api_sndcmd.t_type, p_data->api_sndcmd.param, p_data->api_sndcmd.data, p_data->api_sndcmd.rpt_id, p_data->api_sndcmd.p_data) != HID_SUCCESS) { APPL_TRACE_ERROR("HID_HostWriteDev Error "); cbdata.status = BTA_HH_ERR; if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL && p_data->api_sndcmd.t_type != HID_TRANS_DATA) (* bta_hh_cb.p_cback)(event, (tBTA_HH *)&cbdata); else if (p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) (* bta_hh_cb.p_cback)(BTA_HH_VC_UNPLUG_EVT, (tBTA_HH *)&cbdata); } else { switch(p_data->api_sndcmd.t_type) { case HID_TRANS_SET_PROTOCOL: /* fall through */ case HID_TRANS_GET_REPORT: /* fall through */ case HID_TRANS_SET_REPORT: /* fall through */ case HID_TRANS_GET_PROTOCOL: /* fall through */ case HID_TRANS_GET_IDLE: /* fall through */ case HID_TRANS_SET_IDLE:/* set w4_handsk event name for callback function use */ p_cb->w4_evt = event; break; case HID_TRANS_DATA: /* output report */ /* fall through */ case HID_TRANS_CONTROL: /* no handshake event will be generated */ /* if VC_UNPLUG is issued, set flag */ if (p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) p_cb->vp = TRUE; break; /* currently not expected */ case HID_TRANS_DATAC: default: APPL_TRACE_DEBUG("bta_hh_write_dev_act:: cmd type = %d", p_data->api_sndcmd.t_type); break; } /* if not control type transaction, notify PM for energy control */ if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) { /* inform PM for mode change */ bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->addr); } else if (p_data->api_sndcmd.param == BTA_HH_CTRL_SUSPEND) { bta_sys_sco_close(BTA_ID_HH, p_cb->app_id, p_cb->addr); } else if (p_data->api_sndcmd.param == BTA_HH_CTRL_EXIT_SUSPEND) { bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); } } } return; }
--->
/******************************************************************************* ** ** Function bta_hh_le_write_dev_act ** ** Description Write LE device action. can be SET/GET/DATA transaction. ** ** Returns void ** *******************************************************************************/ void bta_hh_le_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { switch(p_data->api_sndcmd.t_type) //上面打印結果是10,即對應:HID_TRANS_DATA { case HID_TRANS_SET_PROTOCOL: p_cb->w4_evt = BTA_HH_SET_PROTO_EVT; bta_hh_le_set_protocol_mode(p_cb, p_data->api_sndcmd.param); break; case HID_TRANS_GET_PROTOCOL: bta_hh_le_get_protocol_mode(p_cb); break; case HID_TRANS_GET_REPORT: bta_hh_le_get_rpt(p_cb, BTA_HH_LE_SRVC_DEF, p_data->api_sndcmd.param, p_data->api_sndcmd.rpt_id); break; case HID_TRANS_SET_REPORT: bta_hh_le_write_rpt(p_cb, BTA_HH_LE_SRVC_DEF, BTA_GATTC_TYPE_WRITE, p_data->api_sndcmd.param, p_data->api_sndcmd.p_data, BTA_HH_SET_RPT_EVT); break; case HID_TRANS_DATA: /* output report */ bta_hh_le_write_rpt(p_cb, BTA_HH_LE_SRVC_DEF, BTA_GATTC_TYPE_WRITE_NO_RSP, p_data->api_sndcmd.param, p_data->api_sndcmd.p_data, BTA_HH_DATA_EVT); break; case HID_TRANS_CONTROL: /* no handshake event will be generated */ /* if VC_UNPLUG is issued, set flag */ if (p_data->api_sndcmd.param == BTA_HH_CTRL_SUSPEND || p_data->api_sndcmd.param == BTA_HH_CTRL_EXIT_SUSPEND) { bta_hh_le_suspend(p_cb, p_data->api_sndcmd.param); } break; default: APPL_TRACE_ERROR("%s unsupported transaction for BLE HID device: %d", __func__, p_data->api_sndcmd.t_type); break; } }
-->
/******************************************************************************* ** ** Function bta_hh_le_write_rpt ** ** Description SET_REPORT/or DATA output on a LE HID Report ** ** Returns void ** *******************************************************************************/ void bta_hh_le_write_rpt(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst, tBTA_GATTC_WRITE_TYPE write_type, tBTA_HH_RPT_TYPE r_type, BT_HDR *p_buf, UINT16 w4_evt ) { tBTA_HH_LE_RPT *p_rpt; tBTA_GATTC_CHAR_ID char_id; UINT8 *p_value, rpt_id; if (p_buf == NULL || p_buf->len == 0) { APPL_TRACE_ERROR("bta_hh_le_write_rpt: Illegal data"); return; } /* strip report ID from the data */ p_value = (UINT8 *)(p_buf + 1) + p_buf->offset; STREAM_TO_UINT8(rpt_id, p_value); p_buf->len -= 1; p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[srvc_inst].report, p_cb->mode, r_type, rpt_id); if (p_rpt == NULL) { APPL_TRACE_ERROR("bta_hh_le_write_rpt: no matching report 0x%02x",rpt_id); GKI_freebuf(p_buf); return; } APPL_TRACE_ERROR("bta_hh_le_write_rpt: ReportID: 0x%02x Data Len: %d", rpt_id, p_buf->len); p_cb->w4_evt = w4_evt; bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); bta_hh_le_fill_16bits_char_id(p_rpt->inst_id, p_rpt->uuid, &char_id.char_id); LOG_DEBUG("%s: BTA_GATTC_WriteCharValue::", __FUNCTION__); BTA_GATTC_WriteCharValue(p_cb->conn_id, &char_id, write_type, /* default to use write request */ //之前reportid 0x5b的patch使 write_type為BTA_GATTC_TYPE_WRITE_NO_RSP p_buf->len, p_value, BTA_GATT_AUTH_REQ_NONE); }
-->
/******************************************************************************* ** ** Function BTA_GATTC_WriteCharValue ** ** Description This function is called to write characteristic value. ** ** Parameters conn_id - connection ID. ** p_char_id - characteristic ID to write. ** write_type - type of write. ** len: length of the data to be written. ** p_value - the value to be written. ** ** Returns None ** *******************************************************************************/ void BTA_GATTC_WriteCharValue ( UINT16 conn_id, tBTA_GATTC_CHAR_ID *p_char_id, tBTA_GATTC_WRITE_TYPE write_type, UINT16 len, UINT8 *p_value, tBTA_GATT_AUTH_REQ auth_req) { tBTA_GATTC_API_WRITE *p_buf; if ((p_buf = (tBTA_GATTC_API_WRITE *) GKI_getbuf((UINT16)(sizeof(tBTA_GATTC_API_WRITE) + len))) != NULL) { memset(p_buf, 0, sizeof(tBTA_GATTC_API_WRITE) + len); p_buf->hdr.event = BTA_GATTC_API_WRITE_EVT; p_buf->hdr.layer_specific = conn_id; p_buf->auth_req = auth_req; memcpy(&p_buf->srvc_id, &p_char_id->srvc_id, sizeof(tBTA_GATT_SRVC_ID)); memcpy(&p_buf->char_id, &p_char_id->char_id, sizeof(tBTA_GATT_ID)); APPL_TRACE_DEBUG("BTA_GATTC_WriteCharValue : write_type=%d",write_type); p_buf->write_type = write_type; p_buf->len = len; if (p_value && len > 0) { p_buf->p_value = (UINT8 *)(p_buf + 1); memcpy(p_buf->p_value, p_value, len); } bta_sys_sendmsg(p_buf); //設置數據類型后發送到gatt層 } return; }
應用層傳下來的消息到GATT層:
external\bluetooth\bluedroid\stack\btu\btu_task.c
/******************************************************************************* ** ** Function btu_task ** ** Description This is the main task of the Bluetooth Upper Layers unit. ** It sits in a loop waiting for messages, and dispatches them ** to the appropiate handlers. ** ** Returns should never return ** *******************************************************************************/ BTU_API UINT32 btu_task (UINT32 param) { UINT16 event; BT_HDR *p_msg; UINT8 i; UINT16 mask; BOOLEAN handled; UNUSED(param); #if (defined(HCISU_H4_INCLUDED) && HCISU_H4_INCLUDED == TRUE) /* wait an event that HCISU is ready */ BT_TRACE(TRACE_LAYER_BTU, TRACE_TYPE_API, "btu_task pending for preload complete event"); for (;;) { event = GKI_wait (0xFFFF, 0); if (event & EVENT_MASK(GKI_SHUTDOWN_EVT)) { /* indicates BT ENABLE abort */ BT_TRACE(TRACE_LAYER_BTU, TRACE_TYPE_WARNING, "btu_task start abort!"); return (0); } else if (event & BT_EVT_PRELOAD_CMPL) { break; } else { BT_TRACE(TRACE_LAYER_BTU, TRACE_TYPE_WARNING, "btu_task ignore evt %04x while pending for preload complete", event); } } BT_TRACE(TRACE_LAYER_BTU, TRACE_TYPE_API, "btu_task received preload complete event"); #endif /* Initialize the mandatory core stack control blocks (BTU, BTM, L2CAP, and SDP) */ btu_init_core(); /* Initialize any optional stack components */ BTE_InitStack(); #if (defined(BTU_BTA_INCLUDED) && BTU_BTA_INCLUDED == TRUE) bta_sys_init(); #endif /* Initialise platform trace levels at this point as BTE_InitStack() and bta_sys_init() * reset the control blocks and preset the trace level with XXX_INITIAL_TRACE_LEVEL */ #if ( BT_USE_TRACES==TRUE ) BTE_InitTraceLevels(); #endif /* Send a startup evt message to BTIF_TASK to kickstart the init procedure */ GKI_send_event(BTIF_TASK, BT_EVT_TRIGGER_STACK_INIT); prctl(PR_SET_NAME, (unsigned long)"BTU TASK", 0, 0, 0); raise_priority_a2dp(TASK_HIGH_BTU); /* Wait for, and process, events */ for (;;) { event = GKI_wait (0xFFFF, 0); if (event & TASK_MBOX_0_EVT_MASK) { /* Process all messages in the queue */ while ((p_msg = (BT_HDR *) GKI_read_mbox (BTU_HCI_RCV_MBOX)) != NULL) { /* Determine the input message type. */ switch (p_msg->event & BT_EVT_MASK) { case BT_EVT_TO_BTU_HCI_ACL: /* All Acl Data goes to L2CAP */ l2c_rcv_acl_data (p_msg); break; case BT_EVT_TO_BTU_L2C_SEG_XMIT: /* L2CAP segment transmit complete */ l2c_link_segments_xmitted (p_msg); break; case BT_EVT_TO_BTU_HCI_SCO: #if BTM_SCO_INCLUDED == TRUE btm_route_sco_data (p_msg); break; #endif case BT_EVT_TO_BTU_HCI_EVT: btu_hcif_process_event ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg); GKI_freebuf(p_msg); #if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) /* If host receives events which it doesn't response to, */ /* host should start idle timer to enter sleep mode. */ btu_check_bt_sleep (); #endif break; case BT_EVT_TO_BTU_HCI_CMD: btu_hcif_send_cmd ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg); break; #if (defined(OBX_INCLUDED) && OBX_INCLUDED == TRUE) #if (defined(OBX_SERVER_INCLUDED) && OBX_SERVER_INCLUDED == TRUE) case BT_EVT_TO_OBX_SR_MSG: obx_sr_proc_evt((tOBX_PORT_EVT *)(p_msg + 1)); GKI_freebuf (p_msg); break; case BT_EVT_TO_OBX_SR_L2C_MSG: obx_sr_proc_l2c_evt((tOBX_L2C_EVT_MSG *)(p_msg + 1)); GKI_freebuf (p_msg); break; #endif #if (defined(OBX_CLIENT_INCLUDED) && OBX_CLIENT_INCLUDED == TRUE) case BT_EVT_TO_OBX_CL_MSG: obx_cl_proc_evt((tOBX_PORT_EVT *)(p_msg + 1)); GKI_freebuf (p_msg); break; case BT_EVT_TO_OBX_CL_L2C_MSG: obx_cl_proc_l2c_evt((tOBX_L2C_EVT_MSG *)(p_msg + 1)); GKI_freebuf (p_msg); break; #endif #if (defined(BIP_INCLUDED) && BIP_INCLUDED == TRUE) case BT_EVT_TO_BIP_CMDS : bip_proc_btu_event(p_msg); GKI_freebuf (p_msg); break; #endif /* BIP */ #if (BPP_SND_INCLUDED == TRUE || BPP_INCLUDED == TRUE) case BT_EVT_TO_BPP_PR_CMDS: bpp_pr_proc_event(p_msg); GKI_freebuf (p_msg); break; case BT_EVT_TO_BPP_SND_CMDS: bpp_snd_proc_event(p_msg); GKI_freebuf (p_msg); break; #endif /* BPP */ #endif /* OBX */ #if (defined(SAP_SERVER_INCLUDED) && SAP_SERVER_INCLUDED == TRUE) case BT_EVT_TO_BTU_SAP : sap_proc_btu_event(p_msg); GKI_freebuf (p_msg); break; #endif /* SAP */ #if (defined(GAP_CONN_INCLUDED) && GAP_CONN_INCLUDED == TRUE && GAP_CONN_POST_EVT_INCLUDED == TRUE) case BT_EVT_TO_GAP_MSG : gap_proc_btu_event(p_msg); GKI_freebuf (p_msg); break; #endif case BT_EVT_TO_START_TIMER : /* Start free running 1 second timer for list management */ GKI_start_timer (TIMER_0, GKI_SECS_TO_TICKS (1), TRUE); GKI_freebuf (p_msg); break; case BT_EVT_TO_STOP_TIMER: if (GKI_timer_queue_is_empty(&btu_cb.timer_queue)) { GKI_stop_timer(TIMER_0); } GKI_freebuf (p_msg); break; case BT_EVT_TO_START_TIMER_ONESHOT: if (!GKI_timer_queue_is_empty(&btu_cb.timer_queue_oneshot)) { TIMER_LIST_ENT *tle = GKI_timer_getfirst(&btu_cb.timer_queue_oneshot); // Start non-repeating timer. GKI_start_timer(TIMER_3, tle->ticks, FALSE); } else { BTM_TRACE_WARNING("Oneshot timer queue empty when received start request"); } GKI_freebuf(p_msg); break; case BT_EVT_TO_STOP_TIMER_ONESHOT: if (GKI_timer_queue_is_empty(&btu_cb.timer_queue_oneshot)) { GKI_stop_timer(TIMER_3); } else { BTM_TRACE_WARNING("Oneshot timer queue not empty when received stop request"); } GKI_freebuf (p_msg); break; #if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) case BT_EVT_TO_START_QUICK_TIMER : GKI_start_timer (TIMER_2, QUICK_TIMER_TICKS, TRUE); GKI_freebuf (p_msg); break; #endif default: i = 0; mask = (UINT16) (p_msg->event & BT_EVT_MASK); handled = FALSE; for (; !handled && i < BTU_MAX_REG_EVENT; i++) { if (btu_cb.event_reg[i].event_cb == NULL) continue; if (mask == btu_cb.event_reg[i].event_range) { if (btu_cb.event_reg[i].event_cb) { btu_cb.event_reg[i].event_cb(p_msg); handled = TRUE; } } } if (handled == FALSE) GKI_freebuf (p_msg); break; } } } if (event & TIMER_0_EVT_MASK) { GKI_update_timer_list (&btu_cb.timer_queue, 1); while (!GKI_timer_queue_is_empty(&btu_cb.timer_queue)) { TIMER_LIST_ENT *p_tle = GKI_timer_getfirst(&btu_cb.timer_queue); if (p_tle->ticks != 0) break; GKI_remove_from_timer_list(&btu_cb.timer_queue, p_tle); switch (p_tle->event) { case BTU_TTYPE_BTM_DEV_CTL: btm_dev_timeout(p_tle); break; case BTU_TTYPE_BTM_ACL: btm_acl_timeout(p_tle); break; case BTU_TTYPE_L2CAP_LINK: case BTU_TTYPE_L2CAP_CHNL: case BTU_TTYPE_L2CAP_HOLD: case BTU_TTYPE_L2CAP_INFO: case BTU_TTYPE_L2CAP_FCR_ACK: l2c_process_timeout (p_tle); break; case BTU_TTYPE_SDP: sdp_conn_timeout ((tCONN_CB *)p_tle->param); break; case BTU_TTYPE_BTM_RMT_NAME: btm_inq_rmt_name_failed(); break; #if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) case BTU_TTYPE_RFCOMM_MFC: case BTU_TTYPE_RFCOMM_PORT: rfcomm_process_timeout (p_tle); break; #endif /* If defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE */ #if ((defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE)) case BTU_TTYPE_BNEP: bnep_process_timeout(p_tle); break; #endif #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) case BTU_TTYPE_AVDT_CCB_RET: case BTU_TTYPE_AVDT_CCB_RSP: case BTU_TTYPE_AVDT_CCB_IDLE: case BTU_TTYPE_AVDT_SCB_TC: avdt_process_timeout(p_tle); break; #endif #if (defined(OBX_INCLUDED) && OBX_INCLUDED == TRUE) #if (defined(OBX_CLIENT_INCLUDED) && OBX_CLIENT_INCLUDED == TRUE) case BTU_TTYPE_OBX_CLIENT_TO: obx_cl_timeout(p_tle); break; #endif #if (defined(OBX_SERVER_INCLUDED) && OBX_SERVER_INCLUDED == TRUE) case BTU_TTYPE_OBX_SERVER_TO: obx_sr_timeout(p_tle); break; case BTU_TTYPE_OBX_SVR_SESS_TO: obx_sr_sess_timeout(p_tle); break; #endif #endif #if (defined(SAP_SERVER_INCLUDED) && SAP_SERVER_INCLUDED == TRUE) case BTU_TTYPE_SAP_TO: sap_process_timeout(p_tle); break; #endif case BTU_TTYPE_BTU_CMD_CMPL: btu_hcif_cmd_timeout((UINT8)(p_tle->event - BTU_TTYPE_BTU_CMD_CMPL)); break; #if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) case BTU_TTYPE_HID_HOST_REPAGE_TO : hidh_proc_repage_timeout(p_tle); break; #endif #if (defined(BLE_INCLUDED) && BLE_INCLUDED == TRUE) case BTU_TTYPE_BLE_INQUIRY: case BTU_TTYPE_BLE_GAP_LIM_DISC: case BTU_TTYPE_BLE_GAP_FAST_ADV: case BTU_TTYPE_BLE_OBSERVE: btm_ble_timeout(p_tle); break; case BTU_TTYPE_ATT_WAIT_FOR_RSP: gatt_rsp_timeout(p_tle); break; case BTU_TTYPE_ATT_WAIT_FOR_IND_ACK: gatt_ind_ack_timeout(p_tle); break; #if (defined(SMP_INCLUDED) && SMP_INCLUDED == TRUE) case BTU_TTYPE_SMP_PAIRING_CMD: smp_rsp_timeout(p_tle); break; #endif #endif #if (MCA_INCLUDED == TRUE) case BTU_TTYPE_MCA_CCB_RSP: mca_process_timeout(p_tle); break; #endif case BTU_TTYPE_USER_FUNC: { tUSER_TIMEOUT_FUNC *p_uf = (tUSER_TIMEOUT_FUNC *)p_tle->param; (*p_uf)(p_tle); } break; default: i = 0; handled = FALSE; for (; !handled && i < BTU_MAX_REG_TIMER; i++) { if (btu_cb.timer_reg[i].timer_cb == NULL) continue; if (btu_cb.timer_reg[i].p_tle == p_tle) { btu_cb.timer_reg[i].timer_cb(p_tle); handled = TRUE; } } break; } } /* if timer list is empty stop periodic GKI timer */ if (btu_cb.timer_queue.p_first == NULL) { GKI_stop_timer(TIMER_0); } } #if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) if (event & TIMER_2_EVT_MASK) { btu_process_quick_timer_evt(); } #endif #if (RPC_INCLUDED == TRUE) /* if RPC message queue event */ if (event & RPCGEN_MSG_EVT) { if ((p_msg = (BT_HDR *) GKI_read_mbox(RPCGEN_MSG_MBOX)) != NULL) RPCT_RpcgenMsg(p_msg); /* handle RPC message queue */ } #endif #if (defined(BTU_BTA_INCLUDED) && BTU_BTA_INCLUDED == TRUE) if (event & TASK_MBOX_2_EVT_MASK) { while ((p_msg = (BT_HDR *) GKI_read_mbox(TASK_MBOX_2)) != NULL) { bta_sys_event(p_msg); //處理上層傳來的消息 } } if (event & TIMER_1_EVT_MASK) { bta_sys_timer_update(); } #endif if (event & TIMER_3_EVT_MASK) { BTM_TRACE_API("Received oneshot timer event complete"); if (!GKI_timer_queue_is_empty(&btu_cb.timer_queue_oneshot)) { TIMER_LIST_ENT *p_tle = GKI_timer_getfirst(&btu_cb.timer_queue_oneshot); INT32 ticks_since_last_update = GKI_timer_ticks_getinitial(GKI_timer_getfirst(&btu_cb.timer_queue_oneshot)); GKI_update_timer_list(&btu_cb.timer_queue_oneshot, ticks_since_last_update); } while (!GKI_timer_queue_is_empty(&btu_cb.timer_queue_oneshot)) { TIMER_LIST_ENT *p_tle = GKI_timer_getfirst(&btu_cb.timer_queue_oneshot); if (p_tle->ticks != 0) break; GKI_remove_from_timer_list(&btu_cb.timer_queue_oneshot, p_tle); switch (p_tle->event) { #if (defined(BLE_INCLUDED) && BLE_INCLUDED == TRUE) case BTU_TTYPE_BLE_RANDOM_ADDR: btm_ble_timeout(p_tle); break; #endif case BTU_TTYPE_USER_FUNC: { tUSER_TIMEOUT_FUNC *p_uf = (tUSER_TIMEOUT_FUNC *)p_tle->param; (*p_uf)(p_tle); } break; default: // FAIL BTM_TRACE_WARNING("Received unexpected oneshot timer event:0x%x\n", p_tle->event); break; } } /* Update GKI timer with new tick value from first timer. */ if (!GKI_timer_queue_is_empty(&btu_cb.timer_queue_oneshot)) { TIMER_LIST_ENT *p_tle = GKI_timer_getfirst(&btu_cb.timer_queue_oneshot); if (p_tle->ticks > 0) GKI_start_timer(TIMER_3, p_tle->ticks, FALSE); } else { GKI_stop_timer(TIMER_3); } } if (event & EVENT_MASK(APPL_EVT_7)) break; } return(0); }
應用層注冊的回調函數被調用:
/******************************************************************************* ** ** Function bta_sys_event ** ** Description BTA event handler; called from task event handler. ** ** ** Returns void ** *******************************************************************************/ BTA_API void bta_sys_event(BT_HDR *p_msg) { UINT8 id; BOOLEAN freebuf = TRUE; APPL_TRACE_EVENT("BTA got event 0x%x", p_msg->event); //event = BTA_HH_API_WRITE_DEV_EVT /* get subsystem id from event */ id = (UINT8) (p_msg->event >> 8); /* verify id and call subsystem event handler */ if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL)) { freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg); //Id為0x17,即BTA_ID_HH,回調函數為bta_hh_hdl_event,前面已分析
//Id為0x1f,即為BTA_ID_GATTC,回調函數為bta_gattc_hdl_event,繼續往后看 } else { APPL_TRACE_WARNING("BTA got unregistered event id %d", id); } if (freebuf) { GKI_freebuf(p_msg); } }
gatt client事件處理函數:
/******************************************************************************* ** ** Function bta_gattc_hdl_event ** ** Description GATT client main event handling function. ** ** ** Returns BOOLEAN ** *******************************************************************************/ BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg) { tBTA_GATTC_CB *p_cb = &bta_gattc_cb; tBTA_GATTC_CLCB *p_clcb = NULL; tBTA_GATTC_RCB *p_clreg; BOOLEAN rt = TRUE; #if BTA_GATT_DEBUG == TRUE APPL_TRACE_DEBUG("bta_gattc_hdl_event: Event [%s]", gattc_evt_code(p_msg->event)); #endif switch (p_msg->event) { case BTA_GATTC_API_DISABLE_EVT: bta_gattc_disable(p_cb); break; case BTA_GATTC_API_REG_EVT: bta_gattc_register(p_cb, (tBTA_GATTC_DATA *) p_msg); break; case BTA_GATTC_INT_START_IF_EVT: bta_gattc_start_if(p_cb, (tBTA_GATTC_DATA *) p_msg); break; case BTA_GATTC_API_DEREG_EVT: p_clreg = bta_gattc_cl_get_regcb(((tBTA_GATTC_DATA *)p_msg)->api_dereg.client_if); bta_gattc_deregister(p_cb, p_clreg); break; case BTA_GATTC_API_OPEN_EVT: bta_gattc_process_api_open(p_cb, (tBTA_GATTC_DATA *) p_msg); break; case BTA_GATTC_API_CANCEL_OPEN_EVT: bta_gattc_process_api_open_cancel(p_cb, (tBTA_GATTC_DATA *) p_msg); break; case BTA_GATTC_API_REFRESH_EVT: bta_gattc_process_api_refresh(p_cb, (tBTA_GATTC_DATA *) p_msg); break; #if BLE_INCLUDED == TRUE case BTA_GATTC_API_LISTEN_EVT: bta_gattc_listen(p_cb, (tBTA_GATTC_DATA *) p_msg); break; case BTA_GATTC_API_BROADCAST_EVT: bta_gattc_broadcast(p_cb, (tBTA_GATTC_DATA *) p_msg); break; #endif case BTA_GATTC_ENC_CMPL_EVT: bta_gattc_process_enc_cmpl(p_cb, (tBTA_GATTC_DATA *) p_msg); break; default: if (p_msg->event == BTA_GATTC_INT_CONN_EVT) p_clcb = bta_gattc_find_int_conn_clcb((tBTA_GATTC_DATA *) p_msg); else if (p_msg->event == BTA_GATTC_INT_DISCONN_EVT) p_clcb = bta_gattc_find_int_disconn_clcb((tBTA_GATTC_DATA *) p_msg); else p_clcb = bta_gattc_find_clcb_by_conn_id(p_msg->layer_specific); if (p_clcb != NULL) { rt = bta_gattc_sm_execute(p_clcb, p_msg->event, (tBTA_GATTC_DATA *) p_msg); //事件處理 } else { APPL_TRACE_DEBUG("Ignore unknown conn ID: %d", p_msg->layer_specific); } break; } return rt; }
gatt client事件處理狀態機
/******************************************************************************* ** ** Function bta_gattc_sm_execute ** ** Description State machine event handling function for GATTC ** ** ** Returns BOOLEAN : TRUE if queued client request buffer can be immediately released ** else FALSE ** *******************************************************************************/ BOOLEAN bta_gattc_sm_execute(tBTA_GATTC_CLCB *p_clcb, UINT16 event, tBTA_GATTC_DATA *p_data) { tBTA_GATTC_ST_TBL state_table; UINT8 action; int i; BOOLEAN rt = TRUE; #if BTA_GATT_DEBUG == TRUE tBTA_GATTC_STATE in_state = p_clcb->state; UINT16 in_event = event; APPL_TRACE_DEBUG("bta_gattc_sm_execute: State 0x%02x [%s], Event 0x%x[%s]", in_state, gattc_state_code(in_state), in_event, gattc_evt_code(in_event)); #endif /* look up the state table for the current state */ state_table = bta_gattc_st_tbl[p_clcb->state]; event &= 0x00FF; /* set next state */ p_clcb->state = state_table[event][BTA_GATTC_NEXT_STATE]; /* execute action functions */ for (i = 0; i < BTA_GATTC_ACTIONS; i++) { if ((action = state_table[event][i]) != BTA_GATTC_IGNORE) { (*bta_gattc_action[action])(p_clcb, p_data); //執行bta_gattc_write, if (p_clcb->p_q_cmd == p_data) { /* buffer is queued, don't free in the bta dispatcher. * we free it ourselves when a completion event is received. */ rt = FALSE; } } else { break; } } #if BTA_GATT_DEBUG == TRUE if (in_state != p_clcb->state) { APPL_TRACE_DEBUG("GATTC [%d] State Change: [%s] -> [%s] after Event [%s]",p_clcb->bta_conn_id, gattc_state_code(in_state), gattc_state_code(p_clcb->state), gattc_evt_code(in_event)); } #endif return rt; }
->
/******************************************************************************* ** ** Function bta_gattc_write ** ** Description Write an attribute ** ** Returns None. ** *******************************************************************************/ void bta_gattc_write(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { UINT16 handle = 0; tGATT_VALUE attr = {0}; tBTA_GATTC_OP_CMPL op_cmpl; tBTA_GATT_STATUS status = BTA_GATT_OK; if (bta_gattc_enqueue(p_clcb, p_data)) { if ((handle = bta_gattc_id2handle(p_clcb->p_srcb, &p_data->api_write.srvc_id, &p_data->api_write.char_id, p_data->api_write.p_descr_type)) == 0) { status = BTA_GATT_ERROR; } else { attr.handle= handle; attr.offset = p_data->api_write.offset; attr.len = p_data->api_write.len; attr.auth_req = p_data->api_write.auth_req; if (p_data->api_write.p_value) memcpy(attr.value, p_data->api_write.p_value, p_data->api_write.len); status = GATTC_Write(p_clcb->bta_conn_id, p_data->api_write.write_type, &attr); } /* write fail */ if (status != BTA_GATT_OK) { memset(&op_cmpl, 0, sizeof(tBTA_GATTC_OP_CMPL)); op_cmpl.status = status; op_cmpl.op_code = GATTC_OPTYPE_WRITE; op_cmpl.p_cmpl = NULL; bta_gattc_sm_execute(p_clcb, BTA_GATTC_OP_CMPL_EVT, (tBTA_GATTC_DATA *)&op_cmpl); } } }
->
/******************************************************************************* ** ** Function GATTC_Write ** ** Description This function is called to write the value of an attribute to ** the server. ** ** Parameters conn_id: connection identifier. ** type - attribute write type. ** p_write - write operation parameters. ** ** Returns GATT_SUCCESS if command started successfully. ** *******************************************************************************/ tGATT_STATUS GATTC_Write (UINT16 conn_id, tGATT_WRITE_TYPE type, tGATT_VALUE *p_write) { tGATT_STATUS status = GATT_SUCCESS; tGATT_CLCB *p_clcb; tGATT_VALUE *p; tGATT_IF gatt_if=GATT_GET_GATT_IF(conn_id); UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); tGATT_REG *p_reg = gatt_get_regcb(gatt_if); if ( (p_tcb == NULL) || (p_reg==NULL) || (p_write == NULL) || ((type != GATT_WRITE) && (type != GATT_WRITE_PREPARE) && (type != GATT_WRITE_NO_RSP)) ) { GATT_TRACE_ERROR("GATT_Write Illegal param: conn_id %d, type 0%d,", conn_id, type); return GATT_ILLEGAL_PARAMETER; } if (gatt_is_clcb_allocated(conn_id)) { GATT_TRACE_ERROR("GATTC_Write GATT_BUSY conn_id = %d", conn_id); return GATT_BUSY; } if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) { p_clcb->operation = GATTC_OPTYPE_WRITE; p_clcb->op_subtype = type; p_clcb->auth_req = p_write->auth_req; if (( p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf((UINT16)sizeof(tGATT_VALUE))) != NULL) { memcpy(p_clcb->p_attr_buf, (void *)p_write, sizeof(tGATT_VALUE)); p = (tGATT_VALUE *)p_clcb->p_attr_buf; if (type == GATT_WRITE_PREPARE) { p_clcb->start_offset = p_write->offset; p->offset = 0; } if (gatt_security_check_start(p_clcb) == FALSE) { status = GATT_NO_RESOURCES; } } else { status = GATT_NO_RESOURCES; } if (status == GATT_NO_RESOURCES) gatt_clcb_dealloc(p_clcb); } else { status = GATT_NO_RESOURCES; } return status; }
->
/******************************************************************************* ** ** Function gatt_check_enc_req ** ** Description check link security. ** ** Returns TRUE if encrypted, otherwise FALSE. ** *******************************************************************************/ BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb) { tGATT_TCB *p_tcb = p_clcb->p_tcb; tGATT_SEC_ACTION gatt_sec_act; tBTM_BLE_SEC_ACT btm_ble_sec_act; BOOLEAN status = TRUE; tBTM_STATUS btm_status; tGATT_SEC_ACTION sec_act_old = gatt_get_sec_act(p_tcb); gatt_sec_act = gatt_determine_sec_act(p_clcb); if (sec_act_old == GATT_SEC_NONE) gatt_set_sec_act(p_tcb, gatt_sec_act); switch (gatt_sec_act ) { case GATT_SEC_SIGN_DATA: GATT_TRACE_DEBUG("gatt_security_check_start: Do data signing"); gatt_sign_data(p_clcb); break; case GATT_SEC_ENCRYPT: case GATT_SEC_ENCRYPT_NO_MITM: case GATT_SEC_ENCRYPT_MITM: if (sec_act_old < GATT_SEC_ENCRYPT) { GATT_TRACE_DEBUG("gatt_security_check_start: Encrypt now or key upgreade first"); gatt_convert_sec_action(gatt_sec_act, &btm_ble_sec_act); btm_status = BTM_SetEncryption(p_tcb->peer_bda, p_tcb->transport , gatt_enc_cmpl_cback, &btm_ble_sec_act); if ( (btm_status != BTM_SUCCESS) && (btm_status != BTM_CMD_STARTED)) { GATT_TRACE_ERROR("gatt_security_check_start BTM_SetEncryption failed btm_status=%d", btm_status); status = FALSE; } } if (status) gatt_add_pending_enc_channel_clcb (p_tcb, p_clcb); break; case GATT_SEC_ENC_PENDING: gatt_add_pending_enc_channel_clcb (p_tcb, p_clcb); /* wait for link encrypotion to finish */ break; default: gatt_sec_check_complete(TRUE, p_clcb, gatt_sec_act); break; } if (status == FALSE) { gatt_set_sec_act(p_tcb, GATT_SEC_NONE); gatt_set_ch_state(p_tcb, GATT_CH_OPEN); } return status; }
->
/******************************************************************************* ** ** Function gatt_sec_check_complete ** ** Description security check complete and proceed to data sending action. ** ** Returns void. ** *******************************************************************************/ void gatt_sec_check_complete(BOOLEAN sec_check_ok, tGATT_CLCB *p_clcb, UINT8 sec_act) { if (p_clcb && p_clcb->p_tcb && GKI_queue_is_empty(&p_clcb->p_tcb->pending_enc_clcb)) gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE); if (!sec_check_ok) { gatt_end_operation(p_clcb, GATT_AUTH_FAIL, NULL); } else if (p_clcb->operation == GATTC_OPTYPE_WRITE) { gatt_act_write(p_clcb, sec_act); } else if (p_clcb->operation == GATTC_OPTYPE_READ) { gatt_act_read(p_clcb, p_clcb->counter); } }
->
/******************************************************************************* ** ** Function gatt_act_write ** ** Description GATT write operation. ** ** Returns void. ** *******************************************************************************/ void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act) { tGATT_TCB *p_tcb = p_clcb->p_tcb; UINT8 rt = GATT_SUCCESS, op_code = 0; tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; if (p_attr) { switch (p_clcb->op_subtype) { case GATT_WRITE_NO_RSP: p_clcb->s_handle = p_attr->handle; op_code = (sec_act == GATT_SEC_SIGN_DATA) ? GATT_SIGN_CMD_WRITE : GATT_CMD_WRITE; rt = gatt_send_write_msg(p_tcb, p_clcb->clcb_idx, op_code, p_attr->handle, p_attr->len, 0, p_attr->value); break; case GATT_WRITE: if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE)) { p_clcb->s_handle = p_attr->handle; rt = gatt_send_write_msg(p_tcb, p_clcb->clcb_idx, GATT_REQ_WRITE, p_attr->handle, p_attr->len, 0, p_attr->value); } else /* prepare write for long attribute */ { gatt_send_prepare_write(p_tcb, p_clcb); } break; case GATT_WRITE_PREPARE: gatt_send_prepare_write(p_tcb, p_clcb); break; default: rt = GATT_INTERNAL_ERROR; GATT_TRACE_ERROR("Unknown write type: %d", p_clcb->op_subtype); break; } } else rt = GATT_INTERNAL_ERROR; if ((rt != GATT_SUCCESS && rt != GATT_CMD_STARTED && rt != GATT_CONGESTED) || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP)) { if (rt != GATT_SUCCESS) { GATT_TRACE_ERROR("gatt_act_write() failed op_code=0x%x", op_code); } gatt_end_operation(p_clcb, rt, NULL); } }
->
/******************************************************************************* ** ** Function gatt_send_write_msg ** ** Description This real function send out the ATT message for write. ** ** Returns status code ** *******************************************************************************/ UINT8 gatt_send_write_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, UINT16 handle, UINT16 len, UINT16 offset, UINT8 *p_data) { tGATT_CL_MSG msg; msg.attr_value.handle = handle; msg.attr_value.len = len; msg.attr_value.offset = offset; memcpy (msg.attr_value.value, p_data, len); /* write by handle */ return attp_send_cl_msg(p_tcb, clcb_idx, op_code, &msg); }
->
/******************************************************************************* ** ** Function attp_send_cl_msg ** ** Description This function sends the client request or confirmation message ** to server. ** ** Parameter p_tcb: pointer to the connectino control block. ** clcb_idx: clcb index ** op_code: message op code. ** p_msg: pointer to message parameters structure. ** ** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. ** ** *******************************************************************************/ tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg) { tGATT_STATUS status = GATT_NO_RESOURCES; BT_HDR *p_cmd = NULL; UINT16 offset = 0, handle; if (p_tcb != NULL) { switch (op_code) { case GATT_REQ_MTU: if (p_msg->mtu <= GATT_MAX_MTU_SIZE) { p_tcb->payload_size = p_msg->mtu; p_cmd = attp_build_mtu_cmd(GATT_REQ_MTU, p_msg->mtu); } else status = GATT_ILLEGAL_PARAMETER; break; case GATT_REQ_FIND_INFO: case GATT_REQ_READ_BY_TYPE: case GATT_REQ_READ_BY_GRP_TYPE: if (GATT_HANDLE_IS_VALID (p_msg->browse.s_handle) && GATT_HANDLE_IS_VALID (p_msg->browse.e_handle) && p_msg->browse.s_handle <= p_msg->browse.e_handle) { p_cmd = attp_build_browse_cmd(op_code, p_msg->browse.s_handle, p_msg->browse.e_handle, p_msg->browse.uuid); } else status = GATT_ILLEGAL_PARAMETER; break; case GATT_REQ_READ_BLOB: offset = p_msg->read_blob.offset; /* fall through */ case GATT_REQ_READ: handle = (op_code == GATT_REQ_READ) ? p_msg->handle: p_msg->read_blob.handle; /* handle checking */ if (GATT_HANDLE_IS_VALID (handle)) { p_cmd = attp_build_handle_cmd(op_code, handle, offset); } else status = GATT_ILLEGAL_PARAMETER; break; case GATT_HANDLE_VALUE_CONF: p_cmd = attp_build_opcode_cmd(op_code); break; case GATT_REQ_PREPARE_WRITE: offset = p_msg->attr_value.offset; /* fall through */ case GATT_REQ_WRITE: case GATT_CMD_WRITE: case GATT_SIGN_CMD_WRITE: if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle)) {
//先調用這里 p_cmd = attp_build_value_cmd (p_tcb->payload_size, op_code, p_msg->attr_value.handle, offset, p_msg->attr_value.len, p_msg->attr_value.value); } else status = GATT_ILLEGAL_PARAMETER; break; case GATT_REQ_EXEC_WRITE: p_cmd = attp_build_exec_write_cmd(op_code, p_msg->exec_write); break; case GATT_REQ_FIND_TYPE_VALUE: p_cmd = attp_build_read_by_type_value_cmd(p_tcb->payload_size, &p_msg->find_type_value); break; case GATT_REQ_READ_MULTI: p_cmd = attp_build_read_multi_cmd(p_tcb->payload_size, p_msg->read_multi.num_handles, p_msg->read_multi.handles); break; default: break; }
//再調用這里 if (p_cmd != NULL) status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd); } else { GATT_TRACE_ERROR("Peer device not connected"); } return status; }
-->
/******************************************************************************* ** ** Function attp_build_value_cmd ** ** Description Build a attribute value request ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle, UINT16 offset, UINT16 len, UINT8 *p_data) { BT_HDR *p_buf = NULL; UINT8 *p, *pp, pair_len, *p_pair_len; if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) { p = pp =(UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; UINT8_TO_STREAM (p, op_code); p_buf->offset = L2CAP_MIN_OFFSET; p_buf->len = 1; if (op_code == GATT_RSP_READ_BY_TYPE) { p_pair_len = p; pair_len = len + 2; UINT8_TO_STREAM (p, pair_len); p_buf->len += 1; } if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ) { UINT16_TO_STREAM (p, handle); p_buf->len += 2; } if (op_code == GATT_REQ_PREPARE_WRITE ||op_code == GATT_RSP_PREPARE_WRITE ) { UINT16_TO_STREAM (p, offset); p_buf->len += 2; } if (len > 0 && p_data != NULL) { /* ensure data not exceed MTU size */ if (payload_size - p_buf->len < len) { len = payload_size - p_buf->len; /* update handle value pair length */ if (op_code == GATT_RSP_READ_BY_TYPE) *p_pair_len = (len + 2); GATT_TRACE_WARNING("attribute value too long, to be truncated to %d", len); } ARRAY_TO_STREAM (p, p_data, len); p_buf->len += len; } } return p_buf; }
—>
/******************************************************************************* ** ** Function attp_cl_send_cmd ** ** Description Send a ATT command or enqueue it. ** ** Returns GATT_SUCCESS if command sent ** GATT_CONGESTED if command sent but channel congested ** GATT_CMD_STARTED if command queue up in GATT ** GATT_ERROR if command sending failure ** *******************************************************************************/ tGATT_STATUS attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd) { tGATT_STATUS att_ret = GATT_SUCCESS; if (p_tcb != NULL) { cmd_code &= ~GATT_AUTH_SIGN_MASK; /* no pending request or value confirmation */ if (p_tcb->pending_cl_req == p_tcb->next_slot_inq || cmd_code == GATT_HANDLE_VALUE_CONF) { att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd); //發送數據到l2cap層 if (att_ret == GATT_CONGESTED || att_ret == GATT_SUCCESS) { /* do not enq cmd if handle value confirmation or set request */ if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE) { gatt_start_rsp_timer (clcb_idx); //如果是write request數據類型,則會創建定時器等待respond(5s超時) gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL); } } else att_ret = GATT_INTERNAL_ERROR; } else { att_ret = GATT_CMD_STARTED; gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd); } } else att_ret = GATT_ERROR; return att_ret; }
->
/******************************************************************************* ** ** Function attp_send_msg_to_l2cap ** ** Description Send message to L2CAP. ** *******************************************************************************/ tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP) { UINT16 l2cap_ret; if (p_tcb->att_lcid == L2CAP_ATT_CID) l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP); else l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP); if (l2cap_ret == L2CAP_DW_FAILED) { GATT_TRACE_ERROR("ATT failed to pass msg:0x%0x to L2CAP", *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset)); return GATT_INTERNAL_ERROR; } else if (l2cap_ret == L2CAP_DW_CONGESTED) { GATT_TRACE_DEBUG("ATT congested, message accepted"); return GATT_CONGESTED; } return GATT_SUCCESS; }
->
/******************************************************************************* ** ** Function L2CA_SendFixedChnlData ** ** Description Write data on a fixed channel. ** ** Parameters: Fixed CID ** BD Address of remote ** Pointer to buffer of type BT_HDR ** ** Return value L2CAP_DW_SUCCESS, if data accepted ** L2CAP_DW_FAILED, if error ** *******************************************************************************/ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf) { tL2C_LCB *p_lcb; tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; L2CAP_TRACE_API ("L2CA_SendFixedChnlData() CID: 0x%04x BDA: %08x%04x", fixed_cid, (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); #if BLE_INCLUDED == TRUE if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) transport = BT_TRANSPORT_LE; #endif /* Check CID is valid and registered */ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) { L2CAP_TRACE_ERROR ("L2CA_SendFixedChnlData() Invalid CID: 0x%04x", fixed_cid); GKI_freebuf (p_buf); return (L2CAP_DW_FAILED); } /* Fail if BT is not yet up */ if (!BTM_IsDeviceUp()) { L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData(0x%04x) - BTU not ready", fixed_cid); GKI_freebuf (p_buf); return (L2CAP_DW_FAILED); } /* We need to have a link up */ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport)) == NULL || /* if link is disconnecting, also report data sending failure */ p_lcb->link_state == LST_DISCONNECTING) { L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData(0x%04x) - no LCB", fixed_cid); GKI_freebuf (p_buf); return (L2CAP_DW_FAILED); } if ((p_lcb->peer_chnl_mask[0] & (1 << fixed_cid)) == 0) { L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData() - peer does not support fixed chnl: 0x%04x", fixed_cid); GKI_freebuf (p_buf); return (L2CAP_DW_FAILED); } p_buf->event = 0; p_buf->layer_specific = L2CAP_FLUSHABLE_CH_BASED; if (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) { if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) { L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData() - no CCB for chnl: 0x%4x", fixed_cid); GKI_freebuf (p_buf); return (L2CAP_DW_FAILED); } } /* If already congested, do not accept any more packets */ if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent) { L2CAP_TRACE_ERROR ("L2CAP - CID: 0x%04x cannot send, already congested \ xmit_hold_q.count: %u buff_quota: %u", fixed_cid, p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->xmit_hold_q.count, p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->buff_quota); GKI_freebuf (p_buf); return (L2CAP_DW_FAILED); } l2c_enqueue_peer_data (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL], p_buf); l2c_link_check_send_pkts (p_lcb, NULL, NULL); /* If there is no dynamic CCB on the link, restart the idle timer each time something is sent */ if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb) { l2cu_no_dynamic_ccbs (p_lcb); } if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent) return (L2CAP_DW_CONGESTED); return (L2CAP_DW_SUCCESS); }
調用 l2c_enqueue_peer_data 讓數據進入到當前 ccb 的 xmit_hold_q 隊列中,暫存此數據包。
/******************************************************************************* ** ** Function l2c_enqueue_peer_data ** ** Description Enqueues data destined for the peer in the ccb. Handles ** FCR segmentation and checks for congestion. ** ** Returns void ** *******************************************************************************/ void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf) { UINT8 *p; if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { p_buf->event = 0; } else { /* Save the channel ID for faster counting */ p_buf->event = p_ccb->local_cid; /* Step back to add the L2CAP header */ p_buf->offset -= L2CAP_PKT_OVERHEAD; p_buf->len += L2CAP_PKT_OVERHEAD; /* Set the pointer to the beginning of the data */ p = (UINT8 *)(p_buf + 1) + p_buf->offset; /* Now the L2CAP header */ UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD); UINT16_TO_STREAM (p, p_ccb->remote_cid); } GKI_enqueue (&p_ccb->xmit_hold_q, p_buf); l2cu_check_channel_congestion (p_ccb); //檢測當前 Channel 擁堵情況 #if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) /* if new packet is higher priority than serving ccb and it is not overrun */ if (( p_ccb->p_lcb->rr_pri > p_ccb->ccb_priority ) &&( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota > 0)) { /* send out higher priority packet */ p_ccb->p_lcb->rr_pri = p_ccb->ccb_priority; } #endif /* if we are doing a round robin scheduling, set the flag */ if (p_ccb->p_lcb->link_xmit_quota == 0) l2cb.check_round_robin = TRUE; }
-->
/****************************************************************************** ** ** Function l2cu_check_channel_congestion ** ** Description check if any change in congestion status ** ** Returns None ** *******************************************************************************/ void l2cu_check_channel_congestion (tL2C_CCB *p_ccb) { UINT16 q_count = GKI_queue_length(&p_ccb->xmit_hold_q); #if (L2CAP_UCD_INCLUDED == TRUE) if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) { q_count += p_ccb->p_lcb->ucd_out_sec_pending_q.count; } #endif /* If the CCB queue limit is subject to a quota, check for congestion */ /* if this channel has outgoing traffic */ if (p_ccb->buff_quota != 0) { /* If this channel was congested */ if ( p_ccb->cong_sent ) { /* If the channel is not congested now, tell the app */
//在函數 l2c_link_adjust_chnl_allocation 中配置此值
//p_ccb->buff_quota = quota_per_weighted_chnls[p_ccb->ertm_info.user_tx_pool_id] * p_ccb->tx_data_rate; if (q_count <= (p_ccb->buff_quota / 2)) //當前CCB中的 xmit_hold_q 小於 buffer_quota 值的一半,即認為已經不擁堵 { p_ccb->cong_sent = FALSE; if (p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb) { L2CAP_TRACE_DEBUG ("L2CAP - Calling CongestionStatus_Cb (FALSE), CID: 0x%04x xmit_hold_q.count: %u buff_quota: %u", p_ccb->local_cid, q_count, p_ccb->buff_quota); /* Prevent recursive calling */ l2cb.is_cong_cback_context = TRUE; (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, FALSE); l2cb.is_cong_cback_context = FALSE; } #if (L2CAP_UCD_INCLUDED == TRUE) else if ( p_ccb->p_rcb && p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) { if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) { L2CAP_TRACE_DEBUG ("L2CAP - Calling UCD CongestionStatus_Cb (FALSE), SecPendingQ:%u,XmitQ:%u,Quota:%u", p_ccb->p_lcb->ucd_out_sec_pending_q.count, p_ccb->xmit_hold_q.count, p_ccb->buff_quota); p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, FALSE ); } } #endif #if (L2CAP_NUM_FIXED_CHNLS > 0) else { UINT8 xx; for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx ++) { if (p_ccb->p_lcb->p_fixed_ccbs[xx] == p_ccb) { if (l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb != NULL) (* l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb)(p_ccb->p_lcb->remote_bd_addr, FALSE); break; } } } #endif } } else { /* If this channel was not congested but it is congested now, tell the app */ if (q_count > p_ccb->buff_quota) { p_ccb->cong_sent = TRUE; if (p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb) { L2CAP_TRACE_DEBUG ("L2CAP - Calling CongestionStatus_Cb (TRUE),CID:0x%04x,XmitQ:%u,Quota:%u", p_ccb->local_cid, q_count, p_ccb->buff_quota); (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, TRUE); } #if (L2CAP_UCD_INCLUDED == TRUE) else if ( p_ccb->p_rcb && p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) { if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) { L2CAP_TRACE_DEBUG ("L2CAP - Calling UCD CongestionStatus_Cb (TRUE), SecPendingQ:%u,XmitQ:%u,Quota:%u", p_ccb->p_lcb->ucd_out_sec_pending_q.count, p_ccb->xmit_hold_q.count, p_ccb->buff_quota); p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, TRUE ); } } #endif #if (L2CAP_NUM_FIXED_CHNLS > 0) else { UINT8 xx; for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx ++) { if (p_ccb->p_lcb->p_fixed_ccbs[xx] == p_ccb) { if (l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb != NULL) (* l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb)(p_ccb->p_lcb->remote_bd_addr, TRUE); break; } } } #endif } } } }
L2CAP層是通過 l2c_link_check_send_pkts 這個函數發送數據包:
/******************************************************************************* ** ** Function l2c_link_check_send_pkts ** ** Description This function is called to check if it can send packets ** to the Host Controller. It may be passed the address of ** a packet to send. ** ** Returns void ** *******************************************************************************/ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) { int xx; BOOLEAN single_write = FALSE; //最后 Link Disc 用來把 CCB 中的數據包放到 Link 上的隊列發,速度加快 /* Save the channel ID for faster counting */ if (p_buf)//一般數據包都為空,只有發送 L2CAP 發送 command/response 或 發送 S-Frame 才用到 { if (p_ccb != NULL) //這個 case 就是 當前 Link 即將斷開的情況了 { p_buf->event = p_ccb->local_cid; single_write = TRUE; } else p_buf->event = 0; p_buf->layer_specific = 0; //把這個數據包放到 當前 link 上的 link_xmit_data_q隊列中 GKI_enqueue (&p_lcb->link_xmit_data_q, p_buf); //沒有發送窗口了,需要 RR 看看有沒有別的數據包可以發 if (p_lcb->link_xmit_quota == 0) { #if BLE_INCLUDED == TRUE if (p_lcb->transport == BT_TRANSPORT_LE) l2cb.ble_check_round_robin = TRUE; else #endif l2cb.check_round_robin = TRUE; } } /* If this is called from uncongested callback context break recursive calling. ** This LCB will be served when receiving number of completed packet event. */ if (l2cb.is_cong_cback_context)//當前 Link 擁堵了,不發送數據包直接返回 return; /* If we are in a scenario where there are not enough buffers for each link to ** have at least 1, then do a round-robin for all the LCBs */ if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) ) { if (p_lcb == NULL) p_lcb = l2cb.lcb_pool; else if (!single_write) p_lcb++; /* Loop through, starting at the next */ //沒有足夠buffer發送窗口了,在所有的 Link 上做一次 RR for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) { /* If controller window is full, nothing to do */ if ( (l2cb.controller_xmit_window == 0 #if (BLE_INCLUDED == TRUE) && (p_lcb->transport == BT_TRANSPORT_BR_EDR) #endif ) #if (BLE_INCLUDED == TRUE) || (p_lcb->transport == BT_TRANSPORT_LE && l2cb.controller_le_xmit_window == 0 ) #endif || (l2cb.round_robin_unacked >= l2cb.round_robin_quota) ) break; /* Check for wraparound */ if (p_lcb == &l2cb.lcb_pool[MAX_L2CAP_LINKS]) p_lcb = &l2cb.lcb_pool[0]; if ( (!p_lcb->in_use) || (p_lcb->partial_segment_being_sent) || (p_lcb->link_state != LST_CONNECTED) || (p_lcb->link_xmit_quota != 0) || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) continue; /* See if we can send anything from the Link Queue */ //首先從當前 Link 上的 link_xmit_data_q 中取出數據包並發送 if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL) { l2c_link_send_to_lower (p_lcb, p_buf); } else if (single_write)//如果是 single_write 設置為 TRUE,說明數據包 已經在 link_xmit_data_q 發送了,沒必要在執行下面的 code 了 { /* If only doing one write, break out */ break; } /* If nothing on the link queue, check the channel queue */ //Link 上的 Queue 中沒有東西可以發送,查找 CCB 中的 Queue,直到找到一個為止。 else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL) { l2c_link_send_to_lower (p_lcb, p_buf); } } /* If we finished without using up our quota, no need for a safety check */ if ( (l2cb.controller_xmit_window > 0) && (l2cb.round_robin_unacked < l2cb.round_robin_quota) #if (BLE_INCLUDED == TRUE) && (p_lcb->transport == BT_TRANSPORT_BR_EDR) #endif ) l2cb.check_round_robin = FALSE; #if (BLE_INCLUDED == TRUE) if ( (l2cb.controller_le_xmit_window > 0) && (l2cb.ble_round_robin_unacked < l2cb.ble_round_robin_quota) && (p_lcb->transport == BT_TRANSPORT_LE)) l2cb.ble_check_round_robin = FALSE; #endif } else /* if this is not round-robin service */ { /* If a partial segment is being sent, can't send anything else */ if ( (p_lcb->partial_segment_being_sent) || (p_lcb->link_state != LST_CONNECTED) || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) return; /* See if we can send anything from the link queue */ #if (BLE_INCLUDED == TRUE) while ( ((l2cb.controller_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) || (l2cb.controller_le_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_LE))) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) #else while ( (l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) #endif { if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) == NULL) break; if (!l2c_link_send_to_lower (p_lcb, p_buf)) break; } if (!single_write)//確保不是在鏈路 disc 狀態下 { /* See if we can send anything for any channel */ #if (BLE_INCLUDED == TRUE) while ( ((l2cb.controller_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) || (l2cb.controller_le_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_LE))) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) #else while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) #endif { if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) == NULL)//找到一個數據包來發送 break; if (!l2c_link_send_to_lower (p_lcb, p_buf)) break; } } /* There is a special case where we have readjusted the link quotas and */ /* this link may have sent anything but some other link sent packets so */ /* so we may need a timer to kick off this link's transmissions. */ if ( (p_lcb->link_xmit_data_q.count) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT); } }
最終 l2c_link_check_send_pkts 把數據包交給了 l2c_link_send_to_lower 來做處理,我們的音樂數據包最終也被從某個 CCB 中的隊列出隊列給了 l2c_link_send_to_lower。l2c_link_send_to_lower 主要做了這些事情:
- 如果當前數據包 p_buf 的長度小於 ACL 包的最大值,sent_not_acked 加1,整個 L2CAP 的 controller_xmit_window 減1。然后通過 L2C_LINK_SEND_ACL_DATA 將此數據包發送出去。
- 如果當前數據包 p_buf 的長度大於 ACL 包的最大值,先看看能分成幾個分包(為了求的幾個窗口能容下),然后窗口值減掉這些分包個數,然后將整個數據包交給 L2C_LINK_SEND_ACL_DATA (大於ACL包長度),具體分包發送由 H5(串口) 部分來負責。
/******************************************************************************* ** ** Function l2c_link_send_to_lower ** ** Description This function queues the buffer for HCI transmission ** ** Returns TRUE for success, FALSE for fail ** *******************************************************************************/ static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf) { UINT16 num_segs; UINT16 xmit_window, acl_data_size; if ((p_buf->len <= btu_cb.hcit_acl_pkt_size #if (BLE_INCLUDED == TRUE) && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) || ((p_lcb->transport == BT_TRANSPORT_LE) && (p_buf->len <= btu_cb.hcit_ble_acl_pkt_size)) #else ) #endif ) { if (p_lcb->link_xmit_quota == 0) { #if (BLE_INCLUDED == TRUE) if (p_lcb->transport == BT_TRANSPORT_LE) l2cb.ble_round_robin_unacked++; else #endif l2cb.round_robin_unacked++; } p_lcb->sent_not_acked++; p_buf->layer_specific = 0; #if (BLE_INCLUDED == TRUE) if (p_lcb->transport == BT_TRANSPORT_LE) { l2cb.controller_le_xmit_window--; L2C_LINK_SEND_BLE_ACL_DATA (p_buf); } else #endif { l2cb.controller_xmit_window--; L2C_LINK_SEND_ACL_DATA (p_buf); } } else { #if BLE_INCLUDED == TRUE if (p_lcb->transport == BT_TRANSPORT_LE) { acl_data_size = btu_cb.hcit_ble_acl_data_size; xmit_window = l2cb.controller_le_xmit_window; } else #endif { acl_data_size = btu_cb.hcit_acl_data_size; xmit_window = l2cb.controller_xmit_window; } num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size; /* If doing round-robin, then only 1 segment each time */ if (p_lcb->link_xmit_quota == 0) { num_segs = 1; p_lcb->partial_segment_being_sent = TRUE; } else { /* Multi-segment packet. Make sure it can fit */ if (num_segs > xmit_window) { num_segs = xmit_window; p_lcb->partial_segment_being_sent = TRUE; } if (num_segs > (p_lcb->link_xmit_quota - p_lcb->sent_not_acked)) { num_segs = (p_lcb->link_xmit_quota - p_lcb->sent_not_acked); p_lcb->partial_segment_being_sent = TRUE; } } p_buf->layer_specific = num_segs; #if BLE_INCLUDED == TRUE if (p_lcb->transport == BT_TRANSPORT_LE) { l2cb.controller_le_xmit_window -= num_segs; if (p_lcb->link_xmit_quota == 0) l2cb.ble_round_robin_unacked += num_segs; } else #endif { l2cb.controller_xmit_window -= num_segs; if (p_lcb->link_xmit_quota == 0) l2cb.round_robin_unacked += num_segs; } p_lcb->sent_not_acked += num_segs; #if BLE_INCLUDED == TRUE if (p_lcb->transport == BT_TRANSPORT_LE) { L2C_LINK_SEND_BLE_ACL_DATA(p_buf); } else #endif { L2C_LINK_SEND_ACL_DATA (p_buf); } } #if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE) #if (BLE_INCLUDED == TRUE) if (p_lcb->transport == BT_TRANSPORT_LE) { L2CAP_TRACE_DEBUG ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d", l2cb.controller_le_xmit_window, p_lcb->handle, p_lcb->link_xmit_quota, p_lcb->sent_not_acked, l2cb.ble_round_robin_quota, l2cb.ble_round_robin_unacked); } else #endif { L2CAP_TRACE_DEBUG ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d", l2cb.controller_xmit_window, p_lcb->handle, p_lcb->link_xmit_quota, p_lcb->sent_not_acked, l2cb.round_robin_quota, l2cb.round_robin_unacked); } #endif return TRUE; }
l2c_link_send_to_lower 把數據交給了 L2C_LINK_SEND_ACL_DATA,L2C_LINK_SEND_ACL_DATA 其實是 bte_main_hci_send 函數,bte_main_hci_send 函數通過調用 hci 的接口 transmit_buf 來轉送數據包。transmit_buf 函數作用比較簡單,將此數據包入 tx_q 隊列,串口(H5)的守護線程 bt_hc_worker_thread 會從 tx_q 隊列中獲取數據包,並將其發送。
/****************************************************************************** ** ** Function bte_main_hci_send ** ** Description BTE MAIN API - This function is called by the upper stack to ** send an HCI message. The function displays a protocol trace ** message (if enabled), and then calls the 'transmit' function ** associated with the currently selected HCI transport ** ** Returns None ** ******************************************************************************/ void bte_main_hci_send (BT_HDR *p_msg, UINT16 event) { UINT16 sub_event = event & BT_SUB_EVT_MASK; /* local controller ID */ p_msg->event = event; OS_PRINTF("bte_main_hci_send event 0x%2x\n",event); if((sub_event == LOCAL_BR_EDR_CONTROLLER_ID) || \ (sub_event == LOCAL_BLE_CONTROLLER_ID)) { if (bt_hc_if) bt_hc_if->transmit_buf((TRANSAC)p_msg, \ (char *) (p_msg + 1), \ p_msg->len); else GKI_freebuf(p_msg); } else { APPL_TRACE_ERROR("Invalid Controller ID. Discarding message."); GKI_freebuf(p_msg); } }
在hci中,數據被寫入bt driver中
static int transmit_buf(TRANSAC transac, char *p_buf, int len) { utils_enqueue(&tx_q, (void *) transac); bthc_signal_event(HC_EVENT_TX); return BT_HC_STATUS_SUCCESS; }
external\bluetooth\bluedroid\hci\src\bt_hci_bdroid.c
bt_hc_worker_thread負責處理hci事件
/******************************************************************************* ** ** Function bt_hc_worker_thread ** ** Description Mian worker thread ** ** Returns void * ** *******************************************************************************/ static void *bt_hc_worker_thread(void *arg) { uint16_t events; HC_BT_HDR *p_msg, *p_next_msg; ALOGI("bt_hc_worker_thread started"); prctl(PR_SET_NAME, (unsigned long)"bt_hc_worker", 0, 0, 0); tx_cmd_pkts_pending = FALSE; raise_priority_a2dp(TASK_HIGH_HCI_WORKER); while (lib_running) { pthread_mutex_lock(&hc_cb.mutex); while (ready_events == 0) { pthread_cond_wait(&hc_cb.cond, &hc_cb.mutex); } events = ready_events; ready_events = 0; pthread_mutex_unlock(&hc_cb.mutex); #ifndef HCI_USE_MCT if (events & HC_EVENT_RX) { p_hci_if->rcv(); if ((tx_cmd_pkts_pending == TRUE) && (num_hci_cmd_pkts > 0)) { /* Got HCI Cmd Credits from Controller. * Prepare to send prior pending Cmd packets in the * following HC_EVENT_TX session. */ events |= HC_EVENT_TX; } } #endif if (events & HC_EVENT_PRELOAD) { userial_open(USERIAL_PORT_1); /* Calling vendor-specific part */ if (bt_vnd_if) { bt_vnd_if->op(BT_VND_OP_FW_CFG, NULL); } else { if (bt_hc_cbacks) bt_hc_cbacks->preload_cb(NULL, BT_HC_PRELOAD_FAIL); } } if (events & HC_EVENT_POSTLOAD) { /* Start from SCO related H/W configuration, if SCO configuration * is required. Then, follow with reading requests of getting * ACL data length for both BR/EDR and LE. */ int result = -1; /* Calling vendor-specific part */ if (bt_vnd_if) result = bt_vnd_if->op(BT_VND_OP_SCO_CFG, NULL); if (result == -1) p_hci_if->get_acl_max_len(); } if (events & HC_EVENT_TX) { /* * We will go through every packets in the tx queue. * Fine to clear tx_cmd_pkts_pending. */ tx_cmd_pkts_pending = FALSE; HC_BT_HDR * sending_msg_que[64]; int sending_msg_count = 0; int sending_hci_cmd_pkts_count = 0; utils_lock(); p_next_msg = tx_q.p_first; while (p_next_msg && sending_msg_count < (int)sizeof(sending_msg_que)/sizeof(sending_msg_que[0])) { if ((p_next_msg->event & MSG_EVT_MASK)==MSG_STACK_TO_HC_HCI_CMD) { /* * if we have used up controller's outstanding HCI command * credits (normally is 1), skip all HCI command packets in * the queue. * The pending command packets will be sent once controller * gives back us credits through CommandCompleteEvent or * CommandStatusEvent. */ if ((tx_cmd_pkts_pending == TRUE) || (sending_hci_cmd_pkts_count >= num_hci_cmd_pkts)) { tx_cmd_pkts_pending = TRUE; p_next_msg = utils_getnext(p_next_msg); continue; } sending_hci_cmd_pkts_count++; } p_msg = p_next_msg; p_next_msg = utils_getnext(p_msg); utils_remove_from_queue_unlocked(&tx_q, p_msg); sending_msg_que[sending_msg_count++] = p_msg; } utils_unlock(); int i; for(i = 0; i < sending_msg_count; i++) p_hci_if->send(sending_msg_que[i]); if (tx_cmd_pkts_pending == TRUE) BTHCDBG("Used up Tx Cmd credits"); } if (events & HC_EVENT_LPM_ENABLE) { lpm_enable(TRUE); } if (events & HC_EVENT_LPM_DISABLE) { lpm_enable(FALSE); } if (events & HC_EVENT_LPM_IDLE_TIMEOUT) { lpm_wake_deassert(); } if (events & HC_EVENT_LPM_ALLOW_SLEEP) { lpm_allow_bt_device_sleep(); } if (events & HC_EVENT_LPM_WAKE_DEVICE) { lpm_wake_assert(); } if (events & HC_EVENT_EPILOG) { /* Calling vendor-specific part */ if (bt_vnd_if) bt_vnd_if->op(BT_VND_OP_EPILOG, NULL); else break; // equivalent to HC_EVENT_EXIT } if (events & HC_EVENT_EXIT) break; } ALOGI("bt_hc_worker_thread exiting"); lib_running = 0; pthread_exit(NULL); return NULL; // compiler friendly }
send to : userial.c
最終write 系統調操作driver的 fops 操作接口集:
/******************************************************************************* ** ** Function userial_write ** ** Description Write data to the userial port ** ** Returns Number of bytes actually written to the userial port. This ** may be less than len. ** *******************************************************************************/ uint16_t userial_write(uint16_t msg_id, uint8_t *p_data, uint16_t len) { int ret, total = 0; while(len != 0) { #if defined(ENABLE_USERIAL_TIMING_LOGS) && (ENABLE_USERIAL_TIMING_LOGS==TRUE) log_userial_tx_timing(len); #endif ret = write(userial_cb.fd, p_data+total, len); total += ret; len -= ret; } return ((uint16_t)total); }
write /dev/hidraw* 節點數據下發流程大致如上,敬請勘誤~!