feat: config manager
This commit is contained in:
+4
-1
@@ -44,6 +44,8 @@ add_executable(ds5-bridge
|
|||||||
src/usb.cpp
|
src/usb.cpp
|
||||||
src/audio.cpp
|
src/audio.cpp
|
||||||
src/bt.cpp
|
src/bt.cpp
|
||||||
|
src/config.cpp
|
||||||
|
src/cmd.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cockos WDLResampler
|
# Cockos WDLResampler
|
||||||
@@ -67,7 +69,7 @@ target_compile_definitions(ds5-bridge PRIVATE
|
|||||||
CYW43_LWIP=0
|
CYW43_LWIP=0
|
||||||
PICO_FLASH_ASSUME_CORE1_SAFE=1
|
PICO_FLASH_ASSUME_CORE1_SAFE=1
|
||||||
WDL_RESAMPLE_TYPE=float
|
WDL_RESAMPLE_TYPE=float
|
||||||
CYW43_PIO_CLOCK_DIV_INT=4
|
CYW43_PIO_CLOCK_DIV_INT=5
|
||||||
)
|
)
|
||||||
option(ENABLE_DSE "Enable DSE" OFF)
|
option(ENABLE_DSE "Enable DSE" OFF)
|
||||||
if(ENABLE_DSE)
|
if(ENABLE_DSE)
|
||||||
@@ -98,6 +100,7 @@ target_link_libraries(ds5-bridge
|
|||||||
pico_multicore
|
pico_multicore
|
||||||
hardware_watchdog
|
hardware_watchdog
|
||||||
hardware_timer
|
hardware_timer
|
||||||
|
hardware_flash
|
||||||
pico_btstack_classic
|
pico_btstack_classic
|
||||||
# pico_cyw43_arch_threadsafe_background
|
# pico_cyw43_arch_threadsafe_background
|
||||||
pico_cyw43_arch_poll
|
pico_cyw43_arch_poll
|
||||||
|
|||||||
+5
-6
@@ -6,14 +6,13 @@
|
|||||||
#include "bt.h"
|
#include "bt.h"
|
||||||
#include "resample.h"
|
#include "resample.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "usb.h"
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include "opus.h"
|
#include "opus.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "pico/multicore.h"
|
#include "pico/multicore.h"
|
||||||
#include "pico/util/queue.h"
|
#include "pico/util/queue.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#define INPUT_CHANNELS 4
|
#define INPUT_CHANNELS 4
|
||||||
#define OUTPUT_CHANNELS 2
|
#define OUTPUT_CHANNELS 2
|
||||||
@@ -72,8 +71,8 @@ void audio_loop() {
|
|||||||
int nframes = resampler.ResamplePrepare(frames, OUTPUT_CHANNELS, &in_buf);
|
int nframes = resampler.ResamplePrepare(frames, OUTPUT_CHANNELS, &in_buf);
|
||||||
|
|
||||||
for (int i = 0; i < nframes; i++) {
|
for (int i = 0; i < nframes; i++) {
|
||||||
audio_buf[audio_buf_pos++] = raw[i * INPUT_CHANNELS] / 32768.0f * (volume[0] - 1.0f);
|
audio_buf[audio_buf_pos++] = raw[i * INPUT_CHANNELS] / 32768.0f * (get_config().speaker_volume - 1.0f);
|
||||||
audio_buf[audio_buf_pos++] = raw[i * INPUT_CHANNELS + 1] / 32768.0f * (volume[0] - 1.0f);
|
audio_buf[audio_buf_pos++] = raw[i * INPUT_CHANNELS + 1] / 32768.0f * (get_config().speaker_volume - 1.0f);
|
||||||
if (audio_buf_pos == 512 * 2) {
|
if (audio_buf_pos == 512 * 2) {
|
||||||
static audio_raw_element element{};
|
static audio_raw_element element{};
|
||||||
memcpy(element.data,audio_buf,512 * 2 * 4);
|
memcpy(element.data,audio_buf,512 * 2 * 4);
|
||||||
@@ -99,8 +98,8 @@ void audio_loop() {
|
|||||||
|
|
||||||
// 4. 转换为int8并缓冲,满64字节即组包发送
|
// 4. 转换为int8并缓冲,满64字节即组包发送
|
||||||
for (int i = 0; i < out_frames; i++) {
|
for (int i = 0; i < out_frames; i++) {
|
||||||
int val_l = (int) (out_buf[i * 2] * 127.0f * max(volume[1],1.0f));
|
int val_l = (int) (out_buf[i * 2] * 127.0f * max(get_config().haptics_gain,1.0f));
|
||||||
int val_r = (int) (out_buf[i * 2 + 1] * 127.0f * max(volume[1],1.0f));
|
int val_r = (int) (out_buf[i * 2 + 1] * 127.0f * max(get_config().haptics_gain,1.0f));
|
||||||
haptic_buf[haptic_buf_pos++] = (int8_t) clamp(val_l, -128, 127); // 似乎clamp有点多余?还是以防万一吧
|
haptic_buf[haptic_buf_pos++] = (int8_t) clamp(val_l, -128, 127); // 似乎clamp有点多余?还是以防万一吧
|
||||||
haptic_buf[haptic_buf_pos++] = (int8_t) clamp(val_r, -128, 127);
|
haptic_buf[haptic_buf_pos++] = (int8_t) clamp(val_r, -128, 127);
|
||||||
|
|
||||||
|
|||||||
+3
-5
@@ -4,13 +4,10 @@
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "bt.h"
|
#include "bt.h"
|
||||||
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "btstack_event.h"
|
#include "btstack_event.h"
|
||||||
#include "l2cap.h"
|
#include "l2cap.h"
|
||||||
#include "pico/cyw43_arch.h"
|
#include "pico/cyw43_arch.h"
|
||||||
@@ -20,6 +17,7 @@
|
|||||||
#include "bsp/board_api.h"
|
#include "bsp/board_api.h"
|
||||||
#include "pico/sync.h"
|
#include "pico/sync.h"
|
||||||
#include "classic/sdp_server.h"
|
#include "classic/sdp_server.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#define MTU 672
|
#define MTU 672
|
||||||
|
|
||||||
@@ -321,7 +319,7 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
bt_data_callback(INTERRUPT, packet, size);
|
bt_data_callback(INTERRUPT, packet, size);
|
||||||
|
|
||||||
// 静默检测
|
// 静默检测
|
||||||
if (mute[1]) { // 麦克风静音开启
|
if (get_config().disable_inactive_disconnect) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (packet[3] < 120 || packet[3] > 140) {
|
if (packet[3] < 120 || packet[3] > 140) {
|
||||||
@@ -361,7 +359,7 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", local_cid);
|
printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", local_cid);
|
||||||
hid_interrupt_cid = local_cid;
|
hid_interrupt_cid = local_cid;
|
||||||
|
|
||||||
if (!mute[0]) {
|
if (!get_config().disable_pico_led) {
|
||||||
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true);
|
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true);
|
||||||
}
|
}
|
||||||
inactive_time = time_us_32();
|
inactive_time = time_us_32();
|
||||||
|
|||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// Created by awalol on 2026/5/4.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "cmd.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "pico/time.h"
|
||||||
|
|
||||||
|
bool is_pico_cmd(uint8_t report_id) {
|
||||||
|
if (report_id == 0xf6 ||
|
||||||
|
report_id == 0xf7
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer, uint16_t reqlen) {
|
||||||
|
if (report_id == 0xf7) {
|
||||||
|
printf("[HID] Receive 0xf7 getting config\n");
|
||||||
|
if (sizeof(Config_body) > reqlen) {
|
||||||
|
printf("[Config] Warning: Config_body overflow\n");
|
||||||
|
}
|
||||||
|
const auto len = std::min(sizeof(Config_body),static_cast<size_t>(reqlen));
|
||||||
|
memcpy(buffer,&get_config(),len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pico_cmd_set(uint8_t report_id, uint8_t const *buffer, uint16_t bufsize) {
|
||||||
|
(void) report_id;
|
||||||
|
if (bufsize == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0x01 update config in variable
|
||||||
|
// 0x02 write config to flash
|
||||||
|
// 0x03 reconnect tinyusb device;
|
||||||
|
if (buffer[0] == 0x01) {
|
||||||
|
printf("[CMD] Enter config set func\n");
|
||||||
|
set_config(buffer + 1, bufsize - 1);
|
||||||
|
}
|
||||||
|
if (buffer[0] == 0x02) {
|
||||||
|
printf("[CMD] Enter config save func\n");
|
||||||
|
config_save();
|
||||||
|
}
|
||||||
|
if (buffer[0] == 0x03) {
|
||||||
|
printf("[CMD] Enter tud reconnect func\n");
|
||||||
|
tud_disconnect();
|
||||||
|
sleep_ms(1000);
|
||||||
|
tud_connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// Created by awalol on 2026/5/4.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef DS5_BRIDGE_CMD_H
|
||||||
|
#define DS5_BRIDGE_CMD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
bool is_pico_cmd(uint8_t report_id);
|
||||||
|
uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer,uint16_t reqlen);
|
||||||
|
void pico_cmd_set(uint8_t report_id, uint8_t const *buffer,uint16_t bufsize);
|
||||||
|
|
||||||
|
#endif //DS5_BRIDGE_CMD_H
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
//
|
||||||
|
// Created by awalol on 2026/5/4.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "hardware/flash.h"
|
||||||
|
#include "hardware/sync.h"
|
||||||
|
|
||||||
|
constexpr uint32_t CONFIG_MAGIC = 0x66ccff00;
|
||||||
|
constexpr uint16_t CONFIG_VERSION = 1;
|
||||||
|
constexpr uint32_t CONFIG_FLASH_OFFSET = PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE;
|
||||||
|
static Config config{};
|
||||||
|
|
||||||
|
// 编译期保护
|
||||||
|
// 判断Config结构体是否能放进flash 256bytes
|
||||||
|
static_assert(sizeof(Config) <= FLASH_PAGE_SIZE);
|
||||||
|
// 配置区起始地址必须按 flash sector 对齐。
|
||||||
|
static_assert(CONFIG_FLASH_OFFSET % FLASH_SECTOR_SIZE == 0);
|
||||||
|
|
||||||
|
uint32_t calc_config_crc(const Config &con) {
|
||||||
|
return crc32(reinterpret_cast<const uint8_t *>(&con.body), sizeof(Config_body));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Config *flash_config() {
|
||||||
|
return reinterpret_cast<const Config *>(XIP_BASE + CONFIG_FLASH_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_valid() {
|
||||||
|
// valid config and set default value
|
||||||
|
if (config.magic != CONFIG_MAGIC) {
|
||||||
|
config.magic = CONFIG_MAGIC;
|
||||||
|
printf("[Config] Config Magic Header is invalid\n");
|
||||||
|
}
|
||||||
|
if (config.version != CONFIG_VERSION) {
|
||||||
|
config.version = CONFIG_VERSION;
|
||||||
|
printf("[Config] Config Version is invalid\n");
|
||||||
|
}
|
||||||
|
if (config.size != sizeof(Config_body)) {
|
||||||
|
config.size = sizeof(Config_body);
|
||||||
|
printf("[Config] Config Body size is invalid\n");
|
||||||
|
}
|
||||||
|
auto body = &config.body;
|
||||||
|
if (std::isnan(body->haptics_gain) || body->haptics_gain < 1.0f || body->haptics_gain > 2.0f) {
|
||||||
|
body->haptics_gain = 1.0f;
|
||||||
|
printf("[Config] Haptics Gain value is invalid\n");
|
||||||
|
}
|
||||||
|
if (std::isnan(body->speaker_volume) || body->speaker_volume < 1.0f || body->speaker_volume > 2.0f) {
|
||||||
|
body->speaker_volume = 2.0f;
|
||||||
|
printf("[Config] Speaker Volume is invalid\n");
|
||||||
|
}
|
||||||
|
if (body->disable_inactive_disconnect > 1) {
|
||||||
|
body->disable_inactive_disconnect = 0;
|
||||||
|
printf("[Config] disable_auto_disconnect is invalid\n");
|
||||||
|
}
|
||||||
|
if (body->disable_pico_led > 1) {
|
||||||
|
body->disable_pico_led = 0;
|
||||||
|
printf("[Config] disable_pico_led is invalid\n");
|
||||||
|
}
|
||||||
|
if (body->polling_rate_mode > 2) {
|
||||||
|
body->polling_rate_mode = 0;
|
||||||
|
printf("[Config] polling_rate_mode is invalid\n");
|
||||||
|
}
|
||||||
|
if (body->haptics_buffer_length < 16 || body->haptics_buffer_length > 255) {
|
||||||
|
body->haptics_buffer_length = 48;
|
||||||
|
printf("[Config] haptics_buffer_length is invalid\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_load() {
|
||||||
|
memcpy(&config, flash_config(), sizeof(Config));
|
||||||
|
|
||||||
|
config_valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool config_save() {
|
||||||
|
config.crc32 = calc_config_crc(config);
|
||||||
|
alignas(4) uint8_t page[FLASH_PAGE_SIZE];
|
||||||
|
memset(page, 0xff, sizeof(page));
|
||||||
|
memcpy(page, &config, sizeof(Config));
|
||||||
|
|
||||||
|
const uint32_t interrupts = save_and_disable_interrupts();
|
||||||
|
flash_range_erase(CONFIG_FLASH_OFFSET, FLASH_SECTOR_SIZE);
|
||||||
|
flash_range_program(CONFIG_FLASH_OFFSET, page, sizeof(page));
|
||||||
|
restore_interrupts(interrupts);
|
||||||
|
|
||||||
|
Config verify{};
|
||||||
|
memcpy(&verify, flash_config(), sizeof(verify));
|
||||||
|
const auto verify_crc32 = calc_config_crc(verify);
|
||||||
|
if (verify_crc32 == config.crc32) {
|
||||||
|
printf("[Config] Config write flash verify success\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
printf("[Config] Config write flash verify failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Config_body& get_config() {
|
||||||
|
return config.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_config(const uint8_t *new_config, const uint16_t len) {
|
||||||
|
const auto copy_len = len < sizeof(Config_body) ? len : sizeof(Config_body);
|
||||||
|
memcpy(&config.body, new_config, copy_len);
|
||||||
|
config_valid();
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Created by awalol on 2026/5/4.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef DS5_BRIDGE_CONFIG_H
|
||||||
|
#define DS5_BRIDGE_CONFIG_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct __attribute__((packed)) Config_body {
|
||||||
|
float haptics_gain; // [1.0,2.0]
|
||||||
|
float speaker_volume; // [1.0,2.0]
|
||||||
|
uint8_t disable_inactive_disconnect; // bool: 0 disable,1 enable
|
||||||
|
uint8_t disable_pico_led; // bool
|
||||||
|
uint8_t polling_rate_mode; // 0: 250Hz,1: 500Hz,2: instant
|
||||||
|
uint8_t haptics_buffer_length; // [16,255]
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __attribute__((packed)) Config {
|
||||||
|
uint32_t magic;
|
||||||
|
uint16_t version;
|
||||||
|
uint32_t crc32; // Config_body crc32, only calc and verify when save
|
||||||
|
uint16_t size; // Config_body size
|
||||||
|
Config_body body;
|
||||||
|
};
|
||||||
|
|
||||||
|
void config_default();
|
||||||
|
void config_load();
|
||||||
|
bool config_save();
|
||||||
|
const Config_body& get_config();
|
||||||
|
void set_config(const uint8_t *new_config, const uint16_t len);
|
||||||
|
void config_valid();
|
||||||
|
|
||||||
|
#endif //DS5_BRIDGE_CONFIG_H
|
||||||
@@ -12,6 +12,8 @@
|
|||||||
#include "hardware/vreg.h"
|
#include "hardware/vreg.h"
|
||||||
#include "hardware/watchdog.h"
|
#include "hardware/watchdog.h"
|
||||||
#include "pico/cyw43_arch.h"
|
#include "pico/cyw43_arch.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "cmd.h"
|
||||||
|
|
||||||
// Pico SDK speciifically for waiting on conditions
|
// Pico SDK speciifically for waiting on conditions
|
||||||
#include "pico/critical_section.h"
|
#include "pico/critical_section.h"
|
||||||
@@ -94,6 +96,10 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
|
|||||||
(void) buffer;
|
(void) buffer;
|
||||||
(void) reqlen;
|
(void) reqlen;
|
||||||
|
|
||||||
|
if (is_pico_cmd(report_id)) {
|
||||||
|
return pico_cmd_get(report_id,buffer,reqlen);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> feature_data = get_feature_data(report_id, reqlen);
|
std::vector<uint8_t> feature_data = get_feature_data(report_id, reqlen);
|
||||||
if (!feature_data.empty()) {
|
if (!feature_data.empty()) {
|
||||||
memcpy(buffer, feature_data.data() + 1, feature_data.size() - 1);
|
memcpy(buffer, feature_data.data() + 1, feature_data.size() - 1);
|
||||||
@@ -112,6 +118,12 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep
|
|||||||
(void) buffer;
|
(void) buffer;
|
||||||
(void) bufsize;
|
(void) bufsize;
|
||||||
|
|
||||||
|
if (is_pico_cmd(report_id)) {
|
||||||
|
printf("[HID] Receive 0xf6 setting config, funcid:0x%02X\n",buffer[0]);
|
||||||
|
pico_cmd_set(report_id,buffer,bufsize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// INTERRUPT OUT
|
// INTERRUPT OUT
|
||||||
if (report_id == 0) {
|
if (report_id == 0) {
|
||||||
switch (buffer[0]) {
|
switch (buffer[0]) {
|
||||||
@@ -173,6 +185,8 @@ int main() {
|
|||||||
// Initialize the critical section for the report buffer
|
// Initialize the critical section for the report buffer
|
||||||
critical_section_init(&report_cs);
|
critical_section_init(&report_cs);
|
||||||
|
|
||||||
|
config_load();
|
||||||
|
|
||||||
bt_init();
|
bt_init();
|
||||||
bt_register_data_callback(on_bt_data);
|
bt_register_data_callback(on_bt_data);
|
||||||
|
|
||||||
|
|||||||
+19
-3
@@ -303,9 +303,9 @@ uint8_t const descriptor_configuration[] = {
|
|||||||
0x01, // bNumDescriptors: 1 report descriptor
|
0x01, // bNumDescriptors: 1 report descriptor
|
||||||
0x22, // bDescriptorType: Report
|
0x22, // bDescriptorType: Report
|
||||||
#ifdef ENABLE_DSE
|
#ifdef ENABLE_DSE
|
||||||
0x95, 0x01, // wDescriptorLength: 405 (0x0121)
|
0xA1, 0x01, // wDescriptorLength: 417 (0x01A1)
|
||||||
#else
|
#else
|
||||||
0x21, 0x01, // wDescriptorLength: 289 (0x0121)
|
0x31, 0x01, // wDescriptorLength: 289 (0x0121)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Endpoint Descriptor (HID IN: EP4)
|
// Endpoint Descriptor (HID IN: EP4)
|
||||||
@@ -480,6 +480,14 @@ 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,
|
||||||
|
0x09, 0x37,
|
||||||
|
0x95, 0x3F,
|
||||||
|
0xB1, 0x02,
|
||||||
|
0x85, 0xF7,
|
||||||
|
0x09, 0x38,
|
||||||
|
0x95, 0x3F,
|
||||||
|
0xB1, 0x02,
|
||||||
0xC0, // End Collection
|
0xC0, // End Collection
|
||||||
// 289 bytes
|
// 289 bytes
|
||||||
};
|
};
|
||||||
@@ -686,8 +694,16 @@ 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,
|
||||||
|
0x09, 0x37,
|
||||||
|
0x95, 0x3F,
|
||||||
|
0xB1, 0x02,
|
||||||
|
0x85, 0xF7,
|
||||||
|
0x09, 0x38,
|
||||||
|
0x95, 0x3F,
|
||||||
|
0xB1, 0x02,
|
||||||
0xC0, // End Collection
|
0xC0, // End Collection
|
||||||
// 405 bytes
|
// 417 bytes
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user