主机不更新报告中的数据

2024-01-02

我正在尝试在 Raspberry Pi Pico 上使用 TinyUSB 开发自定义 UPS,并让 UPower 在我的 Ubuntu 主机上发现它。我试图向主机发出充电状态信号,但主机将电池报告为空(UPower),而不是充电(等离子桌面电池指示器),就好像没有发送报告一样。我如何让 UPower 对报告做出反应?我的设备已被检测到,但主机未读取任何信息:

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。设备的报告描述符:

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

Parsed:

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 无法识别fully charged and fully discharged旗帜。我尝试删除它们并只保留charging标志,以及添加absolute state of charge。在 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

我变了TUD_HID_DESCRIPTOR to TUD_HID_INOUT_DESCRIPTOR并将端点编号设置为 1。现在从设备传输到主机完成并显示成功状态,而不是“没有此类文件或目录”。但是,主机使用以下方式发送报告0x81IN 端点而不是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;
}

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

然而,设备尝试将类似的消息发送回主机,但它没有到达。我将数据包跟踪到设备层,数据包被写入硬件缓冲区。我发现的唯一区别是它是在端点 1 处发送的,而所有描述符都是在端点 0 处发送回来的。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

主机不更新报告中的数据 的相关文章

  • 迭代变量并查找特定类型实例的技术

    我想迭代进程中内存中的变量 通过插件动态加载 并查找特定类型的实例 以前我可以找到特定类型 或内存中的所有类型 我可以创建类型的实例 我可以获取作为不同类型的字段包含的实例 但我无论如何都不知道只是 搜索 特定类型的实例 一种方法是使用 W
  • ftrace:仅打印trace_printk()的输出

    是否可以只转储trace printk 输出于trace文件 我的意思是过滤掉函数跟踪器 或任何其他跟踪器 中的所有函数 一般来说 您可以在选项目录中关闭选项 sys kernel debug tracing options Use ls显
  • JSON 数组到 C# 列表

    如何将这个简单的 JSON 字符串反序列化为 C 中的列表 on4ThnU7 n71YZYVKD CVfSpM2W 10kQotV 这样 List
  • C++ 异步线程同时运行

    我是 C 11 中线程的新手 我有两个线程 我想让它们同时启动 我可以想到两种方法 如下 然而 似乎它们都没有按照我的预期工作 他们在启动另一个线程之前启动一个线程 任何提示将不胜感激 另一个问题是我正在研究线程队列 所以我会有两个消费者和
  • 从多个类访问串行端口

    我正在尝试使用串行端口在 arduino 和 C 程序之间进行通信 我对 C 编程有点陌生 该程序有多种用户控制形式 每一个都需要访问串口来发送数据 我需要做的就是从每个类的主窗体中写入串行端口 我了解如何设置和写入串行端口 这是我的 Fo
  • 暂停下载线程

    我正在用 C 编写一个非常简单的批量下载程序 该程序读取要下载的 URL 的 txt 文件 我已经设置了一个全局线程和委托来更新 GUI 按下 开始 按钮即可创建并启动该线程 我想要做的是有一个 暂停 按钮 使我能够暂停下载 直到点击 恢复
  • 访问者和模板化虚拟方法

    在一个典型的实现中Visitor模式 该类必须考虑基类的所有变体 后代 在许多情况下 访问者中的相同方法内容应用于不同的方法 在这种情况下 模板化的虚拟方法是理想的选择 但目前这是不允许的 那么 模板化方法可以用来解析父类的虚方法吗 鉴于
  • 如何从 C# 控制器重定向到外部 url

    我使用 C 控制器作为网络服务 在其中我想将用户重定向到外部网址 我该怎么做 Tried System Web HttpContext Current Response Redirect 但没有成功 使用控制器的重定向 http msdn
  • IronPython:没有名为 json 的模块

    我安装了 IronPython 我的 python 文件如下所示 import sys print sys version import json 运行它的代码 var p Python CreateEngine var scope p C
  • 如何识别 WPF 文本框中的 ValidationError 工具提示位置

    我添加了一个箭头来指示工具提示中的文本框 当文本框远离屏幕边缘时 这非常有效 但是当它靠近屏幕边缘时 工具提示位置发生变化 箭头显示在左侧 Here is the Image Correct as expected since TextBo
  • 如何从网站下载 .EXE 文件?

    我正在编写一个应用程序 需要从网站下载 exe 文件 我正在使用 Visual Studio Express 2008 我正在使用以下代码 private void button1 Click object sender EventArgs
  • Azure 事件中心 - 按顺序接收事件

    我使用下面的代码从 Azure Event Hub 接收事件 https learn microsoft com en us azure event hubs event hubs dotnet framework getstarted s
  • C# 中条件编译符号的编译时检查(参见示例)?

    在 C C 中你可以这样做 define IN USE 1 define NOT IN USE 1 define USING system 1 system 1 IN USE 进而 define MY SYSTEM IN USE if US
  • 为什么我不应该对不是由 malloc() 分配的变量调用 free() ?

    我在某处读到 使用它是灾难性的free删除不是通过调用创建的对象malloc 这是真的 为什么 这是未定义的行为 永远不要尝试它 让我们看看当您尝试时会发生什么free 自动变量 堆管理器必须推断出如何获取内存块的所有权 为此 它要么必须使
  • 在 C 中使用 GNU automake 中的解析器

    我是 GNU autotools 的新手 在我的项目中使用了 lex 和 yacc 解析器 将它们作为 makefile am 中的源代码会产生以下错误 配置 in AC CHECK PROGS YACC bison yacc none i
  • 如何使用 GOPATH 的 Samba 服务器位置?

    我正在尝试将 GOPATH 设置为共享网络文件夹 当我进入 export GOPATH smb path to shared folder I get go GOPATH entry is relative must be absolute
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 尚未处理时调用 Form 的 Invoke 时出现 ObjectDisposeException

    我们得到一个ObjectDisposedException从一个电话到Invoke在尚未处理的表格上 这是一些演示该问题的示例代码 public partial class Form2 Form void Form2 Load object
  • 为什么拆箱枚举会产生奇怪的结果?

    考虑以下 Object box 5 int int int box int 5 int nullableInt box as int nullableInt 5 StringComparison enum StringComparison
  • 我在在线程序挑战编译器中遇到演示错误

    include

随机推荐