#include "include.h" #include "wireless_client.h" #include "wireless_init.h" #include "usb_audio.h" #include "usb_hid.h" #include "usb_vendor.h" #include "utils_ring_buffer.h" #define TRACE_EN 1 #define TRACE_RATE_EN 0 #define TRACE_RATE_PERIOD 400 // Unit: connection interval #define CFG_CONN_INTV 2 // Unit: 1.25ms #define CFG_CONN_LAT 199 // Unit: connection interval #define CFG_CONN_TO 150 // Unit: 10ms #if TRACE_EN #define TRACE(...) my_printf("[WIRELESS] ");\ my_printf(__VA_ARGS__) #define TRACE_R(...) my_print_r(__VA_ARGS__); #else #define TRACE(...) #define TRACE_R(...) #endif // TRACE_EN #define SWDIAG(func, filed, value) #define DBG_FLOW_CTRL_EN 0 static struct { uint16_t con_handle; bool is_bond; wireless_addr_info_typedef bond_addr_info; } wireless_cb AT(.ram_text.wireless.sta); static const uint8_t adv_match_seq[4] = {0x03, 0xff, 0x42, 0x06}; /* Expect set to 2 + payload_length(transmit: e.g USB Vendor is 63) */ static uint8_t transmit_buffer[1 + 2 + 63]; #if TRACE_RATE_EN static uint32_t statistic_frame_count; static uint32_t statistic_hid_pkts_count; #endif /* The max number of caches uploads from transport layer cannot be modify curr Ver. */ static struct { uint8_t * tbl[5]; uint8_t siz[5]; uint8_t widx; uint8_t ridx; uint8_t cnt; bt_timer_mem_t fc_timer_intance; bt_timer_handle_t fc_timer_handle; } wireless_rxbuf_cb AT(.ram_text.wireless.xfer); #define WIRELESS_RXBUF_RELEASE_TIMEOUT 50 /* ms */ #define wireless_rxbuf_is_empty() (wireless_rxbuf_cb.cnt == 0) #define wireless_rxbuf_r_advance() {wireless_rxbuf_cb.ridx=(wireless_rxbuf_cb.ridx+1)%5;wireless_rxbuf_cb.cnt--;} #define wireless_rxbuf_r_head_buf() (wireless_rxbuf_cb.tbl[wireless_rxbuf_cb.ridx]) #define wireless_rxbuf_r_head_siz() (wireless_rxbuf_cb.siz[wireless_rxbuf_cb.ridx]) #define wireless_rxbuf_w_advance() {wireless_rxbuf_cb.widx=(wireless_rxbuf_cb.widx+1)%5;wireless_rxbuf_cb.cnt++;} #define wireless_rxbuf_w_head_buf() (wireless_rxbuf_cb.tbl[wireless_rxbuf_cb.widx]) #define wireless_rxbuf_w_head_siz() (wireless_rxbuf_cb.siz[wireless_rxbuf_cb.widx]) #define wireless_conn_is_exist() (wireless_cb.con_handle != 0) AT(.com_text.wireless.transmit) static void wireless_packet_process(uint8_t *buf, uint32_t size) { u8 head = buf[WIRELESS_HEAD_POS]; u8 pos = WIRELESS_PAYLOAD_POS; u8 *hid_data = NULL; u8 *mic_data = NULL; u8 *cmd_data = NULL, cmd_len = 0, cmd_type; /* Unpack data */ if (head & WIRELESS_HEADER_HID_BIT) { hid_data = &buf[pos]; pos += WIRELESS_DATA_LENGTH_HID; #if TRACE_RATE_EN if (statistic_frame_count > 0 && statistic_frame_count <= TRACE_RATE_PERIOD) { statistic_hid_pkts_count++; } #endif } if (head & WIRELESS_HEADER_AUDIO_BIT) { mic_data = &buf[pos]; pos += WIRELESS_DATA_LENGTH_AUDIO; } if (head & WIRELESS_HEADER_CTRL_BIT) { cmd_data = &buf[pos]; cmd_type = cmd_data[WIRELESS_CMD_DATA_TYPE_POS]; cmd_len = cmd_data[WIRELESS_CMD_DATA_LENGTH_POS]; pos += 2 + cmd_len; } /* Check length valid */ if (pos != size) { TRACE("data length err: %d %d\n", pos, size); return; } /* Process */ #if USB_HID_EN if (hid_data && sys_cb.usb_is_active) { usb_hid_buffer_push(hid_data, WIRELESS_DATA_LENGTH_HID); thread_driver_msg_post(THREAD_DRIVER_MSG_HID_IN); } #else (void)hid_data; #endif #if USB_MIC_EN if (mic_data && sys_cb.usb_is_active) { SWDIAG(RECV, IO, ENTER); uda_mic_buffer_prepare(mic_data, WIRELESS_DATA_LENGTH_AUDIO); thread_driver_msg_post(THREAD_DRIVER_MSG_UAC_DEC); SWDIAG(RECV, IO, EXIT); } #else (void)mic_data; #endif if (cmd_data) { TRACE("receive cmd - type:0x%02x data[%d]:\n", cmd_type, cmd_len); TRACE_R(&cmd_data[WIRELESS_CMD_DATA_VALUE_POS], cmd_len); switch (cmd_type) { default: break; } } } /* AT(.com_text.wireless.transmit) NO_INLINE static void wireless_packet_rxbuf_check(void) { #if DBG_FLOW_CTRL_EN uint8_t *packet = wireless_rxbuf_r_head_buf(); uint8_t size = wireless_rxbuf_r_head_siz(); if (wireless_rxbuf_is_empty()) { return; } if (!wireless_conn_is_exist()) { while (!wireless_rxbuf_is_empty()) { wireless_rxbuf_release(packet); wireless_rxbuf_r_advance(); packet = wireless_rxbuf_r_head_buf(); } return; } if ((packet[WIRELESS_HEAD_POS] & WIRELESS_HEADER_HID_BIT) #if USB_HID_EN && (!usb_hid_buffer_push_check()) #endif // USB_HID_EN ) { return; } bt_alarm_timer_stop(wireless_rxbuf_cb.fc_timer_handle); wireless_packet_process(packet, size); GLOBAL_INT_DISABLE(); wireless_rxbuf_release(packet); wireless_rxbuf_r_advance(); GLOBAL_INT_RESTORE(); if (!wireless_rxbuf_is_empty()) { bt_alarm_timer_start(wireless_rxbuf_cb.fc_timer_handle); } #endif } */ AT(.text.wireless.transmit) static void wireless_packet_rxbuf_timeout_callback(bt_timer_handle_t handle) { if (!wireless_rxbuf_is_empty()) { do { wireless_rxbuf_release(wireless_rxbuf_r_head_buf()); wireless_rxbuf_r_advance(); } while (!wireless_conn_is_exist() && !wireless_rxbuf_is_empty()); if (!wireless_rxbuf_is_empty()) { bt_alarm_timer_start(wireless_rxbuf_cb.fc_timer_handle); } } } AT(.com_text.dbg.statistic) void wireless_frame_start_hook(void) { #if TRACE_RATE_EN if (statistic_frame_count <= TRACE_RATE_PERIOD) { statistic_frame_count++; } #endif } static void wireless_event_packet_handler(uint8_t event_type, uint8_t *packet, uint8_t size) { switch (event_type) { case WIRELESS_EVENT_CONNECTED: TRACE("connected - con_handle: 0x%04x\n", GET_LE16(&packet[0])); wireless_cb.con_handle = GET_LE16(&packet[0]); memcpy((u8 *)&wireless_cb.bond_addr_info, &packet[2], 7); /* Record link info */ bsp_wireless_link_info_write(&wireless_cb.bond_addr_info); wireless_cb.is_bond = true; /* Trigger event that notify peer the mic and usb status */ thread_driver_msg_post(THREAD_DRIVER_MSG_UAC_MIC_CTRL); thread_driver_msg_post(THREAD_DRIVER_MSG_USB_IND); wireless_frame_start_hook_register(wireless_cb.con_handle, wireless_frame_start_hook); break; case WIRELESS_EVENT_DISCONNECTED: TRACE("disconnected - con_handle: 0x%04x reason: 0x%02x\n", GET_LE16(&packet[0]), packet[2]); wireless_cb.con_handle = 0; wireless_scan_ctrl(true); break; case WIRELESS_EVENT_ADV_REPORT: { wireless_adv_report_t *adv_msg = (wireless_adv_report_t *)&packet[0]; wireless_adv_slice_t *adv_sli = (wireless_adv_slice_t *)adv_msg->report; bool found = false; TRACE("adv report - type: 0x%02x addr: ", adv_msg->addr_type); TRACE_R(adv_msg->addr, 6); do { if (wireless_cb.is_bond) { if (0 == memcmp(adv_msg->addr, wireless_cb.bond_addr_info.addr, 6)) { /* It's the last connected device */ found = true; break; } // No break: connects to the last device or any non-directional broadcast device. } /* Try to find the specified seq in undir adv data */ while (((u8 *)adv_sli - adv_msg->report) < adv_msg->report_len) { if (0 == memcmp(adv_sli, adv_match_seq, 4)) { found = true; break; } adv_sli = (wireless_adv_slice_t *)(adv_sli->data + adv_sli->length - 1); } } while (0); if (found) { wireless_scan_ctrl(false); wireless_connect_by_addr(adv_msg->addr, adv_msg->addr_type, CFG_CONN_INTV, CFG_CONN_LAT, CFG_CONN_TO); } break; } default: break; } } AT(.com_text.wireless.transmit) static void wireless_receive_handler(uint16_t con_handle, uint8_t *packet, uint8_t size) { #if DBG_FLOW_CTRL_EN if (con_handle == wireless_cb.con_handle) { if (wireless_rxbuf_is_empty()) { bt_alarm_timer_start(wireless_rxbuf_cb.fc_timer_handle); } else { /* ASSERT: fc timer already start */ } wireless_rxbuf_cb.tbl[wireless_rxbuf_cb.widx] = packet; wireless_rxbuf_cb.siz[wireless_rxbuf_cb.widx] = size; wireless_rxbuf_w_advance(); } else { wireless_rxbuf_release(packet); } #else if (con_handle == wireless_cb.con_handle) { wireless_packet_process(packet, size); } wireless_rxbuf_release(packet); #endif } void wireless_client_init(void) { TRACE("master init\n"); memset(&wireless_cb, 0x00, sizeof(wireless_cb)); memset(&wireless_rxbuf_cb, 0x00, sizeof(wireless_rxbuf_cb)); wireless_cb.is_bond = bsp_wireless_link_info_read(&wireless_cb.bond_addr_info); bt_alarm_timer_mem_extend(&wireless_rxbuf_cb.fc_timer_intance, sizeof(wireless_rxbuf_cb.fc_timer_intance)); bt_alarm_timer_acquire(&wireless_rxbuf_cb.fc_timer_handle, WIRELESS_RXBUF_RELEASE_TIMEOUT, 0, wireless_packet_rxbuf_timeout_callback); wireless_event_handler_register(wireless_event_packet_handler); wireless_receive_handler_register(wireless_receive_handler); } /* void ble_app_proc(void) { #if TRACE_RATE_EN if (statistic_frame_count > TRACE_RATE_PERIOD) { TRACE("rate: %dhids/%dFrames\n", statistic_hid_pkts_count, TRACE_RATE_PERIOD); statistic_hid_pkts_count = 0; statistic_frame_count = 0; } #endif wireless_packet_rxbuf_check(); } */ /* Single packet, no compose with HID or AUDIO */ void wireless_send_ctrl_cmd(uint8_t type, uint8_t length, uint8_t *value) { u8 *cmd_data = &transmit_buffer[WIRELESS_PAYLOAD_POS]; u8 packet_len = 1 + 2 + length; u32 tick, timeout = 500; u8 mtu = 0; wireless_mtu_get(wireless_cb.con_handle, &mtu); if (packet_len > mtu || packet_len > sizeof(transmit_buffer)) { return; } cmd_data[WIRELESS_CMD_DATA_TYPE_POS] = type; cmd_data[WIRELESS_CMD_DATA_LENGTH_POS] = length; if (length && value) { memcpy(&cmd_data[WIRELESS_CMD_DATA_VALUE_POS], value, length); } transmit_buffer[WIRELESS_HEAD_POS] = WIRELESS_HEADER_CTRL_BIT; tick = tick_get(); while (wireless_send_for_con(wireless_cb.con_handle, transmit_buffer, packet_len)) { if (tick_check_expire(tick, timeout)) { break; } delay_5ms(1); } }