Coverage Report

Created: 2025-08-03 06:57

/src/fwupd/libfwupdplugin/fu-hid-report-item.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2023 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuHidDevice"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-common.h"
13
#include "fu-hid-report-item.h"
14
#include "fu-input-stream.h"
15
#include "fu-partial-input-stream.h"
16
#include "fu-string.h"
17
18
/**
19
 * FuHidReportItem:
20
 *
21
 * See also: [class@FuHidDescriptor]
22
 */
23
24
struct _FuHidReportItem {
25
  FuFirmware parent_instance;
26
  guint32 value;
27
};
28
29
G_DEFINE_TYPE(FuHidReportItem, fu_hid_report_item, FU_TYPE_FIRMWARE)
30
31
FuHidItemKind
32
fu_hid_report_item_get_kind(FuHidReportItem *self)
33
35.1M
{
34
35.1M
  g_return_val_if_fail(FU_IS_HID_REPORT_ITEM(self), 0);
35
35.1M
  return fu_firmware_get_idx(FU_FIRMWARE(self)) & 0b11;
36
35.1M
}
37
38
guint32
39
fu_hid_report_item_get_value(FuHidReportItem *self)
40
34.5M
{
41
34.5M
  g_return_val_if_fail(FU_IS_HID_REPORT_ITEM(self), 0);
42
34.5M
  return self->value;
43
34.5M
}
44
45
static void
46
fu_hid_report_item_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
47
123k
{
48
123k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
49
123k
  fu_xmlb_builder_insert_kv(bn,
50
123k
          "kind",
51
123k
          fu_hid_item_kind_to_string(fu_hid_report_item_get_kind(self)));
52
123k
  fu_xmlb_builder_insert_kx(bn, "value", self->value);
53
123k
}
54
55
static gboolean
56
fu_hid_report_item_parse(FuFirmware *firmware,
57
       GInputStream *stream,
58
       FuFirmwareParseFlags flags,
59
       GError **error)
60
123k
{
61
123k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
62
123k
  const guint8 size_lookup[] = {0, 1, 2, 4};
63
123k
  guint8 data_size;
64
123k
  guint8 tag;
65
123k
  guint8 val = 0;
66
67
123k
  if (!fu_input_stream_read_u8(stream, 0x0, &val, error))
68
0
    return FALSE;
69
123k
  data_size = size_lookup[val & 0b11];
70
123k
  tag = (val & 0b11111100) >> 2;
71
123k
  fu_firmware_set_idx(firmware, tag);
72
123k
  fu_firmware_set_id(firmware, fu_hid_item_tag_to_string(tag));
73
74
123k
  if (tag == FU_HID_ITEM_TAG_LONG && data_size == 2) {
75
4.31k
    gsize streamsz = 0;
76
4.31k
    if (!fu_input_stream_size(stream, &streamsz, error))
77
0
      return FALSE;
78
4.31k
    if (streamsz < 1) {
79
0
      g_set_error_literal(error,
80
0
              FWUPD_ERROR,
81
0
              FWUPD_ERROR_INVALID_DATA,
82
0
              "not enough data to read long tag");
83
0
      return FALSE;
84
0
    }
85
4.31k
    if (!fu_input_stream_read_u8(stream, 1, &data_size, error))
86
8
      return FALSE;
87
119k
  } else {
88
119k
    g_autoptr(GInputStream) partial_stream = NULL;
89
119k
    if (data_size == 1) {
90
28.8k
      guint8 value = 0;
91
28.8k
      if (!fu_input_stream_read_u8(stream, 1, &value, error))
92
47
        return FALSE;
93
28.7k
      self->value = value;
94
90.6k
    } else if (data_size == 2) {
95
3.64k
      guint16 value = 0;
96
3.64k
      if (!fu_input_stream_read_u16(stream, 1, &value, G_LITTLE_ENDIAN, error))
97
42
        return FALSE;
98
3.60k
      self->value = value;
99
87.0k
    } else if (data_size == 4) {
100
11.3k
      if (!fu_input_stream_read_u32(stream,
101
11.3k
                  1,
102
11.3k
                  &self->value,
103
11.3k
                  G_LITTLE_ENDIAN,
104
11.3k
                  error))
105
32
        return FALSE;
106
11.3k
    }
107
119k
    partial_stream = fu_partial_input_stream_new(stream, 1, data_size, error);
108
119k
    if (partial_stream == NULL) {
109
0
      g_prefix_error(error, "failed to cut HID payload: ");
110
0
      return FALSE;
111
0
    }
112
119k
    if (!fu_firmware_set_stream(firmware, partial_stream, error))
113
0
      return FALSE;
114
119k
  }
115
116
  /* success */
117
123k
  fu_firmware_set_size(firmware, 1 + data_size);
118
123k
  return TRUE;
119
123k
}
120
121
static GByteArray *
122
fu_hid_report_item_write(FuFirmware *firmware, GError **error)
123
7.05k
{
124
7.05k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
125
7.05k
  g_autoptr(GByteArray) st = g_byte_array_new();
126
7.05k
  guint8 tmp = fu_firmware_get_idx(firmware) << 2;
127
128
7.05k
  if (self->value == 0) {
129
5.17k
    fu_byte_array_append_uint8(st, tmp);
130
5.17k
  } else if (self->value <= G_MAXUINT8) {
131
1.04k
    tmp |= 0b01;
132
1.04k
    fu_byte_array_append_uint8(st, tmp);
133
1.04k
    fu_byte_array_append_uint8(st, self->value);
134
1.04k
  } else if (self->value <= G_MAXUINT16) {
135
426
    tmp |= 0b10;
136
426
    fu_byte_array_append_uint8(st, tmp);
137
426
    fu_byte_array_append_uint16(st, self->value, G_LITTLE_ENDIAN);
138
426
  } else {
139
414
    tmp |= 0b11;
140
414
    fu_byte_array_append_uint8(st, tmp);
141
414
    fu_byte_array_append_uint32(st, self->value, G_LITTLE_ENDIAN);
142
414
  }
143
144
  /* success */
145
7.05k
  return g_steal_pointer(&st);
146
7.05k
}
147
148
static gboolean
149
fu_hid_report_item_build(FuFirmware *firmware, XbNode *n, GError **error)
150
0
{
151
0
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
152
0
  const gchar *tmp;
153
0
  guint64 value = 0;
154
155
  /* optional data */
156
0
  tmp = xb_node_query_text(n, "idx", NULL);
157
0
  if (tmp != NULL) {
158
0
    if (!fu_strtoull(tmp, &value, 0x0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error))
159
0
      return FALSE;
160
0
    fu_firmware_set_idx(firmware, value);
161
0
    fu_firmware_set_id(firmware, fu_hid_item_tag_to_string(value));
162
0
  }
163
0
  tmp = xb_node_query_text(n, "id", NULL);
164
0
  if (tmp != NULL) {
165
0
    fu_firmware_set_id(firmware, tmp);
166
0
    fu_firmware_set_idx(firmware, fu_hid_item_tag_from_string(tmp));
167
0
  }
168
0
  tmp = xb_node_query_text(n, "value", NULL);
169
0
  if (tmp != NULL) {
170
0
    if (!fu_strtoull(tmp, &value, 0x0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error))
171
0
      return FALSE;
172
0
    self->value = value;
173
0
  }
174
175
  /* success */
176
0
  return TRUE;
177
0
}
178
179
static void
180
fu_hid_report_item_init(FuHidReportItem *self)
181
123k
{
182
123k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION);
183
123k
}
184
185
static void
186
fu_hid_report_item_class_init(FuHidReportItemClass *klass)
187
1
{
188
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
189
1
  firmware_class->export = fu_hid_report_item_export;
190
1
  firmware_class->parse = fu_hid_report_item_parse;
191
1
  firmware_class->write = fu_hid_report_item_write;
192
1
  firmware_class->build = fu_hid_report_item_build;
193
1
}
194
195
/**
196
 * fu_hid_report_item_new:
197
 *
198
 * Creates a new HID report item
199
 *
200
 * Returns: (transfer full): a #FuHidReportItem
201
 *
202
 * Since: 1.9.4
203
 **/
204
FuHidReportItem *
205
fu_hid_report_item_new(void)
206
123k
{
207
123k
  return g_object_new(FU_TYPE_HID_REPORT_ITEM, NULL);
208
123k
}