fix reconnect
This commit is contained in:
+8
-4
@@ -25,6 +25,9 @@ endif ()
|
|||||||
# ====================================================================================
|
# ====================================================================================
|
||||||
set(PICO_BOARD pico2_w CACHE STRING "Board type")
|
set(PICO_BOARD pico2_w CACHE STRING "Board type")
|
||||||
|
|
||||||
|
#set(PICO_CYW43_SUPPORTED 1)
|
||||||
|
#set(PICO_CYW43_ARCH_THREADSAFE_BACKGROUND 1)
|
||||||
|
|
||||||
# Pull in Raspberry Pi Pico SDK (must be before project)
|
# Pull in Raspberry Pi Pico SDK (must be before project)
|
||||||
include(pico_sdk_import.cmake)
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
@@ -38,9 +41,9 @@ pico_sdk_init()
|
|||||||
add_executable(ds5-bridge
|
add_executable(ds5-bridge
|
||||||
src/usb_descriptors.c
|
src/usb_descriptors.c
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/bt.cpp
|
|
||||||
src/usb.cpp
|
src/usb.cpp
|
||||||
src/audio.cpp
|
src/audio.cpp
|
||||||
|
src/bt.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(wdl_resampler STATIC
|
add_library(wdl_resampler STATIC
|
||||||
@@ -53,13 +56,14 @@ target_include_directories(wdl_resampler PUBLIC
|
|||||||
|
|
||||||
target_compile_options(wdl_resampler PRIVATE -fsigned-char)
|
target_compile_options(wdl_resampler PRIVATE -fsigned-char)
|
||||||
target_compile_options(ds5-bridge PRIVATE -fsigned-char)
|
target_compile_options(ds5-bridge PRIVATE -fsigned-char)
|
||||||
|
target_compile_definitions(ds5-bridge PRIVATE CYW43_LWIP=0)
|
||||||
|
|
||||||
pico_set_program_name(ds5-bridge "ds5-bridge")
|
pico_set_program_name(ds5-bridge "ds5-bridge")
|
||||||
pico_set_program_version(ds5-bridge "0.1")
|
pico_set_program_version(ds5-bridge "0.1")
|
||||||
|
|
||||||
# Modify the below lines to enable/disable output over UART/USB
|
# Modify the below lines to enable/disable output over UART/USB
|
||||||
pico_enable_stdio_uart(ds5-bridge 0)
|
pico_enable_stdio_uart(ds5-bridge 1)
|
||||||
pico_enable_stdio_usb(ds5-bridge 1)
|
pico_enable_stdio_usb(ds5-bridge 0)
|
||||||
|
|
||||||
# Add the standard library to the build
|
# Add the standard library to the build
|
||||||
target_link_libraries(ds5-bridge
|
target_link_libraries(ds5-bridge
|
||||||
@@ -81,7 +85,7 @@ target_link_libraries(ds5-bridge
|
|||||||
hardware_interp
|
hardware_interp
|
||||||
hardware_timer
|
hardware_timer
|
||||||
pico_btstack_classic
|
pico_btstack_classic
|
||||||
pico_cyw43_arch_none
|
pico_cyw43_arch_threadsafe_background
|
||||||
pico_btstack_cyw43
|
pico_btstack_cyw43
|
||||||
tinyusb_device
|
tinyusb_device
|
||||||
tinyusb_board
|
tinyusb_board
|
||||||
|
|||||||
+3
-14
@@ -6,7 +6,6 @@
|
|||||||
#include "bt.h"
|
#include "bt.h"
|
||||||
#include "resample.h"
|
#include "resample.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "pico/time.h"
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#define INPUT_CHANNELS 4
|
#define INPUT_CHANNELS 4
|
||||||
@@ -19,11 +18,9 @@ static WDL_Resampler resampler;
|
|||||||
static uint8_t reportSeqCounter = 0;
|
static uint8_t reportSeqCounter = 0;
|
||||||
static uint8_t packetCounter = 0;
|
static uint8_t packetCounter = 0;
|
||||||
|
|
||||||
void process_audio() {
|
void audio_loop() {
|
||||||
// 1. 读取 USB 音频数据
|
// 1. 读取 USB 音频数据
|
||||||
if (!tud_audio_available()){
|
if (!tud_audio_available()) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t raw[1024];
|
int16_t raw[1024];
|
||||||
uint32_t bytes_read = tud_audio_read(raw, sizeof(raw));
|
uint32_t bytes_read = tud_audio_read(raw, sizeof(raw));
|
||||||
@@ -80,17 +77,9 @@ void process_audio() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void core1_entry() {
|
void audio_init() {
|
||||||
resampler.SetMode(true, 0, false);
|
resampler.SetMode(true, 0, false);
|
||||||
resampler.SetRates(48000, 3000);
|
resampler.SetRates(48000, 3000);
|
||||||
resampler.SetFeedMode(true);
|
resampler.SetFeedMode(true);
|
||||||
resampler.Prealloc(2, 480, 32);
|
resampler.Prealloc(2, 480, 32);
|
||||||
|
|
||||||
while (1) {
|
|
||||||
if (!tud_ready()) {
|
|
||||||
sleep_ms(10);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
process_audio();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -5,6 +5,7 @@
|
|||||||
#ifndef DS5_BRIDGE_AUDIO_H
|
#ifndef DS5_BRIDGE_AUDIO_H
|
||||||
#define DS5_BRIDGE_AUDIO_H
|
#define DS5_BRIDGE_AUDIO_H
|
||||||
|
|
||||||
void core1_entry();
|
void audio_init();
|
||||||
|
void audio_loop();
|
||||||
|
|
||||||
#endif //DS5_BRIDGE_AUDIO_H
|
#endif //DS5_BRIDGE_AUDIO_H
|
||||||
+125
-48
@@ -3,9 +3,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "bt.h"
|
#include "bt.h"
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -14,16 +16,25 @@
|
|||||||
#include "pico/cyw43_arch.h"
|
#include "pico/cyw43_arch.h"
|
||||||
#include "pico/stdio.h"
|
#include "pico/stdio.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "pico/multicore.h"
|
||||||
|
#include "pico/sync.h"
|
||||||
|
#include "classic/sdp_server.h"
|
||||||
|
|
||||||
|
static btstack_packet_callback_registration_t hci_event_callback_registration, l2cap_event_callback_registration;
|
||||||
|
|
||||||
static btstack_packet_callback_registration_t hci_event_callback_registration,l2cap_event_callback_registration;
|
|
||||||
static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||||
|
|
||||||
static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||||
|
|
||||||
static bd_addr_t current_device_addr;
|
static bd_addr_t current_device_addr;
|
||||||
static bool device_found = false;
|
static bool device_found = false;
|
||||||
|
static bool new_pair = false;
|
||||||
static uint16_t hid_control_cid;
|
static uint16_t hid_control_cid;
|
||||||
static uint16_t hid_interrupt_cid;
|
static uint16_t hid_interrupt_cid;
|
||||||
static bt_data_callback_t bt_data_callback = NULL;
|
static bt_data_callback_t bt_data_callback = nullptr;
|
||||||
std::unordered_map<uint8_t, std::vector<uint8_t>> feature_data;
|
std::unordered_map<uint8_t, std::vector<uint8_t> > feature_data;
|
||||||
|
static std::queue<std::vector<uint8_t> > send_queue;
|
||||||
|
static critical_section_t queue_lock;
|
||||||
|
|
||||||
void bt_register_data_callback(bt_data_callback_t callback) {
|
void bt_register_data_callback(bt_data_callback_t callback) {
|
||||||
bt_data_callback = callback;
|
bt_data_callback = callback;
|
||||||
@@ -41,22 +52,38 @@ void bt_send_control(uint8_t *data, uint16_t len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bt_l2cap_init() {
|
||||||
|
l2cap_event_callback_registration.callback = &l2cap_packet_handler;
|
||||||
|
l2cap_add_event_handler(&l2cap_event_callback_registration);
|
||||||
|
// 修复重连后自动断开的关键点
|
||||||
|
sdp_init();
|
||||||
|
l2cap_register_service(l2cap_packet_handler, PSM_HID_CONTROL, 672, LEVEL_2);
|
||||||
|
l2cap_register_service(l2cap_packet_handler, PSM_HID_INTERRUPT, 672, LEVEL_2);
|
||||||
|
|
||||||
|
l2cap_init();
|
||||||
|
}
|
||||||
|
|
||||||
int bt_init() {
|
int bt_init() {
|
||||||
if (cyw43_arch_init()) {
|
if (cyw43_arch_init()) {
|
||||||
printf("Failed to initialize CYW43\n");
|
printf("Failed to initialize CYW43\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
l2cap_init();
|
critical_section_init(&queue_lock);
|
||||||
|
|
||||||
|
bt_l2cap_init();
|
||||||
|
|
||||||
|
// SSP (Secure Simple Pairing)
|
||||||
|
gap_ssp_set_enable(true);
|
||||||
|
gap_secure_connections_enable(true);
|
||||||
|
gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
|
||||||
|
gap_ssp_set_authentication_requirement(SSP_IO_AUTHREQ_MITM_PROTECTION_NOT_REQUIRED_GENERAL_BONDING);
|
||||||
|
|
||||||
gap_connectable_control(1);
|
gap_connectable_control(1);
|
||||||
gap_discoverable_control(1);
|
gap_discoverable_control(1);
|
||||||
l2cap_register_service(l2cap_packet_handler, PSM_HID_CONTROL, 0xffff, LEVEL_2);
|
|
||||||
l2cap_register_service(l2cap_packet_handler, PSM_HID_INTERRUPT, 0xffff, LEVEL_2);
|
|
||||||
|
|
||||||
hci_event_callback_registration.callback = &hci_packet_handler;
|
hci_event_callback_registration.callback = &hci_packet_handler;
|
||||||
hci_add_event_handler(&hci_event_callback_registration);
|
hci_add_event_handler(&hci_event_callback_registration);
|
||||||
l2cap_event_callback_registration.callback = & l2cap_packet_handler;
|
|
||||||
l2cap_add_event_handler(&l2cap_event_callback_registration);
|
|
||||||
|
|
||||||
hci_power_control(HCI_POWER_ON);
|
hci_power_control(HCI_POWER_ON);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -65,12 +92,12 @@ int bt_init() {
|
|||||||
/*int main() {
|
/*int main() {
|
||||||
stdio_init_all();
|
stdio_init_all();
|
||||||
|
|
||||||
while (!stdio_usb_connected()) {
|
/*while (!stdio_usb_connected()) {
|
||||||
sleep_ms(100);
|
sleep_ms(100);
|
||||||
}
|
}
|
||||||
printf("USB Serial connected!\n");
|
printf("USB Serial connected!\n");#1#
|
||||||
|
|
||||||
init();
|
bt_init();
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
sleep_ms(10);
|
sleep_ms(10);
|
||||||
@@ -85,7 +112,7 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
switch (event_type) {
|
switch (event_type) {
|
||||||
case BTSTACK_EVENT_STATE: {
|
case BTSTACK_EVENT_STATE: {
|
||||||
const uint8_t state = btstack_event_state_get_state(packet);
|
const uint8_t state = btstack_event_state_get_state(packet);
|
||||||
printf("[BT] State: %u\n",state);
|
printf("[BT] State: %u\n", state);
|
||||||
if (state == HCI_STATE_WORKING) {
|
if (state == HCI_STATE_WORKING) {
|
||||||
printf("[BT] Stack ready, start inquiry\n");
|
printf("[BT] Stack ready, start inquiry\n");
|
||||||
gap_inquiry_start(30);
|
gap_inquiry_start(30);
|
||||||
@@ -111,7 +138,7 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
|
|
||||||
// CoD 0x002508 = Gamepad (Major: Peripheral, Minor: Gamepad)
|
// CoD 0x002508 = Gamepad (Major: Peripheral, Minor: Gamepad)
|
||||||
if ((cod & 0x000F00) == 0x000500) {
|
if ((cod & 0x000F00) == 0x000500) {
|
||||||
printf("[HCI] Gamepad found: %s (CoD: 0x%06x)\n", bd_addr_to_str(addr), (unsigned int)cod);
|
printf("[HCI] Gamepad found: %s (CoD: 0x%06x)\n", bd_addr_to_str(addr), (unsigned int) cod);
|
||||||
bd_addr_copy(current_device_addr, addr);
|
bd_addr_copy(current_device_addr, addr);
|
||||||
device_found = true;
|
device_found = true;
|
||||||
gap_inquiry_stop();
|
gap_inquiry_stop();
|
||||||
@@ -124,6 +151,7 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
printf("[HCI] Inquiry complete\n");
|
printf("[HCI] Inquiry complete\n");
|
||||||
if (device_found) {
|
if (device_found) {
|
||||||
printf("[HCI] Connecting to %s...\n", bd_addr_to_str(current_device_addr));
|
printf("[HCI] Connecting to %s...\n", bd_addr_to_str(current_device_addr));
|
||||||
|
new_pair = true;
|
||||||
hci_send_cmd(&hci_create_connection, current_device_addr,
|
hci_send_cmd(&hci_create_connection, current_device_addr,
|
||||||
hci_usable_acl_packet_types(), 0, 0, 0, 1);
|
hci_usable_acl_packet_types(), 0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
@@ -135,8 +163,9 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
printf("[HCI] CmdStatus %s(0x%04X) status=0x%02X\n", opcode_to_str(opcode), opcode, status);
|
printf("[HCI] CmdStatus %s(0x%04X) status=0x%02X\n", opcode_to_str(opcode), opcode, status);
|
||||||
if (opcode == HCI_OPCODE_HCI_CREATE_CONNECTION && status != ERROR_CODE_SUCCESS) {
|
if (opcode == HCI_OPCODE_HCI_CREATE_CONNECTION && status != ERROR_CODE_SUCCESS) {
|
||||||
device_found = false;
|
device_found = false;
|
||||||
|
new_pair = false;
|
||||||
printf("[HCI] Create connection rejected, restart inquiry\n");
|
printf("[HCI] Create connection rejected, restart inquiry\n");
|
||||||
gap_inquiry_start(30);
|
// gap_inquiry_start(30);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -158,8 +187,9 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
hci_send_cmd(&hci_authentication_requested, handle);
|
hci_send_cmd(&hci_authentication_requested, handle);
|
||||||
} else {
|
} else {
|
||||||
device_found = false;
|
device_found = false;
|
||||||
|
new_pair = false;
|
||||||
printf("[HCI] ACL connect failed status=0x%02X, restart inquiry\n", status);
|
printf("[HCI] ACL connect failed status=0x%02X, restart inquiry\n", status);
|
||||||
gap_inquiry_start(30);
|
// gap_inquiry_start(30);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -169,9 +199,15 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
hci_event_link_key_request_get_bd_addr(packet, addr);
|
hci_event_link_key_request_get_bd_addr(packet, addr);
|
||||||
link_key_t link_key;
|
link_key_t link_key;
|
||||||
link_key_type_t link_key_type;
|
link_key_type_t link_key_type;
|
||||||
if (gap_get_link_key_for_bd_addr(addr, link_key, &link_key_type)) {
|
bool link = gap_get_link_key_for_bd_addr(addr, link_key, &link_key_type);
|
||||||
|
printf("[HCI] Link key: ");
|
||||||
|
for (int i = 0; i < sizeof(link_key_t); i++) {
|
||||||
|
printf("%02X", link_key[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
if (link) {
|
||||||
printf("[HCI] Link key request from %s, reply stored key type=%u\n", bd_addr_to_str(addr),
|
printf("[HCI] Link key request from %s, reply stored key type=%u\n", bd_addr_to_str(addr),
|
||||||
(unsigned int)link_key_type);
|
(unsigned int) link_key_type);
|
||||||
hci_send_cmd(&hci_link_key_request_reply, addr, link_key);
|
hci_send_cmd(&hci_link_key_request_reply, addr, link_key);
|
||||||
} else {
|
} else {
|
||||||
printf("[HCI] Link key request from %s, no key, force re-pair\n", bd_addr_to_str(addr));
|
printf("[HCI] Link key request from %s, no key, force re-pair\n", bd_addr_to_str(addr));
|
||||||
@@ -202,8 +238,11 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
printf("[HCI] Authentication complete handle=0x%04X status=0x%02X\n", handle, status);
|
printf("[HCI] Authentication complete handle=0x%04X status=0x%02X\n", handle, status);
|
||||||
if (status != ERROR_CODE_SUCCESS) {
|
if (status != ERROR_CODE_SUCCESS) {
|
||||||
printf("[HCI] Authentication failed, drop stored key for %s\n", bd_addr_to_str(current_device_addr));
|
printf("[HCI] Authentication failed, drop stored key for %s\n", bd_addr_to_str(current_device_addr));
|
||||||
|
multicore_lockout_start_blocking();
|
||||||
gap_drop_link_key_for_bd_addr(current_device_addr);
|
gap_drop_link_key_for_bd_addr(current_device_addr);
|
||||||
}else {
|
multicore_lockout_end_blocking();
|
||||||
|
// gap_inquiry_start(30);
|
||||||
|
} else {
|
||||||
hci_send_cmd(&hci_set_connection_encryption, handle, 1);
|
hci_send_cmd(&hci_set_connection_encryption, handle, 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -216,10 +255,14 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
printf("[HCI] Encryption change handle=0x%04X status=0x%02X enabled=%u\n", handle, status, enabled);
|
printf("[HCI] Encryption change handle=0x%04X status=0x%02X enabled=%u\n", handle, status, enabled);
|
||||||
if (status == ERROR_CODE_SUCCESS && enabled) {
|
if (status == ERROR_CODE_SUCCESS && enabled) {
|
||||||
printf("[L2CAP] Open HID channels\n");
|
printf("[L2CAP] Open HID channels\n");
|
||||||
if (hid_control_cid == 0) {
|
if (new_pair) {
|
||||||
l2cap_create_channel(l2cap_packet_handler, current_device_addr, PSM_HID_CONTROL, 0xffff, &hid_control_cid);
|
if (hid_control_cid == 0) {
|
||||||
} else if (hid_interrupt_cid == 0){
|
l2cap_create_channel(l2cap_packet_handler, current_device_addr, PSM_HID_CONTROL, 0xffff,
|
||||||
l2cap_create_channel(l2cap_packet_handler,current_device_addr,PSM_HID_INTERRUPT,0xffff,&hid_interrupt_cid);
|
&hid_control_cid);
|
||||||
|
} else if (hid_interrupt_cid == 0) {
|
||||||
|
l2cap_create_channel(l2cap_packet_handler, current_device_addr, PSM_HID_INTERRUPT, 0xffff,
|
||||||
|
&hid_interrupt_cid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -239,12 +282,15 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
}
|
}
|
||||||
|
|
||||||
case HCI_EVENT_DISCONNECTION_COMPLETE: {
|
case HCI_EVENT_DISCONNECTION_COMPLETE: {
|
||||||
|
gap_connectable_control(1);
|
||||||
|
gap_discoverable_control(1);
|
||||||
const uint8_t reason = hci_event_disconnection_complete_get_reason(packet);
|
const uint8_t reason = hci_event_disconnection_complete_get_reason(packet);
|
||||||
device_found = false;
|
device_found = false;
|
||||||
|
new_pair = false;
|
||||||
hid_control_cid = 0;
|
hid_control_cid = 0;
|
||||||
hid_interrupt_cid = 0;
|
hid_interrupt_cid = 0;
|
||||||
feature_data.clear();
|
feature_data.clear();
|
||||||
printf("[HCI] Disconnected reason=0x%02X, restart inquiry\n", reason);
|
printf("[HCI] Disconnected reason=0x%02X, start inquiry\n", reason);
|
||||||
gap_inquiry_start(30);
|
gap_inquiry_start(30);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -256,20 +302,21 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
|
|
||||||
if (packet_type == L2CAP_DATA_PACKET) {
|
if (packet_type == L2CAP_DATA_PACKET) {
|
||||||
if (channel == hid_interrupt_cid) {
|
if (channel == hid_interrupt_cid) {
|
||||||
printf("[L2CAP] HID Interrupt data len=%u\n", size);
|
// printf("[L2CAP] HID Interrupt data len=%u\n", size);
|
||||||
printf_hexdump(packet, size);
|
// printf_hexdump(packet, size);
|
||||||
bt_data_callback(INTERRUPT, packet, size);
|
bt_data_callback(INTERRUPT, packet, size);
|
||||||
} else if (channel == hid_control_cid) {
|
} else if (channel == hid_control_cid) {
|
||||||
if (size > 1 && packet[0] == 0xA3) {
|
if (size > 1 && packet[0] == 0xA3) {
|
||||||
uint8_t report_id = packet[1];
|
uint8_t report_id = packet[1];
|
||||||
feature_data[report_id].assign(packet + 1, packet + size);
|
feature_data[report_id].assign(packet + 1, packet + size);
|
||||||
printf("[L2CAP] Stored Feature Report %d, len=%u\n", report_id, size - 1);
|
printf("[L2CAP] Stored Feature Report 0x%02X, len=%u\n", report_id, size - 1);
|
||||||
}
|
}
|
||||||
printf("[L2CAP] HID Control data len=%u\n", size);
|
printf("[L2CAP] HID Control data len=%u\n", size);
|
||||||
printf_hexdump(packet, size);
|
printf_hexdump(packet, size);
|
||||||
bt_data_callback(CONTROL, packet, size);
|
bt_data_callback(CONTROL, packet, size);
|
||||||
} else {
|
} else {
|
||||||
printf("[L2CAP] Data on unknown channel 0x%04X (Interrupt: 0x%04X, Control: 0x%04X)\n", channel, hid_interrupt_cid, hid_control_cid);
|
printf("[L2CAP] Data on unknown channel 0x%04X (Interrupt: 0x%04X, Control: 0x%04X)\n",
|
||||||
|
channel, hid_interrupt_cid, hid_control_cid);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -278,15 +325,17 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
switch (event_type) {
|
switch (event_type) {
|
||||||
case L2CAP_EVENT_CHANNEL_OPENED: {
|
case L2CAP_EVENT_CHANNEL_OPENED: {
|
||||||
const uint8_t status = l2cap_event_channel_opened_get_status(packet);
|
const uint8_t status = l2cap_event_channel_opened_get_status(packet);
|
||||||
|
const uint16_t local_cid = l2cap_event_channel_opened_get_local_cid(packet);
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
const uint16_t psm = l2cap_event_channel_opened_get_psm(packet);
|
const uint16_t psm = l2cap_event_channel_opened_get_psm(packet);
|
||||||
|
|
||||||
if (psm == PSM_HID_CONTROL) {
|
if (psm == PSM_HID_CONTROL) {
|
||||||
printf("[L2CAP] HID Control opened cid=0x%04X\n", hid_control_cid);
|
printf("[L2CAP] HID Control opened cid=0x%04X\n", local_cid);
|
||||||
|
hid_control_cid = local_cid;
|
||||||
} else if (psm == PSM_HID_INTERRUPT) {
|
} else if (psm == PSM_HID_INTERRUPT) {
|
||||||
printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", hid_interrupt_cid);
|
printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", local_cid);
|
||||||
|
hid_interrupt_cid = local_cid;
|
||||||
|
|
||||||
printf("Ready\n");
|
printf("Init DualSense\n");
|
||||||
uint8_t get_feature[41] = {
|
uint8_t get_feature[41] = {
|
||||||
0x43,
|
0x43,
|
||||||
0x05
|
0x05
|
||||||
@@ -308,23 +357,26 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
|
||||||
0x7, 0x0, 0x0, 0x2, 0x1,
|
0x7, 0x0, 0x0, 0x2, 0x1,
|
||||||
0x00,
|
0x00,
|
||||||
0xff,0xd7,0x00 // RGB LED: R, G, B
|
0xff, 0xd7, 0x00 // RGB LED: R, G, B
|
||||||
};
|
};
|
||||||
memcpy(report32 + 2,packet_0x10,sizeof(packet_0x10));
|
memcpy(report32 + 2, packet_0x10, sizeof(packet_0x10));
|
||||||
bt_write(report32,sizeof(report32));
|
bt_write(report32, sizeof(report32));
|
||||||
init_feature();
|
init_feature();
|
||||||
|
} else {
|
||||||
|
printf("[L2CAP] Unknown Channel psm: 0x%02X", psm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hid_control_cid != 0 && hid_interrupt_cid != 0) {
|
/*if (hid_control_cid != 0 && hid_interrupt_cid != 0) {
|
||||||
printf("[L2CAP] HID channels ready, request CAN_SEND_NOW for SET_PROTOCOL\n");
|
printf("[L2CAP] HID channels ready, request CAN_SEND_NOW for SET_PROTOCOL\n");
|
||||||
l2cap_request_can_send_now_event(hid_control_cid);
|
l2cap_request_can_send_now_event(hid_control_cid);
|
||||||
}
|
}*/
|
||||||
} else {
|
} else {
|
||||||
const uint16_t psm = l2cap_event_channel_opened_get_psm(packet);
|
const uint16_t psm = l2cap_event_channel_opened_get_psm(packet);
|
||||||
hid_control_cid = 0;
|
hid_control_cid = 0;
|
||||||
hid_interrupt_cid = 0;
|
hid_interrupt_cid = 0;
|
||||||
device_found = false;
|
device_found = false;
|
||||||
printf("[L2CAP] Open failed psm=0x%04X status=0x%02X\n", psm, status);
|
printf("[L2CAP] Open failed psm=0x%04X status=0x%02X\n", psm, status);
|
||||||
|
hci_send_cmd(&hci_disconnect);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -352,23 +404,48 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
case L2CAP_EVENT_CAN_SEND_NOW: {
|
case L2CAP_EVENT_CAN_SEND_NOW: {
|
||||||
printf("L2CAP_EVENT_CAN_SEND_NOW\n");
|
// printf("[L2CAP] L2CAP_EVENT_CAN_SEND_NOW\n");
|
||||||
|
|
||||||
|
critical_section_enter_blocking(&queue_lock);
|
||||||
|
if (send_queue.empty()) {
|
||||||
|
critical_section_exit(&queue_lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> data = send_queue.front();
|
||||||
|
send_queue.pop();
|
||||||
|
critical_section_exit(&queue_lock);
|
||||||
|
|
||||||
|
uint8_t status = l2cap_send(hid_interrupt_cid, data.data(), data.size());
|
||||||
|
if (status != 0) {
|
||||||
|
printf("[L2CAP] Interrupt Error, Status: 0x%02X\n", status);
|
||||||
|
}
|
||||||
|
l2cap_request_can_send_now_event(hid_interrupt_cid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_write(uint8_t* data,uint16_t len) {
|
void bt_write(uint8_t *data, uint16_t len) {
|
||||||
if (!device_found) return;
|
if (hid_interrupt_cid == 0) return;
|
||||||
uint8_t buffer[len + 1];
|
std::vector<uint8_t> packet(len + 1);
|
||||||
buffer[0] = 0xA2;
|
packet[0] = 0xA2;
|
||||||
memcpy(buffer + 1,data,len);
|
memcpy(packet.data() + 1, data, len);
|
||||||
fill_output_report_checksum(buffer + 1,len);
|
fill_output_report_checksum(packet.data() + 1, len);
|
||||||
l2cap_send(hid_interrupt_cid,buffer,sizeof(buffer));
|
|
||||||
|
critical_section_enter_blocking(&queue_lock);
|
||||||
|
send_queue.push(std::move(packet)); // 使用 std::move 避免深拷贝
|
||||||
|
critical_section_exit(&queue_lock);
|
||||||
|
|
||||||
|
if (hid_interrupt_cid == 0) {
|
||||||
|
printf("[L2CAP bt_write] Warning: hid_interrupt_cid 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (send_queue.size() == 1) {
|
||||||
|
l2cap_request_can_send_now_event(hid_interrupt_cid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* get_feature_data(uint8_t reportId,uint16_t len) {
|
uint8_t *get_feature_data(uint8_t reportId, uint16_t len) {
|
||||||
if (feature_data.find(reportId) == feature_data.end() || feature_data[reportId].empty()) {
|
if (feature_data.find(reportId) == feature_data.end() || feature_data[reportId].empty()) {
|
||||||
if (hid_control_cid != 0) {
|
if (hid_control_cid != 0) {
|
||||||
uint8_t get_feature[] = {0x43, reportId};
|
uint8_t get_feature[] = {0x43, reportId};
|
||||||
@@ -381,7 +458,7 @@ uint8_t* get_feature_data(uint8_t reportId,uint16_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_feature() {
|
void init_feature() {
|
||||||
get_feature_data(0x09,20);
|
get_feature_data(0x09, 20);
|
||||||
get_feature_data(0x20,64);
|
get_feature_data(0x20, 64);
|
||||||
get_feature_data(0x05,41);
|
get_feature_data(0x05, 41);
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
#ifndef BTSTACK_CONFIG_H
|
#ifndef BTSTACK_CONFIG_H
|
||||||
#define BTSTACK_CONFIG_H
|
#define BTSTACK_CONFIG_H
|
||||||
|
|
||||||
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
|
|
||||||
#ifndef ENABLE_CLASSIC
|
#ifndef ENABLE_CLASSIC
|
||||||
#define ENABLE_CLASSIC
|
#define ENABLE_CLASSIC
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+24
-13
@@ -13,20 +13,32 @@
|
|||||||
|
|
||||||
int reportSeqCounter = 0;
|
int reportSeqCounter = 0;
|
||||||
uint8_t packetCounter = 0;
|
uint8_t packetCounter = 0;
|
||||||
|
uint32_t lastTime = 0;
|
||||||
|
|
||||||
uint8_t interrupt_in_data[63];
|
uint8_t interrupt_in_data[63] = {
|
||||||
|
0x7f, 0x7d, 0x7f, 0x7e, 0x00, 0x00, 0xa7,
|
||||||
|
0x08, 0x00, 0x00, 0x00, 0x52, 0x43, 0x30, 0x41,
|
||||||
|
0x01, 0x00, 0x0e, 0x00, 0xef, 0xff, 0x03, 0x03,
|
||||||
|
0x7b, 0x1b, 0x18, 0xf0, 0xcc, 0x9c, 0x60, 0x00,
|
||||||
|
0xfc, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xa7, 0xad, 0x60, 0x00, 0x29, 0x18, 0x00,
|
||||||
|
0x53, 0x9f, 0x28, 0x35, 0xa5, 0xa8, 0x0c, 0x8b
|
||||||
|
};
|
||||||
|
|
||||||
bool interrupt_in_cb(repeating_timer* rt) {
|
void interrupt_loop() {
|
||||||
if (tud_ready()) {
|
if (board_millis() - lastTime < 4) return;
|
||||||
tud_hid_report(0x01,interrupt_in_data,63);
|
lastTime = board_millis();
|
||||||
|
if (!tud_hid_ready()) return;
|
||||||
|
if (!tud_hid_report(0x01, interrupt_in_data, 63)) {
|
||||||
|
printf("[USBHID] tud_hid_report error\n");
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_bt_data(CHANNEL_TYPE channel, uint8_t *data, uint16_t len) {
|
void on_bt_data(CHANNEL_TYPE channel, uint8_t *data, uint16_t len) {
|
||||||
printf("[Main] BT data callback: channel=%u len=%u\n", channel, len);
|
// printf("[Main] BT data callback: channel=%u len=%u\n", channel, len);
|
||||||
if (channel == INTERRUPT && data[1] == 0x31) {
|
if (channel == INTERRUPT && data[1] == 0x31) {
|
||||||
memcpy(interrupt_in_data,data + 3,63);
|
memcpy(interrupt_in_data, data + 3, 63);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +65,6 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
|
|||||||
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
||||||
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
|
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
|
||||||
uint16_t bufsize) {
|
uint16_t bufsize) {
|
||||||
// This example doesn't use multiple report and report ID
|
|
||||||
(void) itf;
|
(void) itf;
|
||||||
(void) report_id;
|
(void) report_id;
|
||||||
(void) report_type;
|
(void) report_type;
|
||||||
@@ -69,8 +80,8 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep
|
|||||||
reportSeqCounter = 0;
|
reportSeqCounter = 0;
|
||||||
}
|
}
|
||||||
outputData[2] = 0x10;
|
outputData[2] = 0x10;
|
||||||
memcpy(outputData + 3,buffer + 1,bufsize - 1);
|
memcpy(outputData + 3, buffer + 1, bufsize - 1);
|
||||||
bt_write(outputData,sizeof(outputData));
|
bt_write(outputData, sizeof(outputData));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,11 +101,11 @@ int main() {
|
|||||||
bt_init();
|
bt_init();
|
||||||
bt_register_data_callback(on_bt_data);
|
bt_register_data_callback(on_bt_data);
|
||||||
|
|
||||||
multicore_launch_core1(core1_entry);
|
audio_init();
|
||||||
repeating_timer rt{};
|
|
||||||
add_repeating_timer_ms(4,interrupt_in_cb,nullptr,&rt);
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
tud_task();
|
tud_task();
|
||||||
|
audio_loop();
|
||||||
|
interrupt_loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -118,16 +118,26 @@
|
|||||||
|
|
||||||
// UAC1 Full-Speed endpoint size
|
// UAC1 Full-Speed endpoint size
|
||||||
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000
|
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000
|
||||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX 392
|
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(false, CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
|
||||||
|
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT
|
||||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX 196
|
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX 196
|
||||||
|
|
||||||
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ 2048
|
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (6 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX)
|
||||||
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (4 * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX)
|
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (4 * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX)
|
||||||
|
|
||||||
// Enable OUT EP (speaker) and IN EP (mic)
|
// Enable OUT EP (speaker) and IN EP (mic)
|
||||||
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
|
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
|
||||||
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
|
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
|
||||||
|
|
||||||
|
// CDC FIFO size of TX and RX
|
||||||
|
#define CFG_TUD_CDC_RX_BUFSIZE 64
|
||||||
|
#define CFG_TUD_CDC_TX_BUFSIZE 64
|
||||||
|
|
||||||
|
// CDC Endpoint transfer buffer size, more is faster
|
||||||
|
// Leave it as default size (512 for HS, 64 for FS) unless your host application
|
||||||
|
// is able to send ZLP (Zero Length Packet) to terminate transfer !
|
||||||
|
#define CFG_TUD_CDC_EP_BUFSIZE 64
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -215,7 +215,8 @@ uint8_t const descriptor_configuration[] = {
|
|||||||
0x01, // bEndpointAddress: OUT EP1
|
0x01, // bEndpointAddress: OUT EP1
|
||||||
0x09, // bmAttributes: Isochronous, Adaptive
|
0x09, // bmAttributes: Isochronous, Adaptive
|
||||||
0x88, 0x01, // wMaxPacketSize: 392 bytes
|
0x88, 0x01, // wMaxPacketSize: 392 bytes
|
||||||
0x04, // bInterval: 4 (1/(2^(4-1)) ms ≈ 125 µs/frame)
|
// 0x04, // bInterval: 4 (1/(2^(4-1)) ms ≈ 125 µs/frame)
|
||||||
|
0x01, // bInterval
|
||||||
0x00, // bRefresh
|
0x00, // bRefresh
|
||||||
0x00, // bSynchAddress
|
0x00, // bSynchAddress
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user