Coverage Report

Created: 2025-07-11 06:31

/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
31.4M
{
34
31.4M
  g_return_val_if_fail(FU_IS_HID_REPORT_ITEM(self), 0);
35
31.4M
  return fu_firmware_get_idx(FU_FIRMWARE(self)) & 0b11;
36
31.4M
}
37
38
guint32
39
fu_hid_report_item_get_value(FuHidReportItem *self)
40
30.9M
{
41
30.9M
  g_return_val_if_fail(FU_IS_HID_REPORT_ITEM(self), 0);
42
30.9M
  return self->value;
43
30.9M
}
44
45
static void
46
fu_hid_report_item_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
47
111k
{
48
111k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
49
111k
  fu_xmlb_builder_insert_kv(bn,
50
111k
          "kind",
51
111k
          fu_hid_item_kind_to_string(fu_hid_report_item_get_kind(self)));
52
111k
  fu_xmlb_builder_insert_kx(bn, "value", self->value);
53
111k
}
54
55
static gboolean
56
fu_hid_report_item_parse(FuFirmware *firmware,
57
       GInputStream *stream,
58
       FuFirmwareParseFlags flags,
59
       GError **error)
60
111k
{
61
111k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
62
111k
  const guint8 size_lookup[] = {0, 1, 2, 4};
63
111k
  guint8 data_size;
64
111k
  guint8 tag;
65
111k
  guint8 val = 0;
66
67
111k
  if (!fu_input_stream_read_u8(stream, 0x0, &val, error))
68
0
    return FALSE;
69
111k
  data_size = size_lookup[val & 0b11];
70
111k
  tag = (val & 0b11111100) >> 2;
71
111k
  fu_firmware_set_idx(firmware, tag);
72
111k
  fu_firmware_set_id(firmware, fu_hid_item_tag_to_string(tag));
73
74
111k
  if (tag == FU_HID_ITEM_TAG_LONG && data_size == 2) {
75
3.89k
    gsize streamsz = 0;
76
3.89k
    if (!fu_input_stream_size(stream, &streamsz, error))
77
0
      return FALSE;
78
3.89k
    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
3.89k
    if (!fu_input_stream_read_u8(stream, 1, &data_size, error))
86
12
      return FALSE;
87
107k
  } else {
88
107k
    g_autoptr(GInputStream) partial_stream = NULL;
89
107k
    if (data_size == 1) {
90
21.9k
      guint8 value = 0;
91
21.9k
      if (!fu_input_stream_read_u8(stream, 1, &value, error))
92
26
        return FALSE;
93
21.9k
      self->value = value;
94
85.3k
    } else if (data_size == 2) {
95
3.19k
      guint16 value = 0;
96
3.19k
      if (!fu_input_stream_read_u16(stream, 1, &value, G_LITTLE_ENDIAN, error))
97
35
        return FALSE;
98
3.16k
      self->value = value;
99
82.1k
    } else if (data_size == 4) {
100
6.37k
      if (!fu_input_stream_read_u32(stream,
101
6.37k
                  1,
102
6.37k
                  &self->value,
103
6.37k
                  G_LITTLE_ENDIAN,
104
6.37k
                  error))
105
33
        return FALSE;
106
6.37k
    }
107
107k
    partial_stream = fu_partial_input_stream_new(stream, 1, data_size, error);
108
107k
    if (partial_stream == NULL) {
109
0
      g_prefix_error(error, "failed to cut HID payload: ");
110
0
      return FALSE;
111
0
    }
112
107k
    if (!fu_firmware_set_stream(firmware, partial_stream, error))
113
0
      return FALSE;
114
107k
  }
115
116
  /* success */
117
111k
  fu_firmware_set_size(firmware, 1 + data_size);
118
111k
  return TRUE;
119
111k
}
120
121
static GByteArray *
122
fu_hid_report_item_write(FuFirmware *firmware, GError **error)
123
6.79k
{
124
6.79k
  FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware);
125
6.79k
  g_autoptr(GByteArray) st = g_byte_array_new();
126
6.79k
  guint8 tmp = fu_firmware_get_idx(firmware) << 2;
127
128
6.79k
  if (self->value == 0) {
129
4.83k
    fu_byte_array_append_uint8(st, tmp);
130
4.83k
  } else if (self->value <= G_MAXUINT8) {
131
1.13k
    tmp |= 0b01;
132
1.13k
    fu_byte_array_append_uint8(st, tmp);
133
1.13k
    fu_byte_array_append_uint8(st, self->value);
134
1.13k
  } else if (self->value <= G_MAXUINT16) {
135
429
    tmp |= 0b10;
136
429
    fu_byte_array_append_uint8(st, tmp);
137
429
    fu_byte_array_append_uint16(st, self->value, G_LITTLE_ENDIAN);
138
429
  } else {
139
389
    tmp |= 0b11;
140
389
    fu_byte_array_append_uint8(st, tmp);
141
389
    fu_byte_array_append_uint32(st, self->value, G_LITTLE_ENDIAN);
142
389
  }
143
144
  /* success */
145
6.79k
  return g_steal_pointer(&st);
146
6.79k
}
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
111k
{
182
111k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION);
183
111k
}
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
111k
{
207
111k
  return g_object_new(FU_TYPE_HID_REPORT_ITEM, NULL);
208
111k
}