Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/epan/dissectors/packet-usb-i1d3.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-usb-i1d3.c
2
 * Dissects the X-Rite i1 Display Pro (and derivatives) USB protocol
3
 * Copyright 2016, Etienne Dechamps <etienne@edechamps.fr>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*
13
 * This code dissects the USB protocol used for communicating with a
14
 * X-Rite i1 Display Pro colorimeter, as well as similar hardware such
15
 * as ColorMunki Display.
16
 *
17
 * Note that this protocol is proprietary and no public specification
18
 * exists. This code is largely based on Graeme Gill's reverse
19
 * engineering work for ArgyllCMS (see spectro/i1d3.c in the ArgyllCMS
20
 * source code).
21
 *
22
 * Because some aspects of the protocol are not yet fully understood,
23
 * this dissector might fail to properly parse some packets, especially
24
 * in unusual scenarios such as error conditions and the like.
25
 */
26
27
#include <config.h>
28
#include <epan/conversation.h>
29
#include <epan/packet.h>
30
#include <epan/expert.h>
31
#include <epan/tfs.h>
32
#include <epan/unit_strings.h>
33
34
#include <wsutil/array.h>
35
36
void proto_register_usb_i1d3(void);
37
void proto_reg_handoff_usb_i1d3(void);
38
39
static dissector_handle_t usb_i1d3_dissector;
40
41
0
#define USB_I1D3_PACKET_LENGTH (64)
42
0
#define USB_I1D3_CLOCK_FREQUENCY (12e6)  // 12 MHz
43
0
#define USB_I1D3_LED_OFFTIME_FACTOR (USB_I1D3_CLOCK_FREQUENCY / (1 << 19))
44
0
#define USB_I1D3_LED_ONTIME_FACTOR (USB_I1D3_CLOCK_FREQUENCY / (1 << 19))
45
0
#define USB_I1D3_LED_ONTIME_FADE_FACTOR (USB_I1D3_CLOCK_FREQUENCY / (1 << 23))
46
47
static int proto_usb_i1d3;
48
static int ett_usb_i1d3;
49
static int ett_usb_i1d3_measured_duration;
50
static int ett_usb_i1d3_requested_edge_count;
51
52
static int hf_usb_i1d3_challenge_response;
53
static int hf_usb_i1d3_challenge_data;
54
static int hf_usb_i1d3_challenge_decode_key;
55
static int hf_usb_i1d3_challenge_encode_key;
56
static int hf_usb_i1d3_command_code;
57
static int hf_usb_i1d3_diffuser_position;
58
static int hf_usb_i1d3_echoed_command_code;
59
static int hf_usb_i1d3_firmdate;
60
static int hf_usb_i1d3_firmver;
61
static int hf_usb_i1d3_information;
62
static int hf_usb_i1d3_measured_duration;
63
static int hf_usb_i1d3_measured_duration_red;
64
static int hf_usb_i1d3_measured_duration_green;
65
static int hf_usb_i1d3_measured_duration_blue;
66
static int hf_usb_i1d3_measured_edge_count;
67
static int hf_usb_i1d3_measured_edge_count_red;
68
static int hf_usb_i1d3_measured_edge_count_green;
69
static int hf_usb_i1d3_measured_edge_count_blue;
70
static int hf_usb_i1d3_led_mode;
71
static int hf_usb_i1d3_led_offtime;
72
static int hf_usb_i1d3_led_ontime;
73
static int hf_usb_i1d3_led_pulse_count;
74
static int hf_usb_i1d3_locked;
75
static int hf_usb_i1d3_prodname;
76
static int hf_usb_i1d3_prodtype;
77
static int hf_usb_i1d3_request_in;
78
static int hf_usb_i1d3_requested_edge_count;
79
static int hf_usb_i1d3_requested_edge_count_red;
80
static int hf_usb_i1d3_requested_edge_count_green;
81
static int hf_usb_i1d3_requested_edge_count_blue;
82
static int hf_usb_i1d3_requested_integration_time;
83
static int hf_usb_i1d3_response_code;
84
static int hf_usb_i1d3_response_in;
85
static int hf_usb_i1d3_readextee_data;
86
static int hf_usb_i1d3_readextee_offset;
87
static int hf_usb_i1d3_readextee_length;
88
static int hf_usb_i1d3_readintee_data;
89
static int hf_usb_i1d3_readintee_offset;
90
static int hf_usb_i1d3_readintee_length;
91
static int hf_usb_i1d3_status;
92
static int hf_usb_i1d3_unlock_result;
93
94
static expert_field ei_usb_i1d3_echoed_command_code_mismatch;
95
static expert_field ei_usb_i1d3_error;
96
static expert_field ei_usb_i1d3_unexpected_response;
97
static expert_field ei_usb_i1d3_unknown_command;
98
static expert_field ei_usb_i1d3_unknown_diffuser_position;
99
static expert_field ei_usb_i1d3_unlock_failed;
100
static expert_field ei_usb_i1d3_unusual_length;
101
102
// Derived from ArgyllCMS spectro/i1d3.c.
103
typedef enum _usb_i1d3_command_code {
104
    USB_I1D3_GET_INFO      = 0x0000,
105
    USB_I1D3_STATUS        = 0x0001,
106
    USB_I1D3_PRODNAME      = 0x0010,
107
    USB_I1D3_PRODTYPE      = 0x0011,
108
    USB_I1D3_FIRMVER       = 0x0012,
109
    USB_I1D3_FIRMDATE      = 0x0013,
110
    USB_I1D3_LOCKED        = 0x0020,
111
    USB_I1D3_MEASURE1      = 0x0100,
112
    USB_I1D3_MEASURE2      = 0x0200,
113
    USB_I1D3_READINTEE     = 0x0800,
114
    USB_I1D3_READEXTEE     = 0x1200,
115
    USB_I1D3_SETLED        = 0x2100,
116
    USB_I1D3_RD_SENSOR     = 0x9300,
117
    USB_I1D3_GET_DIFF      = 0x9400,
118
    USB_I1D3_LOCKCHAL      = 0x9900,
119
    USB_I1D3_LOCKRESP      = 0x9a00,
120
    USB_I1D3_RELOCK        = 0x9b00,
121
} usb_i1d3_command_code;
122
static const value_string usb_i1d3_command_code_strings[] = {
123
    {USB_I1D3_GET_INFO,    "Get information"},
124
    {USB_I1D3_STATUS,      "Get status"},
125
    {USB_I1D3_PRODNAME,    "Get product name"},
126
    {USB_I1D3_PRODTYPE,    "Get product type"},
127
    {USB_I1D3_FIRMVER,     "Get firmware version"},
128
    {USB_I1D3_FIRMDATE,    "Get firmware date"},
129
    {USB_I1D3_LOCKED,      "Get locked status"},
130
    {USB_I1D3_MEASURE1,    "Make measurement (fixed integration time)"},
131
    {USB_I1D3_MEASURE2,    "Make measurement (fixed edge count)"},
132
    {USB_I1D3_READINTEE,   "Read internal EEPROM"},
133
    {USB_I1D3_READEXTEE,   "Read external EEPROM"},
134
    {USB_I1D3_SETLED,      "Set LED state"},
135
    {USB_I1D3_RD_SENSOR,   "Read analog sensor"},
136
    {USB_I1D3_GET_DIFF,    "Get diffuser position"},
137
    {USB_I1D3_LOCKCHAL,    "Request lock challenge"},
138
    {USB_I1D3_LOCKRESP,    "Unlock"},
139
    {USB_I1D3_RELOCK,      "Relock"},
140
    {0, NULL}
141
};
142
143
typedef enum _usb_i1d3_led_mode {
144
    USB_I1D3_LED_BLINK         = 1,
145
    USB_I1D3_LED_BLINK_FADE_ON = 3,
146
} usb_i1d3_led_mode;
147
static const value_string usb_i1d3_led_mode_strings[] = {
148
    {USB_I1D3_LED_BLINK, "Blink"},
149
    {USB_I1D3_LED_BLINK_FADE_ON, "Blink, fade on"},
150
    {0, NULL}
151
};
152
153
typedef enum _usb_i1d3_diffuser_position {
154
    USB_I1D3_DIFFUSER_DISPLAY = 0,
155
    USB_I1D3_DIFFUSER_AMBIENT = 1,
156
} usb_i1d3_diffuser_position;
157
static const value_string usb_i1d3_diffuser_position_strings[] = {
158
    {USB_I1D3_DIFFUSER_DISPLAY, "Display"},
159
    {USB_I1D3_DIFFUSER_AMBIENT, "Ambient"},
160
    {0, NULL}
161
};
162
163
typedef struct _usb_i1d3_transaction_t {
164
    uint32_t request;
165
    uint32_t response;
166
    uint32_t command_code;
167
    uint32_t offset;
168
    uint32_t length;
169
} usb_i1d3_transaction_t;
170
171
typedef struct _usb_i1d3_conversation_t {
172
    wmem_map_t *request_to_transaction;
173
    wmem_map_t *response_to_transaction;
174
    uint32_t previous_packet;
175
} usb_i1d3_conversation_t;
176
177
static const unit_name_string units_edge_edges = { " edge", " edges" };
178
static const unit_name_string units_pulse_pulses = { " pulse", " pulses" };
179
180
0
static usb_i1d3_conversation_t *usb_i1d3_get_conversation(packet_info *pinfo) {
181
0
    conversation_t *conversation = find_or_create_conversation(pinfo);
182
0
    usb_i1d3_conversation_t* i1d3_conversation =
183
0
        (usb_i1d3_conversation_t *)conversation_get_proto_data(
184
0
                conversation, proto_usb_i1d3);
185
0
    if (!i1d3_conversation) {
186
0
        i1d3_conversation = wmem_new0(
187
0
                wmem_file_scope(), usb_i1d3_conversation_t);
188
0
        i1d3_conversation->request_to_transaction = wmem_map_new(
189
0
                wmem_file_scope(), g_direct_hash, g_direct_equal);
190
0
        i1d3_conversation->response_to_transaction = wmem_map_new(
191
0
                wmem_file_scope(), g_direct_hash, g_direct_equal);
192
0
        conversation_add_proto_data(
193
0
                conversation, proto_usb_i1d3, i1d3_conversation);
194
0
    }
195
0
    return i1d3_conversation;
196
0
}
197
198
static usb_i1d3_transaction_t *usb_i1d3_create_transaction(
199
0
        usb_i1d3_conversation_t *conversation, uint32_t request) {
200
0
    usb_i1d3_transaction_t *transaction = wmem_new0(
201
0
            wmem_file_scope(), usb_i1d3_transaction_t);
202
0
    transaction->request = request;
203
0
    wmem_map_insert(
204
0
            conversation->request_to_transaction,
205
0
            GUINT_TO_POINTER(transaction->request), (void *)transaction);
206
0
    return transaction;
207
0
}
208
209
static void dissect_usb_i1d3_command(
210
        tvbuff_t *tvb, packet_info *pinfo,
211
0
        usb_i1d3_conversation_t *conversation, proto_tree *tree) {
212
    // Parsing the command code is a bit tricky: if the most significant
213
    // byte is non-zero, the command code is the most significant byte,
214
    // *and* the next byte is the first byte of the payload.
215
0
    uint32_t command_code = tvb_get_ntohs(tvb, 0);
216
0
    uint32_t command_code_msb = command_code & 0xff00;
217
0
    int command_code_length = 2;
218
0
    if (command_code_msb) {
219
0
        command_code = command_code_msb;
220
0
        command_code_length = 1;
221
0
    }
222
0
    proto_item *command_code_item = proto_tree_add_uint(
223
0
            tree, hf_usb_i1d3_command_code, tvb, 0, command_code_length,
224
0
            command_code);
225
226
0
    usb_i1d3_transaction_t *transaction;
227
0
    if (!PINFO_FD_VISITED(pinfo)) {
228
0
        transaction = usb_i1d3_create_transaction(conversation, pinfo->num);
229
0
        transaction->command_code = command_code;
230
0
    } else {
231
0
        transaction = (usb_i1d3_transaction_t *)wmem_map_lookup(
232
0
                conversation->request_to_transaction,
233
0
                GUINT_TO_POINTER(pinfo->num));
234
0
    }
235
0
    DISSECTOR_ASSERT(transaction);
236
237
0
    if (transaction->response != 0) {
238
0
        proto_item *response_item = proto_tree_add_uint(
239
0
                tree, hf_usb_i1d3_response_in, tvb, 0, 0,
240
0
                transaction->response);
241
0
        proto_item_set_generated(response_item);
242
0
    }
243
244
0
    const char *command_code_string = try_val_to_str(
245
0
            command_code, usb_i1d3_command_code_strings);
246
0
    if (command_code_string) {
247
0
        col_set_str(pinfo->cinfo, COL_INFO, command_code_string);
248
0
    } else {
249
0
        expert_add_info(pinfo, command_code_item,
250
0
                &ei_usb_i1d3_unknown_command);
251
0
        col_set_str(pinfo->cinfo, COL_INFO, "Unknown command");
252
0
    }
253
254
0
    switch (command_code) {
255
0
        case USB_I1D3_LOCKRESP: {
256
            // TODO: verify that the challenge response is correct
257
0
            proto_tree_add_item(
258
0
                    tree, hf_usb_i1d3_challenge_response, tvb, 24, 16, ENC_NA);
259
0
            break;
260
0
        }
261
262
0
        case USB_I1D3_READINTEE: {
263
0
            uint32_t offset, length;
264
0
            proto_tree_add_item_ret_uint(
265
0
                    tree, hf_usb_i1d3_readintee_offset, tvb,
266
0
                    1, 1, ENC_NA, &offset);
267
0
            proto_tree_add_item_ret_uint(
268
0
                    tree, hf_usb_i1d3_readintee_length, tvb,
269
0
                    2, 1, ENC_NA, &length);
270
0
            col_add_fstr(pinfo->cinfo, COL_INFO, "%s (offset: %u, length: %u)",
271
0
                    command_code_string, offset, length);
272
0
            if (!PINFO_FD_VISITED(pinfo)) {
273
0
                transaction->offset = offset;
274
0
                transaction->length = length;
275
0
            }
276
0
            break;
277
0
        }
278
279
0
        case USB_I1D3_READEXTEE: {
280
0
            uint32_t offset, length;
281
0
            proto_tree_add_item_ret_uint(
282
0
                    tree, hf_usb_i1d3_readextee_offset, tvb,
283
0
                    1, 2, ENC_BIG_ENDIAN, &offset);
284
0
            proto_tree_add_item_ret_uint(
285
0
                    tree, hf_usb_i1d3_readextee_length, tvb,
286
0
                    3, 1, ENC_NA, &length);
287
0
            col_add_fstr(pinfo->cinfo, COL_INFO, "%s (offset: %u, length: %u)",
288
0
                    command_code_string, offset, length);
289
0
            if (!PINFO_FD_VISITED(pinfo)) {
290
0
                transaction->offset = offset;
291
0
                transaction->length = length;
292
0
            }
293
0
            break;
294
0
        }
295
296
0
        case USB_I1D3_MEASURE1: {
297
0
            uint32_t integration_time;
298
0
            proto_item *integration_time_item = proto_tree_add_item_ret_uint(
299
0
                    tree, hf_usb_i1d3_requested_integration_time, tvb, 1, 4,
300
0
                    ENC_LITTLE_ENDIAN, &integration_time);
301
0
            double integration_time_seconds =
302
0
                integration_time / USB_I1D3_CLOCK_FREQUENCY;
303
0
            proto_item_append_text(
304
0
                    integration_time_item,
305
0
                    " [%.6f seconds]", integration_time_seconds);
306
0
            col_add_fstr(pinfo->cinfo, COL_INFO,
307
0
                    "Measure for %.6fs", integration_time_seconds);
308
0
            break;
309
0
        }
310
0
        case USB_I1D3_MEASURE2: {
311
0
            proto_item *edge_count_item = proto_tree_add_item(
312
0
                    tree, hf_usb_i1d3_requested_edge_count, tvb, 1, 6, ENC_NA);
313
0
            proto_tree *edge_count_tree = proto_item_add_subtree(
314
0
                    edge_count_item, ett_usb_i1d3_requested_edge_count);
315
0
            uint32_t edge_count_red, edge_count_green, edge_count_blue;
316
0
            proto_tree_add_item_ret_uint(
317
0
                    edge_count_tree, hf_usb_i1d3_requested_edge_count_red, tvb,
318
0
                    1, 2, ENC_LITTLE_ENDIAN, &edge_count_red);
319
0
            proto_tree_add_item_ret_uint(
320
0
                    edge_count_tree, hf_usb_i1d3_requested_edge_count_green, tvb,
321
0
                    3, 2, ENC_LITTLE_ENDIAN, &edge_count_green);
322
0
            proto_tree_add_item_ret_uint(
323
0
                    edge_count_tree, hf_usb_i1d3_requested_edge_count_blue, tvb,
324
0
                    5, 2, ENC_LITTLE_ENDIAN, &edge_count_blue);
325
0
            proto_item_append_text(
326
0
                    edge_count_item, ": R%u G%u B%u",
327
0
                    edge_count_red, edge_count_green, edge_count_blue);
328
0
            col_add_fstr(pinfo->cinfo, COL_INFO, "Measure R%u G%u B%u edges",
329
0
                    edge_count_red, edge_count_green, edge_count_blue);
330
0
            break;
331
0
        }
332
0
        case USB_I1D3_SETLED: {
333
0
            uint32_t led_mode, led_offtime, led_ontime, pulse_count;
334
0
            proto_tree_add_item_ret_uint(
335
0
                    tree, hf_usb_i1d3_led_mode, tvb, 1, 1, ENC_NA, &led_mode);
336
0
            proto_item *led_offtime_item = proto_tree_add_item_ret_uint(
337
0
                    tree, hf_usb_i1d3_led_offtime, tvb, 2, 1, ENC_NA,
338
0
                    &led_offtime);
339
0
            double led_offtime_seconds =
340
0
                led_offtime / USB_I1D3_LED_OFFTIME_FACTOR;
341
0
            proto_item_append_text(
342
0
                    led_offtime_item, " [%.6f seconds]", led_offtime_seconds);
343
0
            proto_item *led_ontime_item = proto_tree_add_item_ret_uint(
344
0
                    tree, hf_usb_i1d3_led_ontime, tvb, 3, 1, ENC_NA,
345
0
                    &led_ontime);
346
0
            double led_ontime_seconds =
347
0
                led_ontime / ((led_mode == USB_I1D3_LED_BLINK) ?
348
0
                        USB_I1D3_LED_ONTIME_FACTOR :
349
0
                        USB_I1D3_LED_ONTIME_FADE_FACTOR);
350
0
            proto_item_append_text(
351
0
                    led_ontime_item, " [%.6f seconds]", led_ontime_seconds);
352
0
            proto_item *pulse_count_item = proto_tree_add_item_ret_uint(
353
0
                    tree, hf_usb_i1d3_led_pulse_count, tvb, 4, 1, ENC_NA,
354
0
                    &pulse_count);
355
0
            if (pulse_count == 0x80) {
356
0
                proto_item_append_text(pulse_count_item, " [infinity]");
357
0
                col_add_fstr(pinfo->cinfo, COL_INFO,
358
0
                        "Pulse LED off (%.6fs) and on (%.6fs%s) "
359
0
                        "indefinitely", led_offtime_seconds, led_ontime_seconds,
360
0
                        (led_mode == USB_I1D3_LED_BLINK_FADE_ON) ?
361
0
                        " fading" : "");
362
0
            } else {
363
0
                col_add_fstr(pinfo->cinfo, COL_INFO,
364
0
                        "Pulse LED off (%.6fs) and on (%.6fs%s) "
365
0
                        "%u times", led_offtime_seconds, led_ontime_seconds,
366
0
                        (led_mode == USB_I1D3_LED_BLINK_FADE_ON) ?
367
0
                        " fading" : "", pulse_count);
368
0
            }
369
0
        }
370
0
    }
371
0
}
372
373
static void dissect_usb_i1d3_response(
374
        tvbuff_t *tvb, packet_info *pinfo,
375
0
        usb_i1d3_conversation_t *conversation, proto_tree *tree) {
376
    // The response packet does not contain any information about the command
377
    // it is a response to, so we need to reconstruct this information using the
378
    // previous packet that we saw.
379
    //
380
    // Note: currently, for simplicity's sake, this assumes that there is only
381
    // one inflight request at any given time - in other words, that there is no
382
    // pipelining going on. It is not clear if the device would even be able to
383
    // service more than one request at the same time in the first place.
384
0
    usb_i1d3_transaction_t *transaction;
385
0
    if (!PINFO_FD_VISITED(pinfo)) {
386
0
        transaction = (usb_i1d3_transaction_t *)wmem_map_lookup(
387
0
                conversation->request_to_transaction,
388
0
                GUINT_TO_POINTER(conversation->previous_packet));
389
0
        if (transaction) {
390
0
            DISSECTOR_ASSERT(transaction->response == 0);
391
0
            transaction->response = pinfo->num;
392
0
            wmem_map_insert(
393
0
                    conversation->response_to_transaction,
394
0
                    GUINT_TO_POINTER(transaction->response),
395
0
                    (void *)transaction);
396
0
        }
397
0
    } else {
398
        // After the first pass, we can't use previous_packet anymore since
399
        // there is no guarantee the dissector is called in order, so we use
400
        // the reverse mapping that we populated above.
401
0
        transaction = (usb_i1d3_transaction_t *)wmem_map_lookup(
402
0
                conversation->response_to_transaction,
403
0
                GUINT_TO_POINTER(pinfo->num));
404
0
    }
405
0
    if (transaction) {
406
0
        DISSECTOR_ASSERT(transaction->response == pinfo->num);
407
0
        DISSECTOR_ASSERT(transaction->request != 0);
408
0
    }
409
410
0
    proto_item *request_item = proto_tree_add_uint(
411
0
            tree, hf_usb_i1d3_request_in, tvb, 0, 0,
412
0
            transaction ? transaction->request : 0);
413
0
    proto_item_set_generated(request_item);
414
0
    if (!transaction) {
415
0
        expert_add_info(pinfo, request_item, &ei_usb_i1d3_unexpected_response);
416
0
    } else {
417
0
        proto_item *command_code_item = proto_tree_add_uint(
418
0
                tree, hf_usb_i1d3_command_code, tvb, 0, 0,
419
0
                transaction->command_code);
420
0
        proto_item_set_generated(command_code_item);
421
0
    }
422
423
0
    const char *command_string = transaction ? try_val_to_str(
424
0
            transaction->command_code, usb_i1d3_command_code_strings) : NULL;
425
0
    if (!command_string) command_string = "unknown";
426
427
0
    uint32_t response_code;
428
0
    proto_item *response_code_item = proto_tree_add_item_ret_uint(
429
0
            tree, hf_usb_i1d3_response_code, tvb, 0, 1, ENC_NA, &response_code);
430
0
    proto_item_append_text(
431
0
            response_code_item, " (%s)", (response_code == 0) ? "OK" : "error");
432
0
    if (response_code != 0) {
433
0
        col_add_fstr(
434
0
                pinfo->cinfo, COL_INFO, "Error code %u (%s)",
435
0
                response_code, command_string);
436
0
        expert_add_info(pinfo, response_code_item, &ei_usb_i1d3_error);
437
0
        return;
438
0
    }
439
440
0
    col_add_fstr(pinfo->cinfo, COL_INFO, "OK (%s)", command_string);
441
442
0
    if (!transaction) return;
443
444
    // As mentioned in ArgyllCMS spectro/i1d3.c, the second byte is usually the
445
    // first byte of the command code, except for GET_DIFF.
446
0
    if (transaction->command_code != USB_I1D3_GET_DIFF) {
447
0
        uint32_t echoed_command_code;
448
0
        proto_item *echoed_command_code_item = proto_tree_add_item_ret_uint(
449
0
                tree, hf_usb_i1d3_echoed_command_code, tvb, 1, 1, ENC_NA,
450
0
                &echoed_command_code);
451
0
        uint8_t expected_command_code = transaction->command_code >> 8;
452
0
        proto_item_append_text(
453
0
                echoed_command_code_item, " [expected 0x%02x]",
454
0
                expected_command_code);
455
0
        if (echoed_command_code != expected_command_code) {
456
0
            expert_add_info(
457
0
                    pinfo, echoed_command_code_item,
458
0
                    &ei_usb_i1d3_echoed_command_code_mismatch);
459
0
        }
460
0
    }
461
462
0
    switch (transaction->command_code) {
463
0
        case USB_I1D3_GET_INFO: {
464
0
            const uint8_t *information;
465
0
            proto_tree_add_item_ret_string(
466
0
                    tree, hf_usb_i1d3_information, tvb, 2, -1,
467
0
                    ENC_ASCII | ENC_NA, pinfo->pool, &information);
468
0
            col_add_fstr(
469
0
                    pinfo->cinfo, COL_INFO, "Information: %s", information);
470
0
            break;
471
0
        }
472
0
        case USB_I1D3_STATUS: {
473
0
            uint32_t status;
474
0
            proto_item *status_item = proto_tree_add_item_ret_uint(
475
0
                    tree, hf_usb_i1d3_status, tvb, 2, 3, ENC_BIG_ENDIAN,
476
0
                    &status);
477
0
            const char *status_string =
478
0
                ((status & 0xff00ff) != 0 || (status & 0x00ff00) >= 5) ?
479
0
                "OK" : "Bad";
480
0
            proto_item_append_text(status_item, " [%s]", status_string);
481
0
            col_add_fstr(
482
0
                     pinfo->cinfo, COL_INFO, "Status: 0x%06x (%s)",
483
0
                     status, status_string);
484
0
            break;
485
0
        }
486
0
        case USB_I1D3_PRODNAME: {
487
0
            const uint8_t *prodname;
488
0
            proto_tree_add_item_ret_string(
489
0
                    tree, hf_usb_i1d3_prodname, tvb, 2, -1,
490
0
                    ENC_ASCII | ENC_NA, pinfo->pool, &prodname);
491
0
            col_add_fstr(pinfo->cinfo, COL_INFO, "Product name: %s", prodname);
492
0
            break;
493
0
        }
494
0
        case USB_I1D3_PRODTYPE: {
495
0
            uint32_t prodtype;
496
0
            proto_tree_add_item_ret_uint(
497
0
                    tree, hf_usb_i1d3_prodtype, tvb, 3, 2, ENC_BIG_ENDIAN,
498
0
                    &prodtype);
499
0
            col_add_fstr(
500
0
                    pinfo->cinfo, COL_INFO, "Product type: 0x%04x",
501
0
                    prodtype);
502
0
            break;
503
0
        }
504
0
        case USB_I1D3_FIRMVER: {
505
0
            const uint8_t *firmver;
506
0
            proto_tree_add_item_ret_string(
507
0
                    tree, hf_usb_i1d3_firmver, tvb, 2, -1,
508
0
                    ENC_ASCII | ENC_NA, pinfo->pool, &firmver);
509
0
            col_add_fstr(
510
0
                    pinfo->cinfo, COL_INFO, "Firmware version: %s", firmver);
511
0
            break;
512
0
        }
513
0
        case USB_I1D3_FIRMDATE: {
514
0
            const uint8_t *firmdate;
515
0
            proto_tree_add_item_ret_string(
516
0
                    tree, hf_usb_i1d3_firmdate, tvb, 2, -1,
517
0
                    ENC_ASCII | ENC_NA, pinfo->pool, &firmdate);
518
0
            col_add_fstr(pinfo->cinfo, COL_INFO, "Firmware date: %s", firmdate);
519
0
            break;
520
0
        }
521
0
        case USB_I1D3_LOCKED: {
522
0
            uint32_t locked;
523
0
            proto_item *locked_item = proto_tree_add_item_ret_uint(
524
0
                    tree, hf_usb_i1d3_locked, tvb, 2, 2, ENC_BIG_ENDIAN,
525
0
                    &locked);
526
0
            const char *locked_string =
527
0
                ((locked & 0xff00) != 0 || (locked & 0x00ff) == 0) ?
528
0
                "Unlocked" : "Locked";
529
0
            proto_item_append_text(locked_item, " [%s]", locked_string);
530
0
            col_add_fstr(
531
0
                     pinfo->cinfo, COL_INFO, "Locked status: 0x%04x (%s)",
532
0
                     locked, locked_string);
533
0
            break;
534
0
        }
535
0
        case USB_I1D3_MEASURE1: {
536
0
            proto_item *edge_count_item = proto_tree_add_item(
537
0
                    tree, hf_usb_i1d3_measured_edge_count, tvb, 2, 12, ENC_NA);
538
0
            proto_tree *edge_count_tree = proto_item_add_subtree(
539
0
                    edge_count_item, ett_usb_i1d3_requested_edge_count);
540
0
            uint32_t edge_count_red, edge_count_green, edge_count_blue;
541
0
            proto_tree_add_item_ret_uint(
542
0
                    edge_count_tree, hf_usb_i1d3_measured_edge_count_red, tvb,
543
0
                    2, 4, ENC_LITTLE_ENDIAN, &edge_count_red);
544
0
            proto_tree_add_item_ret_uint(
545
0
                    edge_count_tree, hf_usb_i1d3_measured_edge_count_green, tvb,
546
0
                    6, 4, ENC_LITTLE_ENDIAN, &edge_count_green);
547
0
            proto_tree_add_item_ret_uint(
548
0
                    edge_count_tree, hf_usb_i1d3_measured_edge_count_blue, tvb,
549
0
                    10, 4, ENC_LITTLE_ENDIAN, &edge_count_blue);
550
0
            proto_item_append_text(
551
0
                    edge_count_item, ": R%u G%u B%u",
552
0
                    edge_count_red, edge_count_green, edge_count_blue);
553
0
            col_add_fstr(pinfo->cinfo, COL_INFO, "Measured R%u G%u B%u edges",
554
0
                    edge_count_red, edge_count_green, edge_count_blue);
555
0
            break;
556
0
        }
557
0
        case USB_I1D3_MEASURE2: {
558
0
            proto_item *duration_item = proto_tree_add_item(
559
0
                    tree, hf_usb_i1d3_measured_duration, tvb, 2, 12, ENC_NA);
560
0
            proto_tree *duration_tree = proto_item_add_subtree(
561
0
                    duration_item, ett_usb_i1d3_measured_duration);
562
0
            uint32_t duration_red, duration_green, duration_blue;
563
0
            proto_item *duration_red_item = proto_tree_add_item_ret_uint(
564
0
                    duration_tree, hf_usb_i1d3_measured_duration_red,
565
0
                    tvb, 2, 4, ENC_LITTLE_ENDIAN, &duration_red);
566
0
            double duration_red_seconds =
567
0
                duration_red / USB_I1D3_CLOCK_FREQUENCY;
568
0
            proto_item_append_text(
569
0
                    duration_red_item,
570
0
                    " [%.6f seconds]", duration_red_seconds);
571
0
            proto_item *duration_green_item = proto_tree_add_item_ret_uint(
572
0
                    duration_tree, hf_usb_i1d3_measured_duration_green,
573
0
                    tvb, 6, 4, ENC_LITTLE_ENDIAN, &duration_green);
574
0
            double duration_green_seconds =
575
0
                duration_green / USB_I1D3_CLOCK_FREQUENCY;
576
0
            proto_item_append_text(
577
0
                    duration_green_item,
578
0
                    " [%.6f seconds]", duration_green_seconds);
579
0
            proto_item *duration_blue_item = proto_tree_add_item_ret_uint(
580
0
                    duration_tree, hf_usb_i1d3_measured_duration_blue,
581
0
                    tvb, 10, 4, ENC_LITTLE_ENDIAN, &duration_blue);
582
0
            double duration_blue_seconds =
583
0
                duration_blue / USB_I1D3_CLOCK_FREQUENCY;
584
0
            proto_item_append_text(
585
0
                    duration_blue_item,
586
0
                    " [%.6f seconds]", duration_blue_seconds);
587
0
            proto_item_append_text(
588
0
                    duration_item, ": R%.6fs G%.6fs B%.6fs",
589
0
                    duration_red_seconds, duration_green_seconds,
590
0
                    duration_blue_seconds);
591
0
            col_add_fstr(pinfo->cinfo, COL_INFO,
592
0
                    "Measured R%.6fs G%.6fs B%.6fs",
593
0
                    duration_red_seconds, duration_green_seconds,
594
0
                    duration_blue_seconds);
595
0
            break;
596
0
        }
597
0
        case USB_I1D3_READINTEE: {
598
0
            proto_item *offset_item = proto_tree_add_uint(
599
0
                    tree, hf_usb_i1d3_readintee_offset, tvb, 0, 0,
600
0
                    transaction->offset);
601
0
            proto_item_set_generated(offset_item);
602
0
            proto_item *length_item = proto_tree_add_uint(
603
0
                    tree, hf_usb_i1d3_readintee_length, tvb, 0, 0,
604
0
                    transaction->length);
605
0
            proto_item_set_generated(length_item);
606
0
            proto_tree_add_item(
607
0
                tree, hf_usb_i1d3_readintee_data, tvb,
608
0
                4, transaction->length, ENC_NA);
609
0
            col_add_fstr(
610
0
                pinfo->cinfo, COL_INFO,
611
0
                "Internal EEPROM data (offset: %u, length: %u)",
612
0
                transaction->offset, transaction->length);
613
0
            break;
614
0
        }
615
0
        case USB_I1D3_READEXTEE: {
616
0
            proto_item *offset_item = proto_tree_add_uint(
617
0
                    tree, hf_usb_i1d3_readextee_offset, tvb, 0, 0,
618
0
                    transaction->offset);
619
0
            proto_item_set_generated(offset_item);
620
0
            proto_item *length_item = proto_tree_add_uint(
621
0
                    tree, hf_usb_i1d3_readextee_length, tvb, 0, 0,
622
0
                    transaction->length);
623
0
            proto_item_set_generated(length_item);
624
0
            proto_tree_add_item(
625
0
                tree, hf_usb_i1d3_readextee_data, tvb,
626
0
                5, transaction->length, ENC_NA);
627
0
            col_add_fstr(
628
0
                pinfo->cinfo, COL_INFO,
629
0
                "External EEPROM data (offset: %u, length: %u)",
630
0
                transaction->offset, transaction->length);
631
0
            break;
632
0
        }
633
0
        case USB_I1D3_GET_DIFF: {
634
0
            uint32_t diffuser_position;
635
0
            proto_item *diffuser_position_item = proto_tree_add_item_ret_uint(
636
0
                    tree, hf_usb_i1d3_diffuser_position, tvb,
637
0
                    1, 1, ENC_NA, &diffuser_position);
638
0
            const char *diffuser_position_string = try_val_to_str(
639
0
                    diffuser_position, usb_i1d3_diffuser_position_strings);
640
0
            if (!diffuser_position_string) {
641
0
                expert_add_info(
642
0
                        pinfo, diffuser_position_item,
643
0
                        &ei_usb_i1d3_unknown_diffuser_position);
644
0
            }
645
0
            col_add_fstr(
646
0
                pinfo->cinfo, COL_INFO, "Diffuser position: %s",
647
0
                diffuser_position_string ?
648
0
                diffuser_position_string : "unknown");
649
0
            break;
650
0
        }
651
0
        case USB_I1D3_LOCKCHAL: {
652
0
            proto_tree_add_item(
653
0
                    tree, hf_usb_i1d3_challenge_encode_key, tvb, 2, 1, ENC_NA);
654
0
            proto_tree_add_item(
655
0
                    tree, hf_usb_i1d3_challenge_decode_key, tvb, 3, 1, ENC_NA);
656
0
            proto_tree_add_item(
657
0
                    tree, hf_usb_i1d3_challenge_data, tvb, 35, 8, ENC_NA);
658
0
            break;
659
0
        }
660
0
        case USB_I1D3_LOCKRESP: {
661
0
            uint32_t unlock_result;
662
0
            proto_item *unlock_result_item = proto_tree_add_item_ret_uint(
663
0
                    tree, hf_usb_i1d3_unlock_result, tvb, 2, 1, ENC_NA,
664
0
                    &unlock_result);
665
0
            int unlock_successful = unlock_result == 0x77;
666
0
            const char *unlock_result_string = unlock_successful ?
667
0
                "Successfully unlocked" : "Failed to unlock";
668
0
            proto_item_append_text(
669
0
                    unlock_result_item, " [%s]", unlock_result_string);
670
0
            if (!unlock_successful) {
671
0
                expert_add_info(
672
0
                        pinfo, unlock_result_item, &ei_usb_i1d3_unlock_failed);
673
0
            }
674
0
            col_add_str(pinfo->cinfo, COL_INFO, unlock_result_string);
675
0
            break;
676
0
        }
677
0
    }
678
0
}
679
680
static int dissect_usb_i1d3(
681
        tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
682
        void *data _U_)
683
0
{
684
0
    if ((pinfo->p2p_dir == P2P_DIR_SENT && pinfo->destport == 0) ||
685
0
        (pinfo->p2p_dir == P2P_DIR_RECV && pinfo->srcport == 0)) {
686
        // The device describes itself as HID class, even though the actual
687
        // protocol doesn't seem to be based on HID at all. However that means
688
        // the device will receive (and respond) to some basic HID requests,
689
        // such as GET_DESCRIPTOR. These HID requests will go to endpoint 0,
690
        // while actual communication takes place on endpoint 1. Therefore, if
691
        // we get handed a packet going to/from endpoint 0, reject it and let
692
        // the HID dissector handle it.
693
0
        return 0;
694
0
    }
695
696
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "i1d3");
697
698
0
    proto_item *usb_i1d3_item = proto_tree_add_item(
699
0
            tree, proto_usb_i1d3, tvb, 0, -1, ENC_NA);
700
0
    proto_tree *usb_i1d3_tree = proto_item_add_subtree(
701
0
            usb_i1d3_item, ett_usb_i1d3);
702
703
    // All i1d3 packets seen in the while are fixed-length, with padding added
704
    // as necessary. It is not clear if using a different length is valid or
705
    // not.
706
0
    if (tvb_reported_length(tvb) != USB_I1D3_PACKET_LENGTH) {
707
0
        expert_add_info(pinfo, usb_i1d3_item, &ei_usb_i1d3_unusual_length);
708
0
    }
709
710
0
    col_clear(pinfo->cinfo, COL_INFO);
711
0
    usb_i1d3_conversation_t *conversation = usb_i1d3_get_conversation(pinfo);
712
0
    if (pinfo->p2p_dir == P2P_DIR_SENT) {
713
0
        dissect_usb_i1d3_command(tvb, pinfo, conversation, usb_i1d3_tree);
714
0
    } else if (pinfo->p2p_dir == P2P_DIR_RECV) {
715
0
        dissect_usb_i1d3_response(tvb, pinfo, conversation, usb_i1d3_tree);
716
0
    } else {
717
0
        DISSECTOR_ASSERT(0);
718
0
    }
719
0
    conversation->previous_packet = pinfo->num;
720
721
0
    return tvb_captured_length(tvb);
722
0
}
723
724
void proto_register_usb_i1d3(void)
725
14
{
726
14
    proto_usb_i1d3 = proto_register_protocol("X-Rite i1 Display Pro (and derivatives) USB protocol", "X-Rite i1 Display Pro", "i1d3");
727
728
14
    static int *ett[] = {
729
14
        &ett_usb_i1d3,
730
14
        &ett_usb_i1d3_measured_duration,
731
14
        &ett_usb_i1d3_requested_edge_count,
732
14
    };
733
14
    static hf_register_info hf[] = {
734
14
        { &hf_usb_i1d3_challenge_response,
735
14
            { "Challenge response", "i1d3.challenge_response",
736
14
                FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL },
737
14
        },
738
14
        { &hf_usb_i1d3_challenge_data,
739
14
            { "Challenge data", "i1d3.challenge_data",
740
14
                FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL },
741
14
        },
742
14
        { &hf_usb_i1d3_challenge_decode_key,
743
14
            { "Challenge decode XOR value", "i1d3.challenge_decode_key",
744
14
                FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL },
745
14
        },
746
14
        { &hf_usb_i1d3_challenge_encode_key,
747
14
            { "Challenge encode XOR value", "i1d3.challenge_encode_key",
748
14
                FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL },
749
14
        },
750
14
        { &hf_usb_i1d3_command_code,
751
14
            { "Command code", "i1d3.command.code", FT_UINT16, BASE_HEX,
752
14
                VALS(usb_i1d3_command_code_strings), 0, NULL, HFILL },
753
14
        },
754
14
        { &hf_usb_i1d3_diffuser_position,
755
14
            { "Diffuser position", "i1d3.diffuser_position", FT_UINT8, BASE_DEC,
756
14
                VALS(usb_i1d3_diffuser_position_strings), 0, NULL, HFILL },
757
14
        },
758
14
        { &hf_usb_i1d3_echoed_command_code,
759
14
            { "Echoed command code", "i1d3.echoed_command.code", FT_UINT8,
760
14
                BASE_HEX, NULL, 0, NULL, HFILL },
761
14
        },
762
14
        { &hf_usb_i1d3_firmdate,
763
14
            { "Firmware date", "i1d3.firmdate", FT_STRINGZ, BASE_NONE,
764
14
                NULL, 0, NULL, HFILL },
765
14
        },
766
14
        { &hf_usb_i1d3_firmver,
767
14
            { "Firmware version", "i1d3.firmver", FT_STRINGZ, BASE_NONE,
768
14
                NULL, 0, NULL, HFILL },
769
14
        },
770
14
        { &hf_usb_i1d3_information,
771
14
            { "Information", "i1d3.information", FT_STRINGZ, BASE_NONE,
772
14
                NULL, 0, NULL, HFILL },
773
14
        },
774
14
        { &hf_usb_i1d3_measured_duration,
775
14
            { "Measured duration", "i1d3.measured_duration",
776
14
                FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL },
777
14
        },
778
14
        { &hf_usb_i1d3_measured_duration_red,
779
14
            { "Red channel",
780
14
                "i1d3.measured_duration.red", FT_UINT32,
781
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_cycle_cycles),
782
14
                0, NULL, HFILL },
783
14
        },
784
14
        { &hf_usb_i1d3_measured_duration_green,
785
14
            { "Green channel",
786
14
                "i1d3.measured_duration.green", FT_UINT32,
787
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_cycle_cycles),
788
14
                0, NULL, HFILL },
789
14
        },
790
14
        { &hf_usb_i1d3_measured_duration_blue,
791
14
            { "Blue channel",
792
14
                "i1d3.measured_duration.blue", FT_UINT32,
793
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_cycle_cycles),
794
14
                0, NULL, HFILL },
795
14
        },
796
14
        { &hf_usb_i1d3_measured_edge_count,
797
14
            { "Measured edge count", "i1d3.measured_edge_count",
798
14
                FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL },
799
14
        },
800
14
        { &hf_usb_i1d3_measured_edge_count_red,
801
14
            { "Red channel",
802
14
                "i1d3.measured_edge_count.red", FT_UINT32,
803
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_edge_edges),
804
14
                0, NULL, HFILL },
805
14
        },
806
14
        { &hf_usb_i1d3_measured_edge_count_green,
807
14
            { "Green channel",
808
14
                "i1d3.measured_edge_count.green", FT_UINT32,
809
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_edge_edges),
810
14
                0, NULL, HFILL },
811
14
        },
812
14
        { &hf_usb_i1d3_measured_edge_count_blue,
813
14
            { "Blue channel",
814
14
                "i1d3.measured_edge_count.blue", FT_UINT32,
815
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_edge_edges),
816
14
                0, NULL, HFILL },
817
14
        },
818
14
        { &hf_usb_i1d3_led_mode,
819
14
            { "LED mode", "i1d3.led_mode", FT_UINT8, BASE_DEC,
820
14
                VALS(usb_i1d3_led_mode_strings), 0, NULL, HFILL },
821
14
        },
822
14
        { &hf_usb_i1d3_led_offtime,
823
14
            { "LED off time", "i1d3.led_offtime", FT_UINT8, BASE_DEC,
824
14
                NULL, 0, NULL, HFILL },
825
14
        },
826
14
        { &hf_usb_i1d3_led_ontime,
827
14
            { "LED on time", "i1d3.led_ontime", FT_UINT8, BASE_DEC,
828
14
                NULL, 0, NULL, HFILL },
829
14
        },
830
14
        { &hf_usb_i1d3_led_pulse_count,
831
14
            { "LED pulse count", "i1d3.led_pulse_count", FT_UINT8,
832
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_pulse_pulses),
833
14
                0, NULL, HFILL },
834
14
        },
835
14
        { &hf_usb_i1d3_locked,
836
14
            { "Lock status", "i1d3.locked",
837
14
                FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL },
838
14
        },
839
14
        { &hf_usb_i1d3_prodname,
840
14
            { "Product name", "i1d3.prodname", FT_STRINGZ, BASE_NONE,
841
14
                NULL, 0, NULL, HFILL },
842
14
        },
843
14
        { &hf_usb_i1d3_prodtype,
844
14
            { "Product type", "i1d3.prodtype", FT_UINT16, BASE_HEX,
845
14
                NULL, 0, NULL, HFILL },
846
14
        },
847
14
        { &hf_usb_i1d3_request_in,
848
14
            { "Request in frame", "i1d3.request_in",
849
14
                FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST),
850
14
                0, NULL, HFILL }
851
14
        },
852
14
        { &hf_usb_i1d3_requested_edge_count,
853
14
            { "Requested edge count", "i1d3.requested_edge_count",
854
14
                FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL },
855
14
        },
856
14
        { &hf_usb_i1d3_requested_edge_count_red,
857
14
            { "Red channel",
858
14
                "i1d3.requested_edge_count.red", FT_UINT16,
859
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_edge_edges),
860
14
                0, NULL, HFILL },
861
14
        },
862
14
        { &hf_usb_i1d3_requested_edge_count_green,
863
14
            { "Green channel",
864
14
                "i1d3.requested_edge_count.green", FT_UINT16,
865
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_edge_edges),
866
14
                0, NULL, HFILL },
867
14
        },
868
14
        { &hf_usb_i1d3_requested_edge_count_blue,
869
14
            { "Blue channel",
870
14
                "i1d3.requested_edge_count.blue", FT_UINT16,
871
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_edge_edges),
872
14
                0, NULL, HFILL },
873
14
        },
874
14
        { &hf_usb_i1d3_requested_integration_time,
875
14
            { "Requested integration time",
876
14
                "i1d3.requested_integration_time", FT_UINT32,
877
14
                BASE_DEC|BASE_UNIT_STRING, UNS(&units_cycle_cycles),
878
14
                0, NULL, HFILL },
879
14
        },
880
14
        { &hf_usb_i1d3_response_code,
881
14
            { "Response code",
882
14
                "i1d3.response_code", FT_UINT8, BASE_HEX,
883
14
                NULL, 0, NULL, HFILL },
884
14
        },
885
14
        { &hf_usb_i1d3_response_in,
886
14
            { "Response in frame", "i1d3.response_in",
887
14
                FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE),
888
14
                0, NULL, HFILL }
889
14
        },
890
14
        { &hf_usb_i1d3_readintee_data,
891
14
            { "Internal EEPROM data", "i1d3.readintee_data",
892
14
                FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL },
893
14
        },
894
14
        { &hf_usb_i1d3_readintee_offset,
895
14
            { "Internal EEPROM read offset", "i1d3.readintee_offset",
896
14
                FT_UINT8, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes),
897
14
                0, NULL, HFILL },
898
14
        },
899
14
        { &hf_usb_i1d3_readintee_length,
900
14
            { "Internal EEPROM read length", "i1d3.readintee_length",
901
14
                FT_UINT8, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes),
902
14
                0, NULL, HFILL },
903
14
        },
904
14
        { &hf_usb_i1d3_readextee_data,
905
14
            { "External EEPROM data", "i1d3.readextee_data",
906
14
                FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL },
907
14
        },
908
14
        { &hf_usb_i1d3_readextee_offset,
909
14
            { "External EEPROM read offset", "i1d3.readextee_offset",
910
14
                FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes),
911
14
                0, NULL, HFILL },
912
14
        },
913
14
        { &hf_usb_i1d3_readextee_length,
914
14
            { "External EEPROM read length", "i1d3.readextee_length",
915
14
                FT_UINT8, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes),
916
14
                0, NULL, HFILL },
917
14
        },
918
14
        { &hf_usb_i1d3_status,
919
14
            { "Status", "i1d3.status",
920
14
                FT_UINT24, BASE_HEX, NULL, 0, NULL, HFILL },
921
14
        },
922
14
        { &hf_usb_i1d3_unlock_result,
923
14
            { "Unlock result", "i1d3.unlock_result",
924
14
                FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL },
925
14
        },
926
14
    };
927
14
    static ei_register_info ei[] = {
928
14
        { &ei_usb_i1d3_echoed_command_code_mismatch,
929
14
            { "i1d3.echoed_command_code_mismatch", PI_PROTOCOL, PI_ERROR,
930
14
                "Echoed command code does not match request", EXPFILL }
931
14
        },
932
14
        { &ei_usb_i1d3_error,
933
14
            { "i1d3.error", PI_RESPONSE_CODE, PI_NOTE,
934
14
                "Error response code", EXPFILL }
935
14
        },
936
14
        { &ei_usb_i1d3_unexpected_response,
937
14
            { "i1d3.unexpected_response", PI_SEQUENCE, PI_WARN,
938
14
                "Could not match response to a request", EXPFILL }
939
14
        },
940
14
        { &ei_usb_i1d3_unknown_command,
941
14
            { "i1d3.unknown_command", PI_MALFORMED, PI_ERROR,
942
14
                "Unknown command code", EXPFILL }
943
14
        },
944
14
        { &ei_usb_i1d3_unknown_diffuser_position,
945
14
            { "i1d3.unknown_diffuser_position", PI_MALFORMED, PI_ERROR,
946
14
                "Unknown diffuser position code", EXPFILL }
947
14
        },
948
14
        { &ei_usb_i1d3_unlock_failed,
949
14
            { "i1d3.unlock_failed", PI_RESPONSE_CODE, PI_NOTE,
950
14
                "Failed to unlock device", EXPFILL }
951
14
        },
952
14
        { &ei_usb_i1d3_unusual_length,
953
14
            { "i1d3.unusual_length", PI_PROTOCOL, PI_WARN,
954
14
                "Packet has unusual length", EXPFILL }
955
14
        },
956
14
    };
957
958
14
    proto_register_subtree_array(ett, array_length(ett));
959
14
    proto_register_field_array(proto_usb_i1d3, hf, array_length(hf));
960
14
    expert_module_t *expert_usb_i1d3 = expert_register_protocol(
961
14
            proto_usb_i1d3);
962
14
    expert_register_field_array(expert_usb_i1d3, ei, array_length(ei));
963
14
    usb_i1d3_dissector = register_dissector("i1d3",
964
14
            dissect_usb_i1d3, proto_usb_i1d3);
965
14
}
966
967
14
void proto_reg_handoff_usb_i1d3(void) {
968
14
    dissector_add_for_decode_as("usb.device", usb_i1d3_dissector);
969
14
    dissector_add_uint("usb.product", 0x7655020, usb_i1d3_dissector);
970
14
}
971
972
/*
973
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
974
 *
975
 * Local variables:
976
 * c-basic-offset: 4
977
 * tab-width: 8
978
 * indent-tabs-mode: nil
979
 * End:
980
 *
981
 * vi: set shiftwidth=4 tabstop=8 expandtab:
982
 * :indentSize=4:tabSize=8:noTabs=true:
983
 */