Coverage Report

Created: 2026-01-25 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-hid-report-item.c
Line
Count
Source
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
80.0M
G_DEFINE_TYPE(FuHidReportItem, fu_hid_report_item, FU_TYPE_FIRMWARE)
30
80.0M
31
80.0M
FuHidItemKind
32
80.0M
fu_hid_report_item_get_kind(FuHidReportItem *self)
33
80.0M
{
34
40.1M
  g_return_val_if_fail(FU_IS_HID_REPORT_ITEM(self), 0);
35
40.1M
  return fu_firmware_get_idx(FU_FIRMWARE(self)) & 0b11;
36
40.1M
}
37
38
guint32
39
fu_hid_report_item_get_value(FuHidReportItem *self)
40
39.3M
{
41
39.3M
  g_return_val_if_fail(FU_IS_HID_REPORT_ITEM(self), 0);
42
39.3M
  return self->value;
43
39.3M
}
44
45
static void
46
fu_hid_report_item_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
47
173k
{
48
173k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
49
173k
  fu_xmlb_builder_insert_kv(bn,
50
173k
          "kind",
51
173k
          fu_hid_item_kind_to_string(fu_hid_report_item_get_kind(self)));
52
173k
  fu_xmlb_builder_insert_kx(bn, "value", self->value);
53
173k
}
54
55
static gboolean
56
fu_hid_report_item_parse(FuFirmware *firmware,
57
       GInputStream *stream,
58
       FuFirmwareParseFlags flags,
59
       GError **error)
60
173k
{
61
173k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
62
173k
  const guint8 size_lookup[] = {0, 1, 2, 4};
63
173k
  guint8 data_size;
64
173k
  guint8 tag;
65
173k
  guint8 val = 0;
66
67
173k
  if (!fu_input_stream_read_u8(stream, 0x0, &val, error))
68
0
    return FALSE;
69
173k
  data_size = size_lookup[val & 0b11];
70
173k
  tag = (val & 0b11111100) >> 2;
71
173k
  fu_firmware_set_idx(firmware, tag);
72
173k
  fu_firmware_set_id(firmware, fu_hid_item_tag_to_string(tag));
73
74
173k
  if (tag == FU_HID_ITEM_TAG_LONG && data_size == 2) {
75
6.51k
    gsize streamsz = 0;
76
6.51k
    if (!fu_input_stream_size(stream, &streamsz, error))
77
0
      return FALSE;
78
6.51k
    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
6.51k
    if (!fu_input_stream_read_u8(stream, 1, &data_size, error))
86
7
      return FALSE;
87
166k
  } else {
88
166k
    g_autoptr(GInputStream) partial_stream = NULL;
89
166k
    if (data_size == 1) {
90
27.0k
      guint8 value = 0;
91
27.0k
      if (!fu_input_stream_read_u8(stream, 1, &value, error))
92
41
        return FALSE;
93
27.0k
      self->value = value;
94
139k
    } else if (data_size == 2) {
95
2.34k
      guint16 value = 0;
96
2.34k
      if (!fu_input_stream_read_u16(stream, 1, &value, G_LITTLE_ENDIAN, error))
97
37
        return FALSE;
98
2.30k
      self->value = value;
99
137k
    } else if (data_size == 4) {
100
3.63k
      if (!fu_input_stream_read_u32(stream,
101
3.63k
                  1,
102
3.63k
                  &self->value,
103
3.63k
                  G_LITTLE_ENDIAN,
104
3.63k
                  error))
105
39
        return FALSE;
106
3.63k
    }
107
166k
    partial_stream = fu_partial_input_stream_new(stream, 1, data_size, error);
108
166k
    if (partial_stream == NULL) {
109
0
      g_prefix_error_literal(error, "failed to cut HID payload: ");
110
0
      return FALSE;
111
0
    }
112
166k
    if (!fu_firmware_set_stream(firmware, partial_stream, error))
113
0
      return FALSE;
114
166k
  }
115
116
  /* success */
117
173k
  fu_firmware_set_size(firmware, 1 + data_size);
118
173k
  return TRUE;
119
173k
}
120
121
static GByteArray *
122
fu_hid_report_item_write(FuFirmware *firmware, GError **error)
123
6.25k
{
124
6.25k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
125
6.25k
  g_autoptr(GByteArray) st = g_byte_array_new();
126
6.25k
  guint8 tmp = fu_firmware_get_idx(firmware) << 2;
127
128
6.25k
  if (self->value == 0) {
129
4.65k
    fu_byte_array_append_uint8(st, tmp);
130
4.65k
  } else if (self->value <= G_MAXUINT8) {
131
911
    tmp |= 0b01;
132
911
    fu_byte_array_append_uint8(st, tmp);
133
911
    fu_byte_array_append_uint8(st, self->value);
134
911
  } else if (self->value <= G_MAXUINT16) {
135
315
    tmp |= 0b10;
136
315
    fu_byte_array_append_uint8(st, tmp);
137
315
    fu_byte_array_append_uint16(st, self->value, G_LITTLE_ENDIAN);
138
371
  } else {
139
371
    tmp |= 0b11;
140
371
    fu_byte_array_append_uint8(st, tmp);
141
371
    fu_byte_array_append_uint32(st, self->value, G_LITTLE_ENDIAN);
142
371
  }
143
144
  /* success */
145
6.25k
  return g_steal_pointer(&st);
146
6.25k
}
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
173k
{
182
173k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION);
183
173k
}
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
173k
{
207
173k
  return g_object_new(FU_TYPE_HID_REPORT_ITEM, NULL);
208
173k
}