定制 HID UPS - 主机不更新报告中的数据

问题描述 投票:0回答:2

我正在尝试在 Raspberry Pi Pico 上使用 TinyUSB 开发自定义 UPS,并让 UPower 在我的 Ubuntu 主机上发现它。

目前我的设备已被检测到,但主机未读取任何信息:

root@user-HP:~# upower -i /org/freedesktop/UPower/devices/ups_hiddev0
  native-path:          /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/usbmisc/hiddev0
  vendor:               APC
  model:                UPS
  serial:               123456
  power supply:         yes
  updated:              Sun, 02 Apr 2023 09:35:04 +0300 (16 seconds ago)
  has history:          yes
  has statistics:       yes
  ups
    present:             yes
    state:               empty
    warning-level:       none
    percentage:          0%
    icon-name:          'battery-empty-symbolic'

我的期望是

state
将是
charging
(请参阅下面的测试报告)。

2023 年 4 月 5 日编辑:根据 aja 的建议更新了报告描述符

设备的报告描述符如下所示:

05 84 09 04 a1 01 09 10 a1 00 85 01 75 08 95 01 09 fd 79 01 b1 03 09 fe 79 02 b1 03 09 ff 79 03 b1 03 09 12 a1 00 85 02 05 85 75 01 95 04 17 00 00 00 00 27 01 00 00 00 09 44 09 45 09 46 09 47 81 82 95 01 75 04 81 01 c0 c0 c0 

  INPUT(2)[INPUT]
    Field(0)
      Physical(Power Device.Battery)
      Application(Power Device.UPS)
      Usage(4)
        Battery System.Charging
        Battery System.Discharging
        Battery System.0046
        Battery System.0047
      Logical Minimum(0)
      Logical Maximum(1)
      Report Size(1)
      Report Count(4)
      Report Offset(0)
      Flags( Variable Absolute Volatile )
  FEATURE(1)[FEATURE]
    Field(0)
      Physical(Power Device.BatterySystem)
      Application(Power Device.UPS)
      Usage(1)
        Power Device.iManufacturer
      Report Size(8)
      Report Count(1)
      Report Offset(0)
      Flags( Constant Variable Absolute )
    Field(1)
      Physical(Power Device.BatterySystem)
      Application(Power Device.UPS)
      Usage(1)
        Power Device.iProduct
      Report Size(8)
      Report Count(1)
      Report Offset(8)
      Flags( Constant Variable Absolute )
    Field(2)
      Physical(Power Device.BatterySystem)
      Application(Power Device.UPS)
      Usage(1)
        Power Device.iSerialNumber
      Report Size(8)
      Report Count(1)
      Report Offset(16)
      Flags( Constant Variable Absolute )

Battery System.Charging ---> Sync.Report
Battery System.Discharging ---> Sync.Report
Battery System.0046 ---> Sync.Report
Battery System.0047 ---> Sync.Report

解析:

USAGE_PAGE (Power Device)
USAGE (UPS)
COLLECTION (Application)
  USAGE (Battery System)
  COLLECTION (Physical)
    REPORT_ID (1)
    REPORT_SIZE (8)
    REPORT_COUNT (1)

    USAGE (iManufacturer)
    STRING_INDEX (1)
    FEATURE (Constant Variable Absolute)

    USAGE (iProduct)
    STRING_INDEX (2)
    FEATURE (Constant Variable Absolute)

    USAGE (iSerialNumber)
    STRING_INDEX (3)
    FEATURE (Constant Variable Absolute)

    USAGE (Battery)
    COLLECTION (Physical)
      REPORT_ID (2)
      USAGE_PAGE (Battery System)
      REPORT_SIZE (1)
      REPORT_COUNT (4)
      LOGICAL_MINIMUM (0)
      LOGICAL_MAXIMUM (1)
      USAGE (Charging)
      USAGE (Discharging)
      USAGE (Fully Charged)
      USAGE (Fully Discharged)
      INPUT (Variable Absolute Volatile)

      REPORT_COUNT (1)
      REPORT_SIZE (4)
      INPUT (Constant Array)
    END_COLLECTION
  END_COLLECTION
END_COLLECTION

Linux 似乎无法识别“完全充电”和“完全放电”标志。我还尝试删除它们并仅保留 charging 标志,以及添加其他数据点,例如 绝对充电状态 在我的 main.cpp 文件中,我发送如下报告:

#include "pico/stdlib.h" #include "hardware/pwm.h" #include "bsp/board.h" #include "tusb.h" int main() { board_init(); tusb_init(); uint32_t last_run = 0; uint8_t report1[] = {1, 2, 3}; uint8_t report2[] = {0b00000001}; while (1) { tud_task(); // Send data every 50 ms if (board_millis() - last_run > 50 && tud_hid_ready() && !tud_suspended()) { last_run = board_millis(); // tud_hid_report(1, report1, sizeof(report1)); // for some reason if I send 2 reports at once, only #1 is received on the host tud_hid_report(2, report2, sizeof(report2)); } } } // Invoked when received GET_REPORT control request // Application must fill buffer report's content and return its length. // Return zero will cause the stack to STALL request uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { switch (report_id) { case 1: buffer[0] = 1; buffer[1] = 2; buffer[2] = 3; return 3; case 2: buffer[0] = 0b00000001; // Report charging state return 1; default: return 0; } } // Invoked when received SET_REPORT control request or // received data on OUT endpoint ( Report ID = 0, Type = 0 ) void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { // echo back anything we received from host tud_hid_report(0, buffer, bufsize); }

我不完全确定哪种是发送报告的正确方法:使用 

tud_hid_get_report_cb
 或使用循环和 
tud_hid_report

。任何有关此的提示将不胜感激。

接收端的报告对我来说看起来是正确的:
root@user-HP:~# usbhid-dump -a 1:119 -e all
001:119:000:DESCRIPTOR         1680420804.631776
 05 84 09 10 A1 01 85 01 75 08 95 01 09 FD 79 01
 B1 03 09 FE 79 02 B1 03 09 FF 79 03 B1 03 09 12
 A1 00 85 02 05 85 75 01 95 04 17 00 00 00 00 27
 01 00 00 00 09 44 09 45 09 46 09 47 81 82 95 01
 75 04 81 01 C0 C0

Starting dumping interrupt transfer stream
with 1 minute timeout.

001:119:000:STREAM             1680420804.643196
 02 01

001:119:000:STREAM             1680420804.694348
 02 01

001:119:000:STREAM             1680420804.745391
 02 01

001:119:000:STREAM             1680420804.796426
 02 01

001:119:000:STREAM             1680420804.847433
 02 01

001:119:000:STREAM             1680420804.898256
 02 01

编辑 2023 年 4 月 5 日:我使用 Wireshark 进行了一些调试,并了解了一些有关 USB 端点的知识,我对以下文件进行了一些更改:将
TUD_HID_DESCRIPTOR
更改为

TUD_HID_INOUT_DESCRIPTOR,并将端点编号更改为 1。现在,从设备到主机的传输完成并显示成功状态,而不是

No such file or directory
。但是,存在一个问题:主机使用
0x81
IN 端点而不是正确的
0x01
OUT 端点向设备发送报告。

最后,这是我的
usb_descriptors.cpp

#include "tusb.h" #include "report-descriptor/descriptor.hpp" //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = 0x00, .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, // Using APC vendor ID, because a UPS from an unknown vendor won't be detected .idVendor = 0x051D, .idProduct = 0x0003, .bcdDevice = 0x0100, .iManufacturer = 0x01, .iProduct = 0x02, .iSerialNumber = 0x03, .bNumConfigurations = 0x01 }; // Invoked when received GET DEVICE DESCRIPTOR // Application return pointer to descriptor uint8_t const *tud_descriptor_device_cb(void) { return (uint8_t const *)&desc_device; } //--------------------------------------------------------------------+ // HID Report Descriptor //--------------------------------------------------------------------+ // Invoked when received GET HID REPORT DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) { return hid_report_descriptor_test.data(); } //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ enum {ITF_NUM_HID, ITF_NUM_TOTAL}; #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN) #define EPNUM_HID 0x01 uint8_t const desc_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval TUD_HID_INOUT_DESCRIPTOR( ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, hid_report_descriptor_test.size(), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_BUFSIZE, 1 ) }; // Invoked when received GET CONFIGURATION DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { (void)index; // for multiple configurations return desc_configuration; } //--------------------------------------------------------------------+ // String Descriptors //--------------------------------------------------------------------+ // array of pointer to string descriptors char const *string_desc_arr[] = { (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) "American Power Conversion", // 1: Manufacturer "UPS", // 2: Product "123456", // 3: Serials, should use chip ID "LiFePO4", // 4: Battery chemistry - unused for now }; static uint16_t _desc_str[32]; // Invoked when received GET STRING DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { (void)langid; uint8_t chr_count; if (index == 0) { memcpy(&_desc_str[1], string_desc_arr[0], 2); chr_count = 1; } else { // Convert ASCII string into UTF-16 if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; const char *str = string_desc_arr[index]; // Cap at max char chr_count = strlen(str); if (chr_count > 31) chr_count = 31; for (uint8_t i = 0; i < chr_count; i++) { _desc_str[1 + i] = str[i]; } } // first byte is length (including header), second byte is string type _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); return _desc_str; }

重申一下,我试图向主机发出充电状态信号,但主机报告电池电量为空(UPower)并且未充电(等离子桌面电池指示器),就好像设备没有发送任何报告一样。我如何让 UPower 对报告做出反应?


我了解到您有一个设备并希望向主机发送 HID 报告。您是否尝试过主机是否可以向设备发送报告?我有一个类似的设置,但不是在 Raspberry Pi 上,而是在 STM32G0B1 上,我尝试为 TinyUSB 为低级驱动程序创建一个端口。就我而言,主机可以发送 HID 报告,然后该报告会到达设备。对我来说,这是一种心跳消息。

linux usb hid raspberry-pi-pico
2个回答
1
投票

所以,就我而言,访问正确的硬件寄存器时可能存在地址不匹配,但我不知道。也许我以错误的方式使用 TinyUSB 堆栈。到目前为止,您在 Raspberry Pi 上找到问题的解决方案了吗?

@安东@aja

https://gist.github.com/Pillar1989/df2615ab29bf9c98f90775ce41635c10

0
投票
我已经根据您的代码完善了所有功能,希望它有所帮助,我在 Seeed XIAO 上进行了测试。

https://wiki.seeedstudio.com/Seeeduino-XIAO

© www.soinside.com 2019 - 2024. All rights reserved.