Merge branch 'master' into refactor/state_mgr

This commit is contained in:
awalol
2026-05-16 14:06:01 +08:00
committed by GitHub
10 changed files with 125 additions and 31 deletions
+11 -4
View File
@@ -30,6 +30,9 @@ jobs:
with: with:
submodules: recursive submodules: recursive
- name: Set firmware version
run: printf 'FIRMWARE_VERSION=%s\n' "${GITHUB_SHA::7}" >> "$GITHUB_ENV"
- name: Prepare build dependency cache - name: Prepare build dependency cache
id: apt-cache id: apt-cache
run: | run: |
@@ -79,7 +82,8 @@ jobs:
run: | run: |
cmake -S . -B build/standard -G Ninja \ cmake -S . -B build/standard -G Ninja \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DPICO_SDK_PATH="$PICO_SDK_PATH" -DPICO_SDK_PATH="$PICO_SDK_PATH" \
-DVERSION="$FIRMWARE_VERSION"
cmake --build build/standard --target ds5-bridge cmake --build build/standard --target ds5-bridge
mkdir -p artifacts mkdir -p artifacts
cp build/standard/ds5-bridge.uf2 artifacts/ds5-bridge.uf2 cp build/standard/ds5-bridge.uf2 artifacts/ds5-bridge.uf2
@@ -90,7 +94,8 @@ jobs:
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DPICO_SDK_PATH="$PICO_SDK_PATH" \ -DPICO_SDK_PATH="$PICO_SDK_PATH" \
-DENABLE_SERIAL=ON \ -DENABLE_SERIAL=ON \
-DENABLE_VERBOSE=ON -DENABLE_VERBOSE=ON \
-DVERSION="$FIRMWARE_VERSION"
cmake --build build/debug --target ds5-bridge cmake --build build/debug --target ds5-bridge
cp build/debug/ds5-bridge.uf2 artifacts/ds5-bridge-debug.uf2 cp build/debug/ds5-bridge.uf2 artifacts/ds5-bridge-debug.uf2
@@ -99,7 +104,8 @@ jobs:
cmake -S . -B build/no-batt-led -G Ninja \ cmake -S . -B build/no-batt-led -G Ninja \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DPICO_SDK_PATH="$PICO_SDK_PATH" \ -DPICO_SDK_PATH="$PICO_SDK_PATH" \
-DENABLE_BATT_LED=OFF -DENABLE_BATT_LED=OFF \
-DVERSION="$FIRMWARE_VERSION"
cmake --build build/no-batt-led --target ds5-bridge cmake --build build/no-batt-led --target ds5-bridge
- name: Build Pico W firmware - name: Build Pico W firmware
@@ -107,7 +113,8 @@ jobs:
cmake -S . -B build/picow -G Ninja \ cmake -S . -B build/picow -G Ninja \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DPICO_SDK_PATH="$PICO_SDK_PATH" \ -DPICO_SDK_PATH="$PICO_SDK_PATH" \
-DPICO_W_BUILD=ON -DPICO_W_BUILD=ON \
-DVERSION="$FIRMWARE_VERSION"
cmake --build build/picow --target ds5-bridge cmake --build build/picow --target ds5-bridge
cp build/picow/ds5-bridge.uf2 artifacts/ds5-bridge-picow.uf2 cp build/picow/ds5-bridge.uf2 artifacts/ds5-bridge-picow.uf2
+9 -2
View File
@@ -33,6 +33,11 @@ jobs:
ref: ${{ github.event.release.tag_name }} ref: ${{ github.event.release.tag_name }}
submodules: recursive submodules: recursive
- name: Set firmware version
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: printf 'FIRMWARE_VERSION=%s\n' "$RELEASE_TAG" >> "$GITHUB_ENV"
- name: Prepare build dependency cache - name: Prepare build dependency cache
id: apt-cache id: apt-cache
run: | run: |
@@ -82,7 +87,8 @@ jobs:
run: | run: |
cmake -S . -B build/standard -G Ninja \ cmake -S . -B build/standard -G Ninja \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DPICO_SDK_PATH="$PICO_SDK_PATH" -DPICO_SDK_PATH="$PICO_SDK_PATH" \
-DVERSION="$FIRMWARE_VERSION"
cmake --build build/standard --target ds5-bridge cmake --build build/standard --target ds5-bridge
mkdir -p artifacts mkdir -p artifacts
cp build/standard/ds5-bridge.uf2 "artifacts/ds5-bridge-${{ github.event.release.tag_name }}.uf2" cp build/standard/ds5-bridge.uf2 "artifacts/ds5-bridge-${{ github.event.release.tag_name }}.uf2"
@@ -93,7 +99,8 @@ jobs:
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DPICO_SDK_PATH="$PICO_SDK_PATH" \ -DPICO_SDK_PATH="$PICO_SDK_PATH" \
-DENABLE_SERIAL=ON \ -DENABLE_SERIAL=ON \
-DENABLE_VERBOSE=ON -DENABLE_VERBOSE=ON \
-DVERSION="$FIRMWARE_VERSION"
cmake --build build/debug --target ds5-bridge cmake --build build/debug --target ds5-bridge
cp build/debug/ds5-bridge.uf2 "artifacts/ds5-bridge-debug-${{ github.event.release.tag_name }}.uf2" cp build/debug/ds5-bridge.uf2 "artifacts/ds5-bridge-debug-${{ github.event.release.tag_name }}.uf2"
+2 -1
View File
@@ -142,9 +142,10 @@ if(WAKE_DEBUG)
WAKE_DEBUG WAKE_DEBUG
) )
endif() endif()
set(VERSION "dev" CACHE STRING "Program version string")
pico_set_program_name(ds5-bridge "ds5-bridge") pico_set_program_name(ds5-bridge "ds5-bridge")
pico_set_program_version(ds5-bridge "0.5.3") pico_set_program_version(ds5-bridge "${VERSION}")
# 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 1) pico_enable_stdio_uart(ds5-bridge 1)
+32 -1
View File
@@ -9,6 +9,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "btstack_event.h" #include "btstack_event.h"
#include "gap.h"
#include "l2cap.h" #include "l2cap.h"
#include "pico/cyw43_arch.h" #include "pico/cyw43_arch.h"
#include "utils.h" #include "utils.h"
@@ -38,6 +39,7 @@ 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 = nullptr; static bt_data_callback_t bt_data_callback = nullptr;
static bool check_dse = false; static bool check_dse = false;
static int8_t bt_rssi = 0;
unordered_map<uint8_t, vector<uint8_t> > feature_data; unordered_map<uint8_t, vector<uint8_t> > feature_data;
queue_t send_fifo; queue_t send_fifo;
@@ -74,6 +76,18 @@ bool bt_disconnect() {
return true; return true;
} }
void bt_get_signal_strength(int8_t *rssi) {
// gap_read_rssi() completes asynchronously, so this function can only
// return the last cached RSSI value. Trigger a refresh afterwards so a
// subsequent call can observe the updated value once the RSSI event arrives.
if (rssi != nullptr) {
*rssi = bt_rssi;
}
if (acl_handle != HCI_CON_HANDLE_INVALID) {
gap_read_rssi(acl_handle);
}
}
void bt_l2cap_init() { void bt_l2cap_init() {
l2cap_event_callback_registration.callback = &l2cap_packet_handler; l2cap_event_callback_registration.callback = &l2cap_packet_handler;
l2cap_add_event_handler(&l2cap_event_callback_registration); l2cap_add_event_handler(&l2cap_event_callback_registration);
@@ -197,7 +211,14 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
case HCI_EVENT_COMMAND_COMPLETE: { case HCI_EVENT_COMMAND_COMPLETE: {
const uint8_t status = hci_event_command_complete_get_return_parameters(packet)[0]; const uint8_t status = hci_event_command_complete_get_return_parameters(packet)[0];
const uint16_t opcode = hci_event_command_complete_get_command_opcode(packet); const uint16_t opcode = hci_event_command_complete_get_command_opcode(packet);
printf("[HCI] CmdComplete %s(0x%04X) status=0x%02X\n", opcode_to_str(opcode), opcode, status); if (opcode != HCI_OPCODE_HCI_READ_RSSI) {
printf("[HCI] CmdComplete %s(0x%04X) status=0x%02X\n", opcode_to_str(opcode), opcode, status);
}
if (opcode == HCI_OPCODE_HCI_READ_RSSI) {
if (status != ERROR_CODE_SUCCESS || packet[1] < 7) {
printf("[HCI] RSSI complete failed status=0x%02X param_len=%u\n", status, packet[1]);
}
}
break; break;
} }
@@ -206,6 +227,7 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
if (status == 0) { if (status == 0) {
const hci_con_handle_t handle = hci_event_connection_complete_get_connection_handle(packet); const hci_con_handle_t handle = hci_event_connection_complete_get_connection_handle(packet);
acl_handle = handle; acl_handle = handle;
bt_rssi = 0;
hci_event_connection_complete_get_bd_addr(packet, current_device_addr); hci_event_connection_complete_get_bd_addr(packet, current_device_addr);
printf("[HCI] ACL connected handle=0x%04X\n", handle); printf("[HCI] ACL connected handle=0x%04X\n", handle);
printf("[HCI] Request authentication on handle=0x%04X\n", handle); printf("[HCI] Request authentication on handle=0x%04X\n", handle);
@@ -315,6 +337,7 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
device_found = false; device_found = false;
new_pair = false; new_pair = false;
acl_handle = HCI_CON_HANDLE_INVALID; acl_handle = HCI_CON_HANDLE_INVALID;
bt_rssi = 0;
hid_control_cid = 0; hid_control_cid = 0;
hid_interrupt_cid = 0; hid_interrupt_cid = 0;
feature_data.clear(); feature_data.clear();
@@ -323,6 +346,14 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
gap_inquiry_start(30); gap_inquiry_start(30);
break; break;
} }
case GAP_EVENT_RSSI_MEASUREMENT: {
const hci_con_handle_t handle = gap_event_rssi_measurement_get_con_handle(packet);
if (handle == acl_handle) {
bt_rssi = static_cast<int8_t>(gap_event_rssi_measurement_get_rssi(packet));
}
break;
}
} }
} }
+1
View File
@@ -20,6 +20,7 @@ void bt_register_data_callback(bt_data_callback_t callback);
void bt_send_packet(uint8_t *data, uint16_t len); void bt_send_packet(uint8_t *data, uint16_t len);
void bt_send_control(uint8_t *data, uint16_t len); void bt_send_control(uint8_t *data, uint16_t len);
void bt_write(const uint8_t *data, uint16_t len); void bt_write(const uint8_t *data, uint16_t len);
void bt_get_signal_strength(int8_t *rssi);
std::vector<uint8_t> get_feature_data(uint8_t reportId,uint16_t len); std::vector<uint8_t> get_feature_data(uint8_t reportId,uint16_t len);
void init_feature(); void init_feature();
void set_feature_data(uint8_t reportId, uint8_t* data,uint16_t len); void set_feature_data(uint8_t reportId, uint8_t* data,uint16_t len);
+23 -1
View File
@@ -8,13 +8,16 @@
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include "bt.h"
#include "config.h" #include "config.h"
#include "device/usbd.h" #include "device/usbd.h"
#include "pico/time.h" #include "pico/time.h"
bool is_pico_cmd(uint8_t report_id) { bool is_pico_cmd(uint8_t report_id) {
if (report_id == 0xf6 || if (report_id == 0xf6 ||
report_id == 0xf7 report_id == 0xf7 ||
report_id == 0xf8 ||
report_id == 0xf9
) { ) {
return true; return true;
} }
@@ -31,6 +34,25 @@ uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer, uint16_t reqlen) {
memcpy(buffer,&get_config(),len); memcpy(buffer,&get_config(),len);
return len; return len;
} }
if (report_id == 0xf8) {
printf("[HID] Receive 0xf8 getting firmware version\n");
const auto len = std::min(strlen(PICO_PROGRAM_VERSION_STRING), static_cast<size_t>(reqlen));
memcpy(buffer, PICO_PROGRAM_VERSION_STRING, len);
return len;
}
if (report_id == 0xf9) {
// [-128,0]
int8_t rssi = 0;
bt_get_signal_strength(&rssi);
if (reqlen == 0) {
return 0;
}
buffer[0] = rssi;
#if ENABLE_VERBOSE
printf("[HID] 0xf9 RSSI=%d raw=0x%02X\n", rssi, buffer[0]);
#endif
return 1;
}
return 0; return 0;
} }
+4
View File
@@ -79,6 +79,10 @@ void config_valid() {
body->controller_mode = 2; body->controller_mode = 2;
printf("[Config] controller_mode is invalid\n"); printf("[Config] controller_mode is invalid\n");
} }
if (body->config_version != CONFIG_VERSION) {
body->config_version = CONFIG_VERSION;
printf("[Config] Warning: Config may breaking change\n");
}
} }
void config_load() { void config_load() {
+1
View File
@@ -8,6 +8,7 @@
#include <cstdint> #include <cstdint>
struct __attribute__((packed)) Config_body { struct __attribute__((packed)) Config_body {
uint8_t config_version; // Config Version
float haptics_gain; // [1.0,2.0] float haptics_gain; // [1.0,2.0]
float speaker_volume; // [-100,0] float speaker_volume; // [-100,0]
uint8_t inactive_time; // [10,60] min uint8_t inactive_time; // [10,60] min
+40 -22
View File
@@ -362,8 +362,8 @@ uint8_t descriptor_configuration[] = {
0x00, // bCountryCode: Not localized 0x00, // bCountryCode: Not localized
0x01, // bNumDescriptors: 1 report descriptor 0x01, // bNumDescriptors: 1 report descriptor
0x22, // bDescriptorType: Report 0x22, // bDescriptorType: Report
0x31, 0x01, // wDescriptorLength: 305 (0x0131) DS 0x41, 0x01, // wDescriptorLength: 321 (0x0141) DS
// 0xA5, 0x01, // wDescriptorLength: 421 (0x01A5) DSE // 0xB5, 0x01, // wDescriptorLength: 437 (0x01B5) DSE
// Endpoint Descriptor (HID IN: EP4) // Endpoint Descriptor (HID IN: EP4)
0x07, // bLength 0x07, // bLength
@@ -408,9 +408,9 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
descriptor_configuration[offset - 1] = bInterval; descriptor_configuration[offset - 1] = bInterval;
descriptor_configuration[offset - 8] = bInterval; descriptor_configuration[offset - 8] = bInterval;
if (ds_mode()) { if (ds_mode()) {
descriptor_configuration[offset - 16] = 0x31; descriptor_configuration[offset - 16] = 0x41;
}else { }else {
descriptor_configuration[offset - 16] = 0xA5; descriptor_configuration[offset - 16] = 0xB5;
} }
return descriptor_configuration; return descriptor_configuration;
} }
@@ -561,17 +561,26 @@ uint8_t const desc_hid_report_ds[] = {
0x09, 0x36, // Usage (0x36) 0x09, 0x36, // Usage (0x36)
0x95, 0x03, // Report Count (3) 0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF6, 0x85, 0xF6, // Report ID (-10)
0x09, 0x37, 0x09, 0x37, // Usage (Vendor 0x37)
0x95, 0x3F, 0x95, 0x3F, // Report Count (63)
0xB1, 0x02, 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF7, 0x85, 0xF7, // Report ID (-9)
0x09, 0x38, 0x09, 0x38, // Usage (Vendor 0x38)
0x95, 0x3F, 0x95, 0x3F, // Report Count (63)
0xB1, 0x02, 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF8, // Report ID (-8)
0x09, 0x39, // Usage (Vendor 0x39)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF9, // Report ID (-7)
0x09, 0x3A, // Usage (Vendor 0x3A)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection 0xC0, // End Collection
// 305 bytes // 321 bytes
}; };
static_assert(sizeof(desc_hid_report_ds) == 0x0141);
uint8_t const desc_hid_report_dse[] = { uint8_t const desc_hid_report_dse[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
@@ -773,17 +782,26 @@ uint8_t const desc_hid_report_dse[] = {
0x85, 0x7B, // Report ID (123) 0x85, 0x7B, // Report ID (123)
0x09, 0x53, // Usage (0x53) 0x09, 0x53, // Usage (0x53)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF6, 0x85, 0xF6, // Report ID (-10)
0x09, 0x37, 0x09, 0x37, // Usage (Vendor 0x37)
0x95, 0x3F, 0x95, 0x3F, // Report Count (63)
0xB1, 0x02, 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF7, 0x85, 0xF7, // Report ID (-9)
0x09, 0x38, 0x09, 0x38, // Usage (Vendor 0x38)
0x95, 0x3F, 0x95, 0x3F, // Report Count (63)
0xB1, 0x02, 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF8, // Report ID (-8)
0x09, 0x39, // Usage (Vendor 0x39)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF9, // Report ID (-7)
0x09, 0x3A, // Usage (Vendor 0x3A)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection 0xC0, // End Collection
// 421 bytes // 437 bytes
}; };
static_assert(sizeof(desc_hid_report_dse) == 0x01B5);
// Invoked when received GET HID REPORT DESCRIPTOR // Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor // Application return pointer to descriptor
+2
View File
@@ -83,6 +83,8 @@ inline const char *opcode_to_str(const uint16_t opcode) {
return "HCI_READ_BD_ADDR"; return "HCI_READ_BD_ADDR";
case HCI_OPCODE_HCI_READ_ENCRYPTION_KEY_SIZE: case HCI_OPCODE_HCI_READ_ENCRYPTION_KEY_SIZE:
return "HCI_READ_ENCRYPTION_KEY_SIZE"; return "HCI_READ_ENCRYPTION_KEY_SIZE";
case HCI_OPCODE_HCI_READ_RSSI:
return "HCI_READ_RSSI";
case 0xFC01: case 0xFC01:
return "HCI_VENDOR_0xFC01"; return "HCI_VENDOR_0xFC01";
default: default: