Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/epan/dissectors/packet-ftdi-mpsse.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-ftdi-mpsse.c
2
 * Routines for FTDI Multi-Protocol Synchronous Serial Engine dissection
3
 *
4
 * Copyright 2020 Tomasz Mon
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998 Gerald Combs
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
#include "config.h"
14
15
#include <epan/packet.h>
16
#include <epan/expert.h>
17
#include <wsutil/str_util.h>
18
#include "packet-ftdi-ft.h"
19
20
static int proto_ftdi_mpsse;
21
22
static int hf_mpsse_command;
23
/* Data Shifting commands bits (b0-b6 are relevant only if b7 is 0) */
24
static int hf_mpsse_command_b0;
25
static int hf_mpsse_command_b1;
26
static int hf_mpsse_command_b2;
27
static int hf_mpsse_command_b3;
28
static int hf_mpsse_command_b4;
29
static int hf_mpsse_command_b5;
30
static int hf_mpsse_command_b6;
31
static int hf_mpsse_command_b7;
32
static int hf_mpsse_command_with_parameters;
33
static int hf_mpsse_bad_command_error;
34
static int hf_mpsse_bad_command_code;
35
static int hf_mpsse_response;
36
static int hf_mpsse_command_in;
37
static int hf_mpsse_response_in;
38
static int hf_mpsse_length_uint8;
39
static int hf_mpsse_length_uint16;
40
static int hf_mpsse_bytes_out;
41
static int hf_mpsse_bytes_in;
42
static int hf_mpsse_bits_out;
43
static int hf_mpsse_bits_in;
44
static int hf_mpsse_value;
45
static int hf_mpsse_value_b0;
46
static int hf_mpsse_value_b1;
47
static int hf_mpsse_value_b2;
48
static int hf_mpsse_value_b3;
49
static int hf_mpsse_value_b4;
50
static int hf_mpsse_value_b5;
51
static int hf_mpsse_value_b6;
52
static int hf_mpsse_value_b7;
53
static int hf_mpsse_direction;
54
static int hf_mpsse_direction_b0;
55
static int hf_mpsse_direction_b1;
56
static int hf_mpsse_direction_b2;
57
static int hf_mpsse_direction_b3;
58
static int hf_mpsse_direction_b4;
59
static int hf_mpsse_direction_b5;
60
static int hf_mpsse_direction_b6;
61
static int hf_mpsse_direction_b7;
62
static int hf_mpsse_cpumode_address_short;
63
static int hf_mpsse_cpumode_address_extended;
64
static int hf_mpsse_cpumode_data;
65
static int hf_mpsse_clk_divisor;
66
static int hf_mpsse_open_drain_enable_low;
67
static int hf_mpsse_open_drain_enable_low_b0;
68
static int hf_mpsse_open_drain_enable_low_b1;
69
static int hf_mpsse_open_drain_enable_low_b2;
70
static int hf_mpsse_open_drain_enable_low_b3;
71
static int hf_mpsse_open_drain_enable_low_b4;
72
static int hf_mpsse_open_drain_enable_low_b5;
73
static int hf_mpsse_open_drain_enable_low_b6;
74
static int hf_mpsse_open_drain_enable_low_b7;
75
static int hf_mpsse_open_drain_enable_high;
76
static int hf_mpsse_open_drain_enable_high_b0;
77
static int hf_mpsse_open_drain_enable_high_b1;
78
static int hf_mpsse_open_drain_enable_high_b2;
79
static int hf_mpsse_open_drain_enable_high_b3;
80
static int hf_mpsse_open_drain_enable_high_b4;
81
static int hf_mpsse_open_drain_enable_high_b5;
82
static int hf_mpsse_open_drain_enable_high_b6;
83
static int hf_mpsse_open_drain_enable_high_b7;
84
85
static int ett_ftdi_mpsse;
86
static int ett_mpsse_command;
87
static int ett_mpsse_command_with_parameters;
88
static int ett_mpsse_response_data;
89
static int ett_mpsse_value;
90
static int ett_mpsse_direction;
91
static int ett_mpsse_open_drain_enable;
92
static int ett_mpsse_skipped_response_data;
93
94
static expert_field ei_undecoded;
95
static expert_field ei_response_without_command;
96
static expert_field ei_skipped_response_data;
97
static expert_field ei_reassembly_unavailable;
98
99
static dissector_handle_t ftdi_mpsse_handle;
100
101
/* Commands expecting response add command_data_t entry to a list. The list is created when first command
102
 * appears on MPSSE instance TX or when previously created list has matched responses to all entries.
103
 * When a new list is created, head pointer is inserted into both tx_command_info and rx_command_info tree.
104
 *
105
 * When RX packet is dissected, it obtains the pointer to a list (if there isn't any then the capture is
106
 * incomplete/malformed and ei_response_without_command is presented to the user). The RX dissection code
107
 * matches commands with responses and updates the response_in_packet and is_response_set flag. When next
108
 * RX packet is being dissected, it skips all the command_data_t entries that have is_response_set flag set.
109
 * To reduce the effective list length that needs to be traversed, a pointer to the first element that does
110
 * not have is_response_set flag set, is added to rx_command_info with the current packet number in the key.
111
 *
112
 * After first pass, RX packets always obtain relevant command_data_t entry without traversing the list.
113
 * If there wasn't a separate tree TX packets (tx_command_info), TX packet dissection would have to
114
 * traverse the list from the pointer obtained from rx_command_info. In normal conditions the number of
115
 * entries to skip in such case is low. However, when the capture file has either:
116
 *   * A lot of TX packets with commands expecting response but no RX packets, or
117
 *   * Bad Command in TX packet that does not have matching Bad Command response in RX data
118
 * then the traversal time in TX packet dissection becomes significant. To bring performance to acceptable
119
 * levels, tx_command_info tree is being used. It contains pointers to the same list as rx_command_info but
120
 * allows TX dissection to obtain the relevant command_data_t entry without traversing the list.
121
 */
122
static wmem_tree_t *rx_command_info;
123
static wmem_tree_t *tx_command_info;
124
125
typedef struct _command_data command_data_t;
126
127
struct _command_data {
128
    ftdi_mpsse_info_t  mpsse_info;
129
130
    /* true if complete command parameters were not dissected yet */
131
    bool               preliminary;
132
    /* true if response_in_packet has been set (response packet is known) */
133
    bool               is_response_set;
134
    uint8_t            cmd;
135
    int32_t            response_length;
136
    uint32_t           command_in_packet;
137
    uint32_t           response_in_packet;
138
139
    command_data_t    *next;
140
};
141
142
void proto_register_ftdi_mpsse(void);
143
144
0
#define BAD_COMMAND_SYNC_CODE                      0xFA
145
146
0
#define CMD_SET_DATA_BITS_LOW_BYTE                 0x80
147
0
#define CMD_READ_DATA_BITS_LOW_BYTE                0x81
148
0
#define CMD_SET_DATA_BITS_HIGH_BYTE                0x82
149
0
#define CMD_READ_DATA_BITS_HIGH_BYTE               0x83
150
0
#define CMD_CLOCK_SET_DIVISOR                      0x86
151
0
#define CMD_CLOCK_N_BITS                           0x8E
152
0
#define CMD_CLOCK_N_TIMES_8_BITS                   0x8F
153
0
#define CMD_CPUMODE_READ_SHORT_ADDR                0x90
154
0
#define CMD_CPUMODE_READ_EXT_ADDR                  0x91
155
0
#define CMD_CPUMODE_WRITE_SHORT_ADDR               0x92
156
0
#define CMD_CPUMODE_WRITE_EXT_ADDR                 0x93
157
0
#define CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_HIGH  0x9C
158
0
#define CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_LOW   0x9D
159
0
#define CMD_IO_OPEN_DRAIN_ENABLE                   0x9E
160
161
static const value_string command_vals[] = {
162
    {0x10, "Clock Data Bytes Out on + ve clock edge MSB first(no read) [Use if CLK starts at '1']"},
163
    {0x11, "Clock Data Bytes Out on -ve clock edge MSB first (no read) [Use if CLK starts at '0']"},
164
    {0x12, "Clock Data Bits Out on +ve clock edge MSB first (no read) [Use if CLK starts at '1']"},
165
    {0x13, "Clock Data Bits Out on -ve clock edge MSB first (no read) [Use if CLK starts at '0']"},
166
    {0x18, "Clock Data Bytes Out on +ve clock edge LSB first (no read) [Use if CLK starts at '1']"},
167
    {0x19, "Clock Data Bytes Out on -ve clock edge LSB first (no read) [Use if CLK starts at '0']"},
168
    {0x1A, "Clock Data Bits Out on +ve clock edge LSB first (no read) [Use if CLK starts at '1']"},
169
    {0x1B, "Clock Data Bits Out on -ve clock edge LSB first (no read) [Use if CLK starts at '0']"},
170
    {0x20, "Clock Data Bytes In on +ve clock edge MSB first (no write)"},
171
    {0x22, "Clock Data Bits In on +ve clock edge MSB first (no write) [TDO/DI sampled just prior to rising edge]"},
172
    {0x24, "Clock Data Bytes In on -ve clock edge MSB first (no write)"},
173
    {0x26, "Clock Data Bits In on -ve clock edge MSB first (no write) [TDO/DI sampled just prior to falling edge]"},
174
    {0x28, "Clock Data Bytes In on +ve clock edge LSB first (no write)"},
175
    {0x29, "Clock Data Bytes In on +ve clock edge LSB first (no write) [undocumented alternative]"},
176
    {0x2A, "Clock Data Bits In on +ve clock edge LSB first (no write) [TDO/DI sampled just prior to rising edge]"},
177
    {0x2B, "Clock Data Bits In on +ve clock edge LSB first (no write) [TDO/DI sampled just prior to rising edge] [undocumented alternative]"},
178
    {0x2C, "Clock Data Bytes In on -ve clock edge LSB first (no write)"},
179
    {0x2E, "Clock Data Bits In on -ve clock edge LSB first (no write) [TDO/DI sampled just prior to falling edge]"},
180
    {0x31, "Clock Data Bytes In and Out MSB first [out on -ve edge, in on +ve edge]"},
181
    {0x33, "Clock Data Bits In and Out MSB first [out on -ve edge, in on +ve edge]"},
182
    {0x34, "Clock Data Bytes In and Out MSB first [out on +ve edge, in on -ve edge]"},
183
    {0x36, "Clock Data Bits In and Out MSB first [out on +ve edge, in on -ve edge]"},
184
    {0x39, "Clock Data Bytes In and Out LSB first [out on -ve edge, in on +ve edge]"},
185
    {0x3B, "Clock Data Bits In and Out LSB first [out on -ve edge, in on +ve edge]"},
186
    {0x3C, "Clock Data Bytes In and Out LSB first [out on +ve edge, in on -ve edge]"},
187
    {0x3E, "Clock Data Bits In and Out LSB first [out on +ve edge, in on -ve edge]"},
188
    {0x4A, "Clock Data to TMS pin (no read) [TMS with LSB first on +ve clk edge - use if clk is set to '1']"},
189
    {0x4B, "Clock Data to TMS pin (no read) [TMS with LSB first on -ve clk edge - use if clk is set to '0']"},
190
    {0x6A, "Clock Data to TMS pin with read [TMS with LSB first on +ve clk edge, read on +ve edge - use if clk is set to '1']"},
191
    {0x6B, "Clock Data to TMS pin with read [TMS with LSB first on -ve clk edge, read on +ve edge - use if clk is set to '0']"},
192
    {0x6E, "Clock Data to TMS pin with read [TMS with LSB first on +ve clk edge, read on -ve edge - use if clk is set to '1']"},
193
    {0x6F, "Clock Data to TMS pin with read [TMS with LSB first on -ve clk edge, read on -ve edge - use if clk is set to '0']"},
194
    {CMD_SET_DATA_BITS_LOW_BYTE, "Set Data bits LowByte"},
195
    {CMD_READ_DATA_BITS_LOW_BYTE, "Read Data bits LowByte"},
196
    {CMD_SET_DATA_BITS_HIGH_BYTE, "Set Data bits HighByte"},
197
    {CMD_READ_DATA_BITS_HIGH_BYTE, "Read Data bits HighByte"},
198
    {0x84, "Connect TDI to TDO for Loopback"},
199
    {0x85, "Disconnect TDI to TDO for Loopback"},
200
    {0x87, "Send Immediate (flush buffer back to the PC)"},
201
    {0x88, "Wait On I/O High (wait until GPIOL1 (JTAG) or I/O1 (CPU) is high)"},
202
    {0x89, "Wait On I/O Low (wait until GPIOL1 (JTAG) or I/O1 (CPU) is low)"},
203
    {0, NULL}
204
};
205
static value_string_ext command_vals_ext = VALUE_STRING_EXT_INIT(command_vals);
206
207
static const value_string cpumode_command_vals[] = {
208
    {CMD_CPUMODE_READ_SHORT_ADDR, "CPUMode Read Short Address"},
209
    {CMD_CPUMODE_READ_EXT_ADDR, "CPUMode Read Extended Address"},
210
    {CMD_CPUMODE_WRITE_SHORT_ADDR, "CPUMode Write Short Address"},
211
    {CMD_CPUMODE_WRITE_EXT_ADDR, "CPUMode Write Extended Address"},
212
    {0, NULL}
213
};
214
static value_string_ext cpumode_command_vals_ext = VALUE_STRING_EXT_INIT(cpumode_command_vals);
215
216
static const value_string ft2232d_only_command_vals[] = {
217
    {CMD_CLOCK_SET_DIVISOR, "Set TCK/SK Divisor"},
218
    {0, NULL}
219
};
220
221
/* FT232H, FT2232H and FT4232H only commands */
222
static const value_string h_only_command_vals[] = {
223
    {CMD_CLOCK_SET_DIVISOR, "Set clk divisor"},
224
    {0x8A, "Disable Clk Divide by 5"},
225
    {0x8B, "Enable Clk Divide by 5"},
226
    {0x8C, "Enable 3 Phase Data Clocking"},
227
    {0x8D, "Disable 3 Phase Data Clocking"},
228
    {CMD_CLOCK_N_BITS, "Clock For n bits with no data transfer"},
229
    {CMD_CLOCK_N_TIMES_8_BITS, "Clock For n x 8 bits with no data transfer"},
230
    {0x94, "Clk continuously and Wait On I/O High"},
231
    {0x95, "Clk continuously and Wait On I/O Low"},
232
    {0x96, "Turn On Adaptive clocking"},
233
    {0x97, "Turn Off Adaptive clocking"},
234
    {CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_HIGH, "Clock For n x 8 bits with no data transfer or Until GPIOL1 is High"},
235
    {CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_LOW, "Clock For n x 8 bits with no data transfer or Until GPIOL1 is Low"},
236
    {0, NULL}
237
};
238
static value_string_ext h_only_command_vals_ext = VALUE_STRING_EXT_INIT(h_only_command_vals);
239
240
static const value_string ft232h_only_command_vals[] = {
241
    {CMD_IO_OPEN_DRAIN_ENABLE, "Set I/O to only drive on a '0' and tristate on a '1'"},
242
    {0, NULL}
243
};
244
245
static const value_string data_shifting_command_b1_vals[] = {
246
    {0, "Byte mode"},
247
    {1, "Bit mode"},
248
    {0, NULL}
249
};
250
251
static const value_string data_shifting_command_b3_vals[] = {
252
    {0, "MSB first"},
253
    {1, "LSB first"},
254
    {0, NULL}
255
};
256
257
static const value_string command_b7_vals[] = {
258
    {0, "Data Shifting Command"},
259
    {1, "Other (Not Data Shifting) Command"},
260
    {0, NULL}
261
};
262
263
0
#define IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd) ((cmd & (1u << 7)) == 0)
264
0
#define IS_DATA_SHIFTING_BYTE_MODE(cmd)          ((cmd & (1u << 1)) == 0)
265
0
#define IS_DATA_SHIFTING_MSB_FIRST(cmd)          ((cmd & (1u << 3)) == 0)
266
0
#define IS_DATA_SHIFTING_WRITING_TDI(cmd)        (cmd & (1u << 4))
267
0
#define IS_DATA_SHIFTING_READING_TDO(cmd)        (cmd & (1u << 5))
268
0
#define IS_DATA_SHIFTING_WRITING_TMS(cmd)        (cmd & (1u << 6))
269
270
static bool is_data_shifting_command(uint8_t cmd)
271
0
{
272
0
    switch (cmd)
273
0
    {
274
    /* Not all data shifting commands (with bit 7 clear) are explicitly listed in MPSSE documentation
275
     * Some undocumented data shifting commands trigger BadCommmand response, but some seem to be handled by the device.
276
     *
277
     * Commands listed below (with bit 7 clear) trigger BadCommand response on FT2232L, FT232H and FT2232H
278
     */
279
0
    case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F:
280
0
    case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49:
281
0
    case 0x4C: case 0x4D:
282
0
    case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59:
283
0
    case 0x5C: case 0x5D:
284
0
    case 0x60: case 0x61:
285
0
    case 0x64: case 0x65:
286
0
    case 0x68: case 0x69:
287
0
    case 0x6C: case 0x6D:
288
0
    case 0x70: case 0x71:
289
0
    case 0x74: case 0x75:
290
0
    case 0x78: case 0x79:
291
0
    case 0x7C: case 0x7D:
292
0
        return false;
293
0
    default:
294
0
        return IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd);
295
0
    }
296
0
}
297
298
static bool is_data_shifting_command_returning_response(uint8_t cmd, ftdi_mpsse_info_t *mpsse_info)
299
0
{
300
0
    DISSECTOR_ASSERT(is_data_shifting_command(cmd));
301
0
    if (mpsse_info->mcu_mode)
302
0
    {
303
        /* MCU mode seems to consume data shifting payloads but do not actually return any response data */
304
0
        return false;
305
0
    }
306
307
0
    return IS_DATA_SHIFTING_READING_TDO(cmd) ? true : false;
308
0
}
309
310
/* Returns human-readable command description string or NULL on BadCommand */
311
static const char *
312
get_command_string(uint8_t cmd, ftdi_mpsse_info_t *mpsse_info)
313
0
{
314
0
    const char *str;
315
316
    /* First, try commands that are common on all chips */
317
0
    str = try_val_to_str_ext(cmd, &command_vals_ext);
318
0
    if (str)
319
0
    {
320
0
        return str;
321
0
    }
322
323
0
    if (is_data_shifting_command(cmd))
324
0
    {
325
0
        return "Undocumented Data Shifting Command";
326
0
    }
327
328
    /* Check chip specific commands */
329
0
    switch (mpsse_info->chip)
330
0
    {
331
0
    case FTDI_CHIP_FT2232D:
332
0
        str = try_val_to_str(cmd, ft2232d_only_command_vals);
333
0
        break;
334
0
    case FTDI_CHIP_FT232H:
335
0
        str = try_val_to_str(cmd, ft232h_only_command_vals);
336
0
        if (str)
337
0
        {
338
0
            break;
339
0
        }
340
        /* Fallthrough */
341
0
    case FTDI_CHIP_FT2232H:
342
0
    case FTDI_CHIP_FT4232H:
343
0
        str = try_val_to_str_ext(cmd, &h_only_command_vals_ext);
344
0
        break;
345
0
    default:
346
0
        break;
347
0
    }
348
349
0
    if (!str && mpsse_info->mcu_mode)
350
0
    {
351
0
        str = try_val_to_str_ext(cmd, &cpumode_command_vals_ext);
352
0
    }
353
354
0
    return str;
355
0
}
356
357
static bool is_valid_command(uint8_t cmd, ftdi_mpsse_info_t *mpsse_info)
358
0
{
359
0
    return get_command_string(cmd, mpsse_info) != NULL;
360
0
}
361
362
static bool is_same_mpsse_instance(ftdi_mpsse_info_t *info1, ftdi_mpsse_info_t *info2)
363
0
{
364
0
    return (info1->bus_id == info2->bus_id) &&
365
0
           (info1->device_address == info2->device_address) &&
366
0
           (info1->chip == info2->chip) &&
367
0
           (info1->iface == info2->iface) &&
368
0
           (info1->mcu_mode == info2->mcu_mode);
369
0
}
370
371
static command_data_t *
372
get_recorded_command_data(wmem_tree_t *command_tree, packet_info *pinfo, ftdi_mpsse_info_t *mpsse_info)
373
0
{
374
0
    uint32_t        k_bus_id = mpsse_info->bus_id;
375
0
    uint32_t        k_device_address = mpsse_info->device_address;
376
0
    uint32_t        k_chip = (uint32_t)mpsse_info->chip;
377
0
    uint32_t        k_interface = (uint32_t)mpsse_info->iface;
378
0
    uint32_t        k_mcu_mode = mpsse_info->mcu_mode;
379
0
    wmem_tree_key_t key[] = {
380
0
        {1, &k_bus_id},
381
0
        {1, &k_device_address},
382
0
        {1, &k_chip},
383
0
        {1, &k_interface},
384
0
        {1, &k_mcu_mode},
385
0
        {1, &pinfo->num},
386
0
        {0, NULL}
387
0
    };
388
389
0
    command_data_t *data = NULL;
390
0
    data = (command_data_t *)wmem_tree_lookup32_array_le(command_tree, key);
391
0
    if (data && is_same_mpsse_instance(mpsse_info, &data->mpsse_info))
392
0
    {
393
0
        return data;
394
0
    }
395
396
0
    return NULL;
397
0
}
398
399
static void
400
insert_command_data_pointer(wmem_tree_t *command_tree, packet_info *pinfo, ftdi_mpsse_info_t *mpsse_info, command_data_t *data)
401
0
{
402
0
    uint32_t        k_bus_id = mpsse_info->bus_id;
403
0
    uint32_t        k_device_address = mpsse_info->device_address;
404
0
    uint32_t        k_chip = (uint32_t)mpsse_info->chip;
405
0
    uint32_t        k_interface = (uint32_t)mpsse_info->iface;
406
0
    uint32_t        k_mcu_mode = mpsse_info->mcu_mode;
407
0
    wmem_tree_key_t key[] = {
408
0
        {1, &k_bus_id},
409
0
        {1, &k_device_address},
410
0
        {1, &k_chip},
411
0
        {1, &k_interface},
412
0
        {1, &k_mcu_mode},
413
0
        {1, &pinfo->num},
414
0
        {0, NULL}
415
0
    };
416
417
0
    wmem_tree_insert32_array(command_tree, key, data);
418
0
}
419
420
static void
421
record_command_data(command_data_t **cmd_data, packet_info *pinfo, ftdi_mpsse_info_t *mpsse_info, uint8_t cmd,
422
                    int32_t response_length, bool preliminary)
423
0
{
424
0
    command_data_t *data = *cmd_data;
425
426
0
    DISSECTOR_ASSERT(response_length > 0);
427
428
0
    if (data && data->preliminary)
429
0
    {
430
0
        DISSECTOR_ASSERT(data->cmd == cmd);
431
0
        DISSECTOR_ASSERT(data->response_length == response_length);
432
0
        data->command_in_packet = pinfo->num;
433
0
        data->preliminary = preliminary;
434
0
        return;
435
0
    }
436
437
0
    data = wmem_new(wmem_file_scope(), command_data_t);
438
0
    memcpy(&data->mpsse_info, mpsse_info, sizeof(ftdi_mpsse_info_t));
439
0
    data->preliminary = preliminary;
440
0
    data->is_response_set = false;
441
0
    data->cmd = cmd;
442
0
    data->response_length = response_length;
443
0
    data->command_in_packet = pinfo->num;
444
0
    data->response_in_packet = 0;
445
0
    data->next = NULL;
446
447
0
    if (*cmd_data && (!(*cmd_data)->is_response_set))
448
0
    {
449
0
        DISSECTOR_ASSERT((*cmd_data)->next == NULL);
450
0
        (*cmd_data)->next = data;
451
0
        if ((*cmd_data)->command_in_packet != pinfo->num)
452
0
        {
453
0
            insert_command_data_pointer(tx_command_info, pinfo, mpsse_info, data);
454
0
        }
455
0
    }
456
0
    else
457
0
    {
458
0
        insert_command_data_pointer(rx_command_info, pinfo, mpsse_info, data);
459
0
        insert_command_data_pointer(tx_command_info, pinfo, mpsse_info, data);
460
0
    }
461
0
    *cmd_data = data;
462
0
}
463
464
static void expect_response(command_data_t **cmd_data, packet_info *pinfo, proto_tree *tree,
465
                            ftdi_mpsse_info_t *mpsse_info, uint8_t cmd, uint16_t response_length)
466
0
{
467
0
    if (pinfo->fd->visited)
468
0
    {
469
0
        DISSECTOR_ASSERT(*cmd_data);
470
0
        DISSECTOR_ASSERT((*cmd_data)->cmd == cmd);
471
0
        DISSECTOR_ASSERT((*cmd_data)->response_length == response_length);
472
0
        if ((*cmd_data)->is_response_set)
473
0
        {
474
0
            proto_tree *response_in = proto_tree_add_uint(tree, hf_mpsse_response_in, NULL, 0, 0, (*cmd_data)->response_in_packet);
475
0
            proto_item_set_generated(response_in);
476
0
            DISSECTOR_ASSERT((*cmd_data)->command_in_packet == pinfo->num);
477
0
        }
478
0
        *cmd_data = (*cmd_data)->next;
479
0
    }
480
0
    else
481
0
    {
482
0
        record_command_data(cmd_data, pinfo, mpsse_info, cmd, response_length, false);
483
0
    }
484
0
}
485
486
static char* freq_to_str(float freq)
487
0
{
488
0
    if (freq < 1e3)
489
0
    {
490
0
        return ws_strdup_printf("%.12g Hz", freq);
491
0
    }
492
0
    else if (freq < 1e6)
493
0
    {
494
0
        return ws_strdup_printf("%.12g kHz", freq / 1e3);
495
0
    }
496
0
    else
497
0
    {
498
0
        return ws_strdup_printf("%.12g MHz", freq / 1e6);
499
0
    }
500
0
}
501
502
static int
503
dissect_data_shifting_command_parameters(uint8_t cmd, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
504
                                         ftdi_mpsse_info_t *mpsse_info, command_data_t **cmd_data)
505
0
{
506
0
    int          offset_start = offset;
507
0
    int32_t      length;
508
509
0
    DISSECTOR_ASSERT(is_data_shifting_command(cmd));
510
511
0
    if (IS_DATA_SHIFTING_BYTE_MODE(cmd))
512
0
    {
513
0
        length = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN);
514
0
        proto_tree_add_uint_format(tree, hf_mpsse_length_uint16, tvb, offset, 2, length, "Length: %d byte%s", length + 1, plurality(length + 1, "", "s"));
515
0
        offset += 2;
516
517
0
        if (IS_DATA_SHIFTING_WRITING_TDI(cmd))
518
0
        {
519
0
            proto_tree_add_item(tree, hf_mpsse_bytes_out, tvb, offset, length + 1, ENC_NA);
520
0
            offset += length + 1;
521
0
        }
522
0
    }
523
0
    else
524
0
    {
525
0
        length = tvb_get_uint8(tvb, offset);
526
0
        proto_tree_add_uint_format(tree, hf_mpsse_length_uint8, tvb, offset, 1, length, "Length: %d bit%s", length + 1, plurality(length + 1, "", "s"));
527
0
        offset += 1;
528
529
0
        if (IS_DATA_SHIFTING_WRITING_TMS(cmd) && IS_DATA_SHIFTING_READING_TDO(cmd) && IS_DATA_SHIFTING_MSB_FIRST(cmd))
530
0
        {
531
            /* These undocumented commands do not seem to consume the data byte, only the length */
532
0
        }
533
0
        else if (IS_DATA_SHIFTING_WRITING_TDI(cmd) || IS_DATA_SHIFTING_WRITING_TMS(cmd))
534
0
        {
535
0
            proto_tree_add_item(tree, hf_mpsse_bits_out, tvb, offset, 1, ENC_LITTLE_ENDIAN);
536
0
            offset += 1;
537
0
        }
538
0
    }
539
540
0
    if (is_data_shifting_command_returning_response(cmd, mpsse_info))
541
0
    {
542
0
        expect_response(cmd_data, pinfo, tree, mpsse_info, cmd, IS_DATA_SHIFTING_BYTE_MODE(cmd) ? length + 1 : 1);
543
0
    }
544
545
0
    return offset - offset_start;
546
0
}
547
548
static int
549
dissect_set_data_bits_parameters(uint8_t cmd _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset,
550
                                 const char *signal_names[8], const char *pin_prefix, unsigned num_pins)
551
0
{
552
0
    static const int *value_bits_hf[] = {
553
0
        &hf_mpsse_value_b0,
554
0
        &hf_mpsse_value_b1,
555
0
        &hf_mpsse_value_b2,
556
0
        &hf_mpsse_value_b3,
557
0
        &hf_mpsse_value_b4,
558
0
        &hf_mpsse_value_b5,
559
0
        &hf_mpsse_value_b6,
560
0
        &hf_mpsse_value_b7,
561
0
    };
562
0
    static const int *direction_bits_hf[] = {
563
0
        &hf_mpsse_direction_b0,
564
0
        &hf_mpsse_direction_b1,
565
0
        &hf_mpsse_direction_b2,
566
0
        &hf_mpsse_direction_b3,
567
0
        &hf_mpsse_direction_b4,
568
0
        &hf_mpsse_direction_b5,
569
0
        &hf_mpsse_direction_b6,
570
0
        &hf_mpsse_direction_b7,
571
0
    };
572
0
    uint32_t value, direction;
573
0
    proto_item *item;
574
0
    proto_item *value_item, *direction_item;
575
0
    proto_tree *value_tree, *direction_tree;
576
0
    unsigned bit;
577
578
0
    value_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_value, tvb, offset, 1, ENC_LITTLE_ENDIAN, &value);
579
0
    direction_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_direction, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN, &direction);
580
581
0
    value_tree = proto_item_add_subtree(value_item, ett_mpsse_value);
582
0
    for (bit = 0; bit < 8; bit++)
583
0
    {
584
0
        const char *state;
585
0
        if ((1 << bit) & direction)
586
0
        {
587
0
            state = ((1 << bit) & value) ? "Output High" : "Output Low";
588
0
        }
589
0
        else
590
0
        {
591
0
            state = "N/A (Input)";
592
0
        }
593
0
        item = proto_tree_add_uint_format_value(value_tree, *value_bits_hf[bit], tvb, offset, 1, value, "%s", signal_names[bit]);
594
0
        if (pin_prefix && (bit < num_pins))
595
0
        {
596
0
            proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
597
0
        }
598
0
        proto_item_append_text(item, " %s", state);
599
0
    }
600
601
0
    direction_tree = proto_item_add_subtree(direction_item, ett_mpsse_direction);
602
0
    for (bit = 0; bit < 8; bit++)
603
0
    {
604
0
        const char *type = ((1 << bit) & direction) ? "Output" : "Input";
605
0
        item = proto_tree_add_uint_format_value(direction_tree, *direction_bits_hf[bit], tvb, offset + 1, 1, direction, "%s", signal_names[bit]);
606
0
        if (pin_prefix && (bit < num_pins))
607
0
        {
608
0
            proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
609
0
        }
610
0
        proto_item_append_text(item, " %s", type);
611
0
    }
612
613
0
    return 2;
614
0
}
615
616
static int
617
dissect_cpumode_parameters(uint8_t cmd, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
618
                           ftdi_mpsse_info_t *mpsse_info, command_data_t **cmd_data)
619
0
{
620
0
    int          offset_start = offset;
621
622
    /* Address is either short (1 byte) or extended (2 bytes) */
623
0
    if ((cmd == CMD_CPUMODE_READ_SHORT_ADDR) || (cmd == CMD_CPUMODE_WRITE_SHORT_ADDR))
624
0
    {
625
0
        proto_tree_add_item(tree, hf_mpsse_cpumode_address_short, tvb, offset, 1, ENC_BIG_ENDIAN);
626
0
        offset += 1;
627
0
    }
628
0
    else if ((cmd == CMD_CPUMODE_READ_EXT_ADDR) || (cmd == CMD_CPUMODE_WRITE_EXT_ADDR))
629
0
    {
630
0
        proto_tree_add_item(tree, hf_mpsse_cpumode_address_extended, tvb, offset, 2, ENC_BIG_ENDIAN);
631
0
        offset += 2;
632
0
    }
633
634
    /* Write commands have data parameter (1 byte) */
635
0
    if ((cmd == CMD_CPUMODE_WRITE_SHORT_ADDR) || (cmd == CMD_CPUMODE_WRITE_EXT_ADDR))
636
0
    {
637
0
        proto_tree_add_item(tree, hf_mpsse_cpumode_data, tvb, offset, 1, ENC_LITTLE_ENDIAN);
638
0
        offset += 1;
639
0
    }
640
641
0
    if ((cmd == CMD_CPUMODE_READ_SHORT_ADDR) || (cmd == CMD_CPUMODE_READ_EXT_ADDR))
642
0
    {
643
0
        expect_response(cmd_data, pinfo, tree, mpsse_info, cmd, 1);
644
0
    }
645
646
0
    return offset - offset_start;
647
0
}
648
649
static int
650
dissect_clock_parameters(uint8_t cmd _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, ftdi_mpsse_info_t *mpsse_info)
651
0
{
652
0
    int          offset_start = offset;
653
0
    uint32_t     value;
654
0
    proto_item   *item;
655
0
    char         *str_old, *str;
656
657
0
    item = proto_tree_add_item_ret_uint(tree, hf_mpsse_clk_divisor, tvb, offset, 2, ENC_LITTLE_ENDIAN, &value);
658
0
    offset += 2;
659
660
0
    str_old = freq_to_str((float) 12e6 / ((1 + value) * 2));
661
0
    str = freq_to_str((float) 60e6 / ((1 + value) * 2));
662
663
0
    if (mpsse_info->chip == FTDI_CHIP_FT2232D)
664
0
    {
665
0
        proto_item_append_text(item, ", TCK/SK Max: %s", str_old);
666
0
    }
667
0
    else
668
0
    {
669
0
        proto_item_append_text(item, ", TCK Max: %s (60 MHz master clock) or %s (12 MHz master clock)", str, str_old);
670
0
    }
671
672
0
    g_free(str_old);
673
0
    g_free(str);
674
675
0
    return offset - offset_start;
676
0
}
677
678
static int
679
dissect_clock_n_bits_parameters(uint8_t cmd _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, ftdi_mpsse_info_t *mpsse_info _U_)
680
0
{
681
0
    uint32_t length = tvb_get_uint8(tvb, offset);
682
0
    proto_tree_add_uint_format(tree, hf_mpsse_length_uint8, tvb, offset, 1, length, "Length: %d clock%s", length + 1, plurality(length + 1, "", "s"));
683
0
    return 1;
684
0
}
685
686
static int
687
dissect_clock_n_times_8_bits_parameters(uint8_t cmd _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, ftdi_mpsse_info_t *mpsse_info _U_)
688
0
{
689
0
    uint32_t length = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN);
690
0
    proto_tree_add_uint_format(tree, hf_mpsse_length_uint16, tvb, offset, 2, length, "Length: %d clocks", (length + 1) * 8);
691
0
    return 2;
692
0
}
693
694
static const char *
695
get_data_bit_pin_prefix(bool is_high_byte, ftdi_mpsse_info_t *mpsse_info, unsigned *out_num_pins, const char *(**out_names)[8])
696
0
{
697
0
    static const char *low_byte_signal_names[8] = {
698
0
        "TCK/SK",
699
0
        "TDI/DO",
700
0
        "TDO/DI",
701
0
        "TMS/CS",
702
0
        "GPIOL0",
703
0
        "GPIOL1",
704
0
        "GPIOL2",
705
0
        "GPIOL3",
706
0
    };
707
0
    static const char *high_byte_signal_names[8] = {
708
0
        "GPIOH0",
709
0
        "GPIOH1",
710
0
        "GPIOH2",
711
0
        "GPIOH3",
712
0
        "GPIOH4",
713
0
        "GPIOH5",
714
0
        "GPIOH6",
715
0
        "GPIOH7",
716
0
    };
717
718
0
    *out_names = (is_high_byte) ? &high_byte_signal_names : &low_byte_signal_names;
719
720
    /* Based on table from FTDI AN_108 chapter 2.1 Data bit Definition */
721
0
    switch (mpsse_info->chip)
722
0
    {
723
0
    case FTDI_CHIP_FT2232D:
724
0
        if (mpsse_info->iface == FTDI_INTERFACE_A)
725
0
        {
726
0
            *out_num_pins = (is_high_byte) ? 4 : 8;
727
0
            return (is_high_byte) ? "ACBUS" : "ADBUS";
728
0
        }
729
0
        break;
730
0
    case FTDI_CHIP_FT232H:
731
0
        *out_num_pins = 8;
732
0
        return (is_high_byte) ? "ACBUS" : "ADBUS";
733
0
    case FTDI_CHIP_FT2232H:
734
0
        if (mpsse_info->iface == FTDI_INTERFACE_A)
735
0
        {
736
0
            *out_num_pins = 8;
737
0
            return (is_high_byte) ? "ACBUS" : "ADBUS";
738
0
        }
739
0
        else if (mpsse_info->iface == FTDI_INTERFACE_B)
740
0
        {
741
0
            *out_num_pins = 8;
742
0
            return (is_high_byte) ? "BCBUS" : "BDBUS";
743
0
        }
744
0
        break;
745
0
    case FTDI_CHIP_FT4232H:
746
0
        if (!is_high_byte)
747
0
        {
748
0
            if (mpsse_info->iface == FTDI_INTERFACE_A)
749
0
            {
750
0
                *out_num_pins = 8;
751
0
                return "ADBUS";
752
0
            }
753
0
            else if (mpsse_info->iface == FTDI_INTERFACE_B)
754
0
            {
755
0
                *out_num_pins = 8;
756
0
                return "BDBUS";
757
0
            }
758
0
        }
759
0
        break;
760
0
    default:
761
0
        break;
762
0
    }
763
764
0
    *out_num_pins = 0;
765
0
    return NULL;
766
0
}
767
768
static int
769
dissect_io_open_drain_enable_parameters(uint8_t cmd _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, ftdi_mpsse_info_t *mpsse_info _U_)
770
0
{
771
0
    static const int *low_byte_bits_hf[] = {
772
0
        &hf_mpsse_open_drain_enable_low_b0,
773
0
        &hf_mpsse_open_drain_enable_low_b1,
774
0
        &hf_mpsse_open_drain_enable_low_b2,
775
0
        &hf_mpsse_open_drain_enable_low_b3,
776
0
        &hf_mpsse_open_drain_enable_low_b4,
777
0
        &hf_mpsse_open_drain_enable_low_b5,
778
0
        &hf_mpsse_open_drain_enable_low_b6,
779
0
        &hf_mpsse_open_drain_enable_low_b7,
780
0
    };
781
0
    static const int *high_byte_bits_hf[] = {
782
0
        &hf_mpsse_open_drain_enable_high_b0,
783
0
        &hf_mpsse_open_drain_enable_high_b1,
784
0
        &hf_mpsse_open_drain_enable_high_b2,
785
0
        &hf_mpsse_open_drain_enable_high_b3,
786
0
        &hf_mpsse_open_drain_enable_high_b4,
787
0
        &hf_mpsse_open_drain_enable_high_b5,
788
0
        &hf_mpsse_open_drain_enable_high_b6,
789
0
        &hf_mpsse_open_drain_enable_high_b7,
790
0
    };
791
0
    int         offset_start = offset;
792
0
    const char *pin_prefix = NULL;
793
0
    unsigned    num_pins = 0;
794
0
    const char *(*signal_names)[8] = NULL;
795
0
    uint32_t    value;
796
0
    proto_item *item;
797
0
    proto_item *byte_item;
798
0
    proto_tree *byte_tree;
799
0
    unsigned    bit;
800
801
0
    pin_prefix = get_data_bit_pin_prefix(false, mpsse_info, &num_pins, &signal_names);
802
0
    byte_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_open_drain_enable_low, tvb, offset, 1, ENC_LITTLE_ENDIAN, &value);
803
0
    byte_tree = proto_item_add_subtree(byte_item, ett_mpsse_open_drain_enable);
804
0
    for (bit = 0; bit < 8; bit++)
805
0
    {
806
0
        const char *output_type = ((1 << bit) & value) ? "Open-Drain" : "Push-Pull";
807
0
        item = proto_tree_add_uint_format_value(byte_tree, *low_byte_bits_hf[bit], tvb, offset, 1, value, "%s", (*signal_names)[bit]);
808
0
        if (pin_prefix && (bit < num_pins))
809
0
        {
810
0
            proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
811
0
        }
812
0
        proto_item_append_text(item, " %s", output_type);
813
0
    }
814
0
    offset++;
815
816
0
    pin_prefix = get_data_bit_pin_prefix(true, mpsse_info, &num_pins, &signal_names);
817
0
    byte_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_open_drain_enable_high, tvb, offset, 1, ENC_LITTLE_ENDIAN, &value);
818
0
    byte_tree = proto_item_add_subtree(byte_item, ett_mpsse_open_drain_enable);
819
0
    for (bit = 0; bit < 8; bit++)
820
0
    {
821
0
        const char *output_type = ((1 << bit) & value) ? "Open-Drain" : "Push-Pull";
822
0
        item = proto_tree_add_uint_format_value(byte_tree, *high_byte_bits_hf[bit], tvb, offset, 1, value, "%s", (*signal_names)[bit]);
823
0
        if (pin_prefix && (bit < num_pins))
824
0
        {
825
0
            proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
826
0
        }
827
0
        proto_item_append_text(item, " %s", output_type);
828
0
    }
829
0
    offset++;
830
831
0
    return offset - offset_start;
832
0
}
833
834
static int
835
dissect_non_data_shifting_command_parameters(uint8_t cmd, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
836
                                             ftdi_mpsse_info_t *mpsse_info, command_data_t **cmd_data)
837
0
{
838
0
    const char *pin_prefix         = NULL;
839
0
    unsigned    num_pins           = 0;
840
0
    const char *(*signal_names)[8] = NULL;
841
842
0
    DISSECTOR_ASSERT(!is_data_shifting_command(cmd) && is_valid_command(cmd, mpsse_info));
843
844
0
    switch (cmd)
845
0
    {
846
0
    case CMD_SET_DATA_BITS_LOW_BYTE:
847
0
        pin_prefix = get_data_bit_pin_prefix(false, mpsse_info, &num_pins, &signal_names);
848
0
        return dissect_set_data_bits_parameters(cmd, tvb, pinfo, tree, offset, *signal_names, pin_prefix, num_pins);
849
0
    case CMD_SET_DATA_BITS_HIGH_BYTE:
850
0
        pin_prefix = get_data_bit_pin_prefix(true, mpsse_info, &num_pins, &signal_names);
851
0
        return dissect_set_data_bits_parameters(cmd, tvb, pinfo, tree, offset, *signal_names, pin_prefix, num_pins);
852
0
    case CMD_READ_DATA_BITS_LOW_BYTE:
853
0
    case CMD_READ_DATA_BITS_HIGH_BYTE:
854
0
        expect_response(cmd_data, pinfo, tree, mpsse_info, cmd, 1);
855
0
        return 0;
856
0
    case CMD_CPUMODE_READ_SHORT_ADDR:
857
0
    case CMD_CPUMODE_READ_EXT_ADDR:
858
0
    case CMD_CPUMODE_WRITE_SHORT_ADDR:
859
0
    case CMD_CPUMODE_WRITE_EXT_ADDR:
860
0
        return dissect_cpumode_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info, cmd_data);
861
0
    case CMD_CLOCK_SET_DIVISOR:
862
0
        return dissect_clock_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info);
863
0
    case CMD_CLOCK_N_BITS:
864
0
        return dissect_clock_n_bits_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info);
865
0
    case CMD_CLOCK_N_TIMES_8_BITS:
866
0
    case CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_HIGH:
867
0
    case CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_LOW:
868
0
        return dissect_clock_n_times_8_bits_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info);
869
0
    case CMD_IO_OPEN_DRAIN_ENABLE:
870
0
        return dissect_io_open_drain_enable_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info);
871
0
    default:
872
0
        return 0;
873
0
    }
874
0
}
875
876
static int estimated_command_parameters_length(uint8_t cmd, tvbuff_t *tvb, packet_info *pinfo, int offset,
877
                                                ftdi_mpsse_info_t *mpsse_info, command_data_t **cmd_data)
878
0
{
879
0
    int parameters_length = 0;
880
881
0
    if (!is_valid_command(cmd, mpsse_info))
882
0
    {
883
0
        return 0;
884
0
    }
885
886
0
    if (is_data_shifting_command(cmd))
887
0
    {
888
0
        int32_t data_length = 0;
889
0
        if (IS_DATA_SHIFTING_BYTE_MODE(cmd))
890
0
        {
891
0
            parameters_length = 2;
892
0
            if (IS_DATA_SHIFTING_WRITING_TDI(cmd))
893
0
            {
894
0
                if (tvb_reported_length_remaining(tvb, offset) >= 2)
895
0
                {
896
0
                    data_length = (int32_t)tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN) + 1;
897
0
                    parameters_length += data_length;
898
0
                }
899
                /* else length is not available already so the caller will know that reassembly is needed */
900
0
            }
901
0
        }
902
0
        else /* bit mode */
903
0
        {
904
0
            parameters_length = (IS_DATA_SHIFTING_WRITING_TDI(cmd) || IS_DATA_SHIFTING_WRITING_TMS(cmd)) ? 2 : 1;
905
0
            data_length = 1;
906
0
            if (IS_DATA_SHIFTING_WRITING_TMS(cmd) && IS_DATA_SHIFTING_READING_TDO(cmd) && IS_DATA_SHIFTING_MSB_FIRST(cmd))
907
0
            {
908
                /* These undocumented commands do not seem to consume the data byte, only the length */
909
0
                parameters_length = 1;
910
0
            }
911
0
        }
912
913
0
        if (!pinfo->fd->visited)
914
0
        {
915
0
            if (is_data_shifting_command_returning_response(cmd, mpsse_info) && data_length)
916
0
            {
917
                /* Record preliminary command info so the response handler can find the matching command
918
                 * if host starts reading data before all output is sent. If this command requires reassembly
919
                 * the command_in_packet member will continue updating until the reassembly is complete.
920
                 * The preliminary flag will be reset when expect_response() executes.
921
                 */
922
0
                record_command_data(cmd_data, pinfo, mpsse_info, cmd, data_length, true);
923
0
            }
924
0
        }
925
0
    }
926
0
    else
927
0
    {
928
0
        switch (cmd)
929
0
        {
930
0
        case CMD_CPUMODE_WRITE_EXT_ADDR:
931
0
            parameters_length = 3;
932
0
            break;
933
0
        case CMD_SET_DATA_BITS_LOW_BYTE:
934
0
        case CMD_SET_DATA_BITS_HIGH_BYTE:
935
0
        case CMD_CPUMODE_READ_EXT_ADDR:
936
0
        case CMD_CPUMODE_WRITE_SHORT_ADDR:
937
0
        case CMD_CLOCK_SET_DIVISOR:
938
0
        case 0x8F: case 0x9C: case 0x9D: case 0x9E:
939
0
            parameters_length = 2;
940
0
            break;
941
0
        case CMD_CPUMODE_READ_SHORT_ADDR:
942
0
        case 0x8E:
943
0
            parameters_length = 1;
944
0
            break;
945
0
        case 0x81: case 0x83: case 0x84: case 0x85: case 0x87: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x94: case 0x95: case 0x96: case 0x97:
946
0
            parameters_length = 0;
947
0
            break;
948
0
        default:
949
0
            DISSECTOR_ASSERT_NOT_REACHED();
950
0
        }
951
0
    }
952
953
0
    return parameters_length;
954
0
}
955
956
static uint8_t
957
dissect_command_code(uint8_t cmd, const char *cmd_str, tvbuff_t *tvb, proto_tree *tree, int offset, ftdi_mpsse_info_t *mpsse_info _U_)
958
0
{
959
0
    proto_item        *cmd_item;
960
0
    proto_tree        *cmd_tree;
961
0
    int               * const *cmd_bits;
962
0
    static int * const data_shifting_cmd_bits[] = {
963
0
        &hf_mpsse_command_b7,
964
0
        &hf_mpsse_command_b6,
965
0
        &hf_mpsse_command_b5,
966
0
        &hf_mpsse_command_b4,
967
0
        &hf_mpsse_command_b3,
968
0
        &hf_mpsse_command_b2,
969
0
        &hf_mpsse_command_b1,
970
0
        &hf_mpsse_command_b0,
971
0
        NULL
972
0
    };
973
974
0
    static int * const non_data_shifting_cmd_bits[] = {
975
0
        &hf_mpsse_command_b7,
976
0
        NULL
977
0
    };
978
979
0
    cmd_item = proto_tree_add_uint_format(tree, hf_mpsse_command, tvb, offset, 1, cmd, "Command: %s (0x%02x)", cmd_str, cmd);
980
0
    cmd_tree = proto_item_add_subtree(cmd_item, ett_mpsse_command);
981
0
    cmd_bits = IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd) ? data_shifting_cmd_bits : non_data_shifting_cmd_bits;
982
983
0
    proto_tree_add_bitmask_list_value(cmd_tree, tvb, offset, 1, cmd_bits, cmd);
984
985
0
    return cmd;
986
0
}
987
988
static int
989
dissect_command(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, bool *need_reassembly,
990
                ftdi_mpsse_info_t *mpsse_info, command_data_t **cmd_data)
991
0
{
992
0
    uint8_t      cmd;
993
0
    const char  *cmd_str;
994
0
    int          offset_start = offset;
995
0
    int          parameters_length;
996
0
    int          dissected;
997
0
    proto_item  *cmd_with_parameters;
998
0
    proto_tree  *cmd_tree;
999
1000
0
    cmd = tvb_get_uint8(tvb, offset);
1001
0
    cmd_str = get_command_string(cmd, mpsse_info);
1002
0
    parameters_length = estimated_command_parameters_length(cmd, tvb, pinfo, offset + 1, mpsse_info, cmd_data);
1003
0
    if (tvb_reported_length_remaining(tvb, offset + 1) < parameters_length)
1004
0
    {
1005
0
        *need_reassembly = true;
1006
0
        return 0;
1007
0
    }
1008
1009
0
    if (!cmd_str)
1010
0
    {
1011
0
        cmd_str = "Bad Command";
1012
0
    }
1013
1014
0
    cmd_with_parameters = proto_tree_add_bytes_format(tree, hf_mpsse_command_with_parameters, tvb, offset, 1 + parameters_length, NULL, "%s", cmd_str);
1015
0
    cmd_tree = proto_item_add_subtree(cmd_with_parameters, ett_mpsse_command_with_parameters);
1016
1017
0
    cmd = dissect_command_code(cmd, cmd_str, tvb, cmd_tree, offset, mpsse_info);
1018
0
    offset += 1;
1019
1020
0
    *need_reassembly = false;
1021
0
    if (is_valid_command(cmd, mpsse_info))
1022
0
    {
1023
0
        if (IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd))
1024
0
        {
1025
0
            dissected = dissect_data_shifting_command_parameters(cmd, tvb, pinfo, cmd_tree, offset, mpsse_info, cmd_data);
1026
0
            DISSECTOR_ASSERT(dissected == parameters_length);
1027
0
            offset += dissected;
1028
0
        }
1029
0
        else
1030
0
        {
1031
0
            dissected = dissect_non_data_shifting_command_parameters(cmd, tvb, pinfo, cmd_tree, offset, mpsse_info, cmd_data);
1032
0
            if (parameters_length > dissected)
1033
0
            {
1034
0
                proto_tree_add_expert(cmd_tree, pinfo, &ei_undecoded, tvb, offset + dissected, parameters_length - dissected);
1035
0
            }
1036
0
            offset += parameters_length;
1037
0
        }
1038
0
    }
1039
0
    else
1040
0
    {
1041
        /* Expect Bad Command response */
1042
0
        expect_response(cmd_data, pinfo, cmd_tree, mpsse_info, cmd, 2);
1043
0
    }
1044
1045
0
    return offset - offset_start;
1046
0
}
1047
1048
1049
static int
1050
dissect_read_data_bits_response(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset,
1051
                                const char *signal_names[8], const char *pin_prefix, unsigned num_pins)
1052
0
{
1053
0
    static const int *value_bits_hf[] = {
1054
0
        &hf_mpsse_value_b0,
1055
0
        &hf_mpsse_value_b1,
1056
0
        &hf_mpsse_value_b2,
1057
0
        &hf_mpsse_value_b3,
1058
0
        &hf_mpsse_value_b4,
1059
0
        &hf_mpsse_value_b5,
1060
0
        &hf_mpsse_value_b6,
1061
0
        &hf_mpsse_value_b7,
1062
0
    };
1063
0
    uint32_t value;
1064
0
    proto_item *item;
1065
0
    proto_item *value_item;
1066
0
    proto_tree *value_tree;
1067
0
    unsigned bit;
1068
1069
0
    value_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_value, tvb, offset, 1, ENC_LITTLE_ENDIAN, &value);
1070
0
    value_tree = proto_item_add_subtree(value_item, ett_mpsse_value);
1071
0
    for (bit = 0; bit < 8; bit++)
1072
0
    {
1073
0
        const char *state;
1074
0
        state = ((1 << bit) & value) ? "High" : "Low";
1075
0
        item = proto_tree_add_uint_format_value(value_tree, *value_bits_hf[bit], tvb, offset, 1, value, "%s", signal_names[bit]);
1076
0
        if (pin_prefix && (bit < num_pins))
1077
0
        {
1078
0
            proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
1079
0
        }
1080
0
        proto_item_append_text(item, " %s", state);
1081
0
    }
1082
1083
0
    return 1;
1084
0
}
1085
1086
static int
1087
dissect_cpumode_response(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset)
1088
0
{
1089
0
    proto_tree_add_item(tree, hf_mpsse_cpumode_data, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1090
0
    return 1;
1091
0
}
1092
1093
static int
1094
dissect_non_data_shifting_command_response(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, command_data_t *cmd_data)
1095
0
{
1096
0
    const char *pin_prefix         = NULL;
1097
0
    unsigned    num_pins           = 0;
1098
0
    const char *(*signal_names)[8] = NULL;
1099
1100
0
    DISSECTOR_ASSERT(!is_data_shifting_command(cmd_data->cmd) && is_valid_command(cmd_data->cmd, &cmd_data->mpsse_info));
1101
1102
0
    switch (cmd_data->cmd)
1103
0
    {
1104
0
    case CMD_READ_DATA_BITS_LOW_BYTE:
1105
0
        pin_prefix = get_data_bit_pin_prefix(false, &cmd_data->mpsse_info, &num_pins, &signal_names);
1106
0
        return dissect_read_data_bits_response(tvb, pinfo, tree, offset, *signal_names, pin_prefix, num_pins);
1107
0
    case CMD_READ_DATA_BITS_HIGH_BYTE:
1108
0
        pin_prefix = get_data_bit_pin_prefix(true, &cmd_data->mpsse_info, &num_pins, &signal_names);
1109
0
        return dissect_read_data_bits_response(tvb, pinfo, tree, offset, *signal_names, pin_prefix, num_pins);
1110
0
    case CMD_CPUMODE_READ_SHORT_ADDR:
1111
0
    case CMD_CPUMODE_READ_EXT_ADDR:
1112
0
        return dissect_cpumode_response(tvb, pinfo, tree, offset);
1113
0
    default:
1114
0
        return 0;
1115
0
    }
1116
0
}
1117
static int
1118
dissect_response_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, command_data_t *cmd_data)
1119
0
{
1120
0
    int          offset_start = offset;
1121
1122
0
    if (pinfo->fd->visited)
1123
0
    {
1124
0
        DISSECTOR_ASSERT(cmd_data->is_response_set && cmd_data->response_in_packet == pinfo->num);
1125
0
    }
1126
0
    else
1127
0
    {
1128
0
        DISSECTOR_ASSERT(!cmd_data->is_response_set);
1129
0
        cmd_data->response_in_packet = pinfo->num;
1130
0
        cmd_data->is_response_set = true;
1131
0
    }
1132
1133
0
    if (is_valid_command(cmd_data->cmd, &cmd_data->mpsse_info))
1134
0
    {
1135
0
        if (IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd_data->cmd))
1136
0
        {
1137
0
            if (IS_DATA_SHIFTING_BYTE_MODE(cmd_data->cmd))
1138
0
            {
1139
0
                proto_tree_add_item(tree, hf_mpsse_bytes_in, tvb, offset, cmd_data->response_length, ENC_NA);
1140
0
            }
1141
0
            else
1142
0
            {
1143
0
                proto_tree_add_item(tree, hf_mpsse_bits_in, tvb, offset, cmd_data->response_length, ENC_LITTLE_ENDIAN);
1144
0
            }
1145
0
            offset += cmd_data->response_length;
1146
0
        }
1147
0
        else
1148
0
        {
1149
0
            int dissected;
1150
1151
0
            dissected = dissect_non_data_shifting_command_response(tvb, pinfo, tree, offset, cmd_data);
1152
0
            offset += dissected;
1153
1154
0
            DISSECTOR_ASSERT(dissected <= cmd_data->response_length);
1155
0
            if (cmd_data->response_length > dissected)
1156
0
            {
1157
0
                proto_tree_add_expert(tree, pinfo, &ei_undecoded, tvb, offset, cmd_data->response_length - dissected);
1158
0
                offset += (cmd_data->response_length - dissected);
1159
0
            }
1160
0
        }
1161
0
    }
1162
0
    else
1163
0
    {
1164
0
        proto_tree_add_item(tree, hf_mpsse_bad_command_error, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1165
0
        offset++;
1166
1167
0
        proto_tree_add_item(tree, hf_mpsse_bad_command_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1168
0
        offset++;
1169
0
    }
1170
1171
0
    return offset - offset_start;
1172
0
}
1173
1174
static int
1175
dissect_response(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, bool *need_reassembly,
1176
                 command_data_t *cmd_data)
1177
0
{
1178
0
    const char  *cmd_str;
1179
0
    int          offset_start = offset;
1180
0
    proto_item  *rsp_data;
1181
0
    proto_tree  *rsp_tree;
1182
0
    proto_item  *command_in;
1183
1184
0
    cmd_str = get_command_string(cmd_data->cmd, &cmd_data->mpsse_info);
1185
0
    if (!cmd_str)
1186
0
    {
1187
0
        bool found = false;
1188
0
        bool request_reassembly = false;
1189
1190
0
        DISSECTOR_ASSERT(cmd_data->response_length == 2);
1191
0
        cmd_str = "Bad Command";
1192
1193
        /* Look for Bad Command response in data */
1194
0
        while (tvb_reported_length_remaining(tvb, offset) >= 2)
1195
0
        {
1196
0
            if (tvb_get_uint8(tvb, offset) == BAD_COMMAND_SYNC_CODE)
1197
0
            {
1198
0
                if (tvb_get_uint8(tvb, offset + 1) == cmd_data->cmd)
1199
0
                {
1200
0
                    found = true;
1201
0
                    break;
1202
0
                }
1203
0
            }
1204
0
            offset++;
1205
0
        }
1206
1207
0
        if (!found)
1208
0
        {
1209
0
            if (tvb_get_uint8(tvb, offset) == BAD_COMMAND_SYNC_CODE)
1210
0
            {
1211
                /* Request reassembly only if there is chance it will help */
1212
0
                request_reassembly = true;
1213
0
            }
1214
0
            else
1215
0
            {
1216
0
                offset++;
1217
0
            }
1218
0
        }
1219
1220
0
        if (offset != offset_start)
1221
0
        {
1222
0
            proto_item *item;
1223
0
            proto_tree *expert_tree;
1224
1225
0
            item = proto_tree_add_expert(tree, pinfo, &ei_skipped_response_data, tvb, offset_start, offset - offset_start);
1226
0
            expert_tree = proto_item_add_subtree(item, ett_mpsse_skipped_response_data);
1227
1228
0
            command_in = proto_tree_add_uint_format(expert_tree, hf_mpsse_command_in, NULL, 0, 0, cmd_data->command_in_packet,
1229
0
                                                    "Bad Command 0x%02x in: %" PRIu32, cmd_data->cmd, cmd_data->command_in_packet);
1230
0
            proto_item_set_generated(command_in);
1231
0
            if (cmd_data->is_response_set)
1232
0
            {
1233
0
                proto_item *response_in;
1234
1235
0
                response_in = proto_tree_add_uint(expert_tree, hf_mpsse_response_in, NULL, 0, 0, cmd_data->response_in_packet);
1236
0
                proto_item_set_generated(response_in);
1237
0
            }
1238
0
        }
1239
1240
0
        if (!found)
1241
0
        {
1242
0
            *need_reassembly = request_reassembly;
1243
0
            return offset - offset_start;
1244
0
        }
1245
0
    }
1246
1247
0
    if (tvb_reported_length_remaining(tvb, offset) < cmd_data->response_length)
1248
0
    {
1249
0
        *need_reassembly = true;
1250
0
        return 0;
1251
0
    }
1252
1253
0
    rsp_data = proto_tree_add_bytes_format(tree, hf_mpsse_response, tvb, offset, cmd_data->response_length, NULL, "%s", cmd_str);
1254
0
    rsp_tree = proto_item_add_subtree(rsp_data, ett_mpsse_response_data);
1255
1256
0
    command_in = proto_tree_add_uint_format(rsp_tree, hf_mpsse_command_in, NULL, 0, 0, cmd_data->command_in_packet,
1257
0
                                            "Command 0x%02x in: %" PRIu32, cmd_data->cmd, cmd_data->command_in_packet);
1258
0
    proto_item_set_generated(command_in);
1259
1260
0
    offset += dissect_response_data(tvb, pinfo, rsp_tree, offset, cmd_data);
1261
1262
0
    return offset - offset_start;
1263
0
}
1264
1265
static int
1266
dissect_ftdi_mpsse(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1267
0
{
1268
0
    bool               need_reassembly = false;
1269
0
    ftdi_mpsse_info_t *mpsse_info = (ftdi_mpsse_info_t *)data;
1270
0
    int                offset = 0;
1271
0
    proto_item        *main_item;
1272
0
    proto_tree        *main_tree;
1273
1274
0
    if (!mpsse_info)
1275
0
    {
1276
0
        return offset;
1277
0
    }
1278
1279
0
    main_item = proto_tree_add_item(tree, proto_ftdi_mpsse, tvb, offset, -1, ENC_NA);
1280
0
    main_tree = proto_item_add_subtree(main_item, ett_ftdi_mpsse);
1281
1282
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "FTDI MPSSE");
1283
1284
0
    if (pinfo->p2p_dir == P2P_DIR_SENT)
1285
0
    {
1286
0
        command_data_t *iter = get_recorded_command_data(tx_command_info, pinfo, mpsse_info);
1287
1288
0
        if (!pinfo->fd->visited)
1289
0
        {
1290
            /* Not visited yet - advance iterator to last element */
1291
0
            while (iter && iter->next)
1292
0
            {
1293
0
                DISSECTOR_ASSERT(!iter->preliminary);
1294
0
                iter = iter->next;
1295
0
            }
1296
0
        }
1297
1298
0
        while ((tvb_reported_length_remaining(tvb, offset) > 0) && (!need_reassembly))
1299
0
        {
1300
0
            offset += dissect_command(tvb, pinfo, main_tree, offset, &need_reassembly, mpsse_info, &iter);
1301
0
        }
1302
0
    }
1303
0
    else if (pinfo->p2p_dir == P2P_DIR_RECV)
1304
0
    {
1305
0
        command_data_t *head = get_recorded_command_data(rx_command_info, pinfo, mpsse_info);
1306
0
        command_data_t *iter = head;
1307
1308
0
        if (!pinfo->fd->visited)
1309
0
        {
1310
0
            while (iter && iter->is_response_set)
1311
0
            {
1312
0
                iter = iter->next;
1313
0
            }
1314
1315
0
            if (iter != head)
1316
0
            {
1317
0
                insert_command_data_pointer(rx_command_info, pinfo, mpsse_info, iter);
1318
0
            }
1319
0
        }
1320
1321
0
        while ((tvb_reported_length_remaining(tvb, offset) > 0) && (!need_reassembly))
1322
0
        {
1323
0
            if (!iter)
1324
0
            {
1325
0
                proto_tree_add_expert(main_tree, pinfo, &ei_response_without_command, tvb, offset, -1);
1326
0
                offset += tvb_reported_length_remaining(tvb, offset);
1327
0
            }
1328
0
            else
1329
0
            {
1330
0
                offset += dissect_response(tvb, pinfo, main_tree, offset, &need_reassembly, iter);
1331
0
                iter = iter->next;
1332
0
            }
1333
0
        }
1334
0
    }
1335
1336
0
    if (need_reassembly)
1337
0
    {
1338
0
        if (pinfo->can_desegment)
1339
0
        {
1340
0
            pinfo->desegment_offset = offset;
1341
0
            pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
1342
0
        }
1343
0
        else
1344
0
        {
1345
0
            proto_tree_add_expert(main_tree, pinfo, &ei_reassembly_unavailable, tvb, offset, -1);
1346
0
        }
1347
0
        offset += tvb_reported_length_remaining(tvb, offset);
1348
0
    }
1349
1350
0
    if (tvb_reported_length_remaining(tvb, offset) > 0)
1351
0
    {
1352
0
        proto_tree_add_expert(main_tree, pinfo, &ei_undecoded, tvb, offset, -1);
1353
0
    }
1354
1355
0
    return tvb_reported_length(tvb);
1356
0
}
1357
1358
void
1359
proto_register_ftdi_mpsse(void)
1360
14
{
1361
14
    expert_module_t  *expert_module;
1362
1363
14
    static hf_register_info hf[] = {
1364
14
        { &hf_mpsse_command,
1365
14
          { "Command", "ftdi-mpsse.command",
1366
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1367
14
            NULL, HFILL }
1368
14
        },
1369
14
        { &hf_mpsse_command_b0,
1370
14
          { "-ve CLK on write", "ftdi-mpsse.command.b0",
1371
14
            FT_BOOLEAN, 8, NULL, (1 << 0),
1372
14
            NULL, HFILL }
1373
14
        },
1374
14
        { &hf_mpsse_command_b1,
1375
14
          { "Mode", "ftdi-mpsse.command.b1",
1376
14
            FT_UINT8, BASE_DEC, VALS(data_shifting_command_b1_vals), (1 << 1),
1377
14
            NULL, HFILL }
1378
14
        },
1379
14
        { &hf_mpsse_command_b2,
1380
14
          { "-ve CLK on read", "ftdi-mpsse.command.b2",
1381
14
            FT_BOOLEAN, 8, NULL, (1 << 2),
1382
14
            NULL, HFILL }
1383
14
        },
1384
14
        { &hf_mpsse_command_b3,
1385
14
          { "Endianness", "ftdi-mpsse.command.b3",
1386
14
            FT_UINT8, BASE_DEC, VALS(data_shifting_command_b3_vals), (1 << 3),
1387
14
            NULL, HFILL }
1388
14
        },
1389
14
        { &hf_mpsse_command_b4,
1390
14
          { "Do write TDI", "ftdi-mpsse.command.b4",
1391
14
            FT_BOOLEAN, 8, NULL, (1 << 4),
1392
14
            NULL, HFILL }
1393
14
        },
1394
14
        { &hf_mpsse_command_b5,
1395
14
          { "Do read TDO", "ftdi-mpsse.command.b5",
1396
14
            FT_BOOLEAN, 8, NULL, (1 << 5),
1397
14
            NULL, HFILL }
1398
14
        },
1399
14
        { &hf_mpsse_command_b6,
1400
14
          { "Do write TMS", "ftdi-mpsse.command.b6",
1401
14
            FT_BOOLEAN, 8, NULL, (1 << 6),
1402
14
            NULL, HFILL }
1403
14
        },
1404
14
        { &hf_mpsse_command_b7,
1405
14
          { "Type", "ftdi-mpsse.command.b7",
1406
14
            FT_UINT8, BASE_DEC, VALS(command_b7_vals), (1 << 7),
1407
14
            NULL, HFILL }
1408
14
        },
1409
14
        { &hf_mpsse_command_with_parameters,
1410
14
          { "Command with parameters", "ftdi-mpsse.command_with_parameters",
1411
14
            FT_BYTES, BASE_NONE, NULL, 0x0,
1412
14
            "Command including optional parameter bytes", HFILL }
1413
14
        },
1414
14
        { &hf_mpsse_bad_command_error,
1415
14
          { "Error code", "ftdi-mpsse.bad_command.error",
1416
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1417
14
            "Bad Command error code 0xFA", HFILL }
1418
14
        },
1419
14
        { &hf_mpsse_bad_command_code,
1420
14
          { "Received invalid command", "ftdi-mpsse.bad_command.command",
1421
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1422
14
            "Byte which caused the bad command", HFILL }
1423
14
        },
1424
14
        { &hf_mpsse_response,
1425
14
          { "Command response data", "ftdi-mpsse.response",
1426
14
            FT_BYTES, BASE_NONE, NULL, 0x0,
1427
14
            NULL, HFILL }
1428
14
        },
1429
14
        { &hf_mpsse_command_in,
1430
14
          { "Command in", "ftdi-mpsse.command.in",
1431
14
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
1432
14
            NULL, HFILL }
1433
14
        },
1434
14
        { &hf_mpsse_response_in,
1435
14
          { "Response in", "ftdi-mpsse.response.in",
1436
14
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
1437
14
            NULL, HFILL }
1438
14
        },
1439
14
        { &hf_mpsse_length_uint8,
1440
14
          { "Length", "ftdi-mpsse.length",
1441
14
            FT_UINT8, BASE_DEC, NULL, 0x0,
1442
14
            NULL, HFILL }
1443
14
        },
1444
14
        { &hf_mpsse_length_uint16,
1445
14
          { "Length", "ftdi-mpsse.length",
1446
14
            FT_UINT16, BASE_DEC, NULL, 0x0,
1447
14
            NULL, HFILL }
1448
14
        },
1449
14
        { &hf_mpsse_bytes_out,
1450
14
          { "Bytes out", "ftdi-mpsse.bytes_out",
1451
14
            FT_BYTES, BASE_NONE, NULL, 0x0,
1452
14
            NULL, HFILL }
1453
14
        },
1454
14
        { &hf_mpsse_bytes_in,
1455
14
          { "Bytes in", "ftdi-mpsse.bytes_in",
1456
14
            FT_BYTES, BASE_NONE, NULL, 0x0,
1457
14
            NULL, HFILL }
1458
14
        },
1459
14
        { &hf_mpsse_bits_out,
1460
14
          { "Bits out", "ftdi-mpsse.bits_out",
1461
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1462
14
            NULL, HFILL }
1463
14
        },
1464
14
        { &hf_mpsse_bits_in,
1465
14
          { "Bits in", "ftdi-mpsse.bits_in",
1466
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1467
14
            NULL, HFILL }
1468
14
        },
1469
14
        { &hf_mpsse_value,
1470
14
          { "Value", "ftdi-mpsse.value",
1471
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1472
14
            NULL, HFILL }
1473
14
        },
1474
14
        { &hf_mpsse_value_b0,
1475
14
          { "Bit 0", "ftdi-mpsse.value.b0",
1476
14
            FT_UINT8, BASE_DEC, NULL, (1 << 0),
1477
14
            NULL, HFILL }
1478
14
        },
1479
14
        { &hf_mpsse_value_b1,
1480
14
          { "Bit 1", "ftdi-mpsse.value.b1",
1481
14
            FT_UINT8, BASE_DEC, NULL, (1 << 1),
1482
14
            NULL, HFILL }
1483
14
        },
1484
14
        { &hf_mpsse_value_b2,
1485
14
          { "Bit 2", "ftdi-mpsse.value.b2",
1486
14
            FT_UINT8, BASE_DEC, NULL, (1 << 2),
1487
14
            NULL, HFILL }
1488
14
        },
1489
14
        { &hf_mpsse_value_b3,
1490
14
          { "Bit 3", "ftdi-mpsse.value.b3",
1491
14
            FT_UINT8, BASE_DEC, NULL, (1 << 3),
1492
14
            NULL, HFILL }
1493
14
        },
1494
14
        { &hf_mpsse_value_b4,
1495
14
          { "Bit 4", "ftdi-mpsse.value.b4",
1496
14
            FT_UINT8, BASE_DEC, NULL, (1 << 4),
1497
14
            NULL, HFILL }
1498
14
        },
1499
14
        { &hf_mpsse_value_b5,
1500
14
          { "Bit 5", "ftdi-mpsse.value.b5",
1501
14
            FT_UINT8, BASE_DEC, NULL, (1 << 5),
1502
14
            NULL, HFILL }
1503
14
        },
1504
14
        { &hf_mpsse_value_b6,
1505
14
          { "Bit 6", "ftdi-mpsse.value.b6",
1506
14
            FT_UINT8, BASE_DEC, NULL, (1 << 6),
1507
14
            NULL, HFILL }
1508
14
        },
1509
14
        { &hf_mpsse_value_b7,
1510
14
          { "Bit 7", "ftdi-mpsse.value.b7",
1511
14
            FT_UINT8, BASE_DEC, NULL, (1 << 7),
1512
14
            NULL, HFILL }
1513
14
        },
1514
14
        { &hf_mpsse_direction,
1515
14
          { "Direction", "ftdi-mpsse.direction",
1516
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1517
14
            NULL, HFILL }
1518
14
        },
1519
14
        { &hf_mpsse_direction_b0,
1520
14
          { "Bit 0", "ftdi-mpsse.direction.b0",
1521
14
            FT_UINT8, BASE_DEC, NULL, (1 << 0),
1522
14
            NULL, HFILL }
1523
14
        },
1524
14
        { &hf_mpsse_direction_b1,
1525
14
          { "Bit 1", "ftdi-mpsse.direction.b1",
1526
14
            FT_UINT8, BASE_DEC, NULL, (1 << 1),
1527
14
            NULL, HFILL }
1528
14
        },
1529
14
        { &hf_mpsse_direction_b2,
1530
14
          { "Bit 2", "ftdi-mpsse.direction.b2",
1531
14
            FT_UINT8, BASE_DEC, NULL, (1 << 2),
1532
14
            NULL, HFILL }
1533
14
        },
1534
14
        { &hf_mpsse_direction_b3,
1535
14
          { "Bit 3", "ftdi-mpsse.direction.b3",
1536
14
            FT_UINT8, BASE_DEC, NULL, (1 << 3),
1537
14
            NULL, HFILL }
1538
14
        },
1539
14
        { &hf_mpsse_direction_b4,
1540
14
          { "Bit 4", "ftdi-mpsse.direction.b4",
1541
14
            FT_UINT8, BASE_DEC, NULL, (1 << 4),
1542
14
            NULL, HFILL }
1543
14
        },
1544
14
        { &hf_mpsse_direction_b5,
1545
14
          { "Bit 5", "ftdi-mpsse.direction.b5",
1546
14
            FT_UINT8, BASE_DEC, NULL, (1 << 5),
1547
14
            NULL, HFILL }
1548
14
        },
1549
14
        { &hf_mpsse_direction_b6,
1550
14
          { "Bit 6", "ftdi-mpsse.direction.b6",
1551
14
            FT_UINT8, BASE_DEC, NULL, (1 << 6),
1552
14
            NULL, HFILL }
1553
14
        },
1554
14
        { &hf_mpsse_direction_b7,
1555
14
          { "Bit 7", "ftdi-mpsse.direction.b7",
1556
14
            FT_UINT8, BASE_DEC, NULL, (1 << 7),
1557
14
            NULL, HFILL }
1558
14
        },
1559
14
        { &hf_mpsse_cpumode_address_short,
1560
14
          { "Address", "ftdi-mpsse.cpumode_address",
1561
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1562
14
            "CPUMode Short Address", HFILL }
1563
14
        },
1564
14
        { &hf_mpsse_cpumode_address_extended,
1565
14
          { "Address", "ftdi-mpsse.cpumode_address",
1566
14
            FT_UINT16, BASE_HEX, NULL, 0x0,
1567
14
            "CPUMode Extended Address", HFILL }
1568
14
        },
1569
14
        { &hf_mpsse_cpumode_data,
1570
14
          { "Data", "ftdi-mpsse.cpumode_data",
1571
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1572
14
            NULL, HFILL }
1573
14
        },
1574
14
        { &hf_mpsse_clk_divisor,
1575
14
          { "Divisor", "ftdi-mpsse.clk_divisor",
1576
14
            FT_UINT16, BASE_HEX, NULL, 0x0,
1577
14
            NULL, HFILL }
1578
14
        },
1579
14
        { &hf_mpsse_open_drain_enable_low,
1580
14
          { "Low Byte", "ftdi-mpsse.open_drain_enable_low",
1581
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1582
14
            NULL, HFILL }
1583
14
        },
1584
14
        { &hf_mpsse_open_drain_enable_low_b0,
1585
14
          { "Bit 0", "ftdi-mpsse.open_drain_enable_low.b0",
1586
14
            FT_UINT8, BASE_DEC, NULL, (1 << 0),
1587
14
            NULL, HFILL }
1588
14
        },
1589
14
        { &hf_mpsse_open_drain_enable_low_b1,
1590
14
          { "Bit 1", "ftdi-mpsse.open_drain_enable_low.b1",
1591
14
            FT_UINT8, BASE_DEC, NULL, (1 << 1),
1592
14
            NULL, HFILL }
1593
14
        },
1594
14
        { &hf_mpsse_open_drain_enable_low_b2,
1595
14
          { "Bit 2", "ftdi-mpsse.open_drain_enable_low.b2",
1596
14
            FT_UINT8, BASE_DEC, NULL, (1 << 2),
1597
14
            NULL, HFILL }
1598
14
        },
1599
14
        { &hf_mpsse_open_drain_enable_low_b3,
1600
14
          { "Bit 3", "ftdi-mpsse.open_drain_enable_low.b3",
1601
14
            FT_UINT8, BASE_DEC, NULL, (1 << 3),
1602
14
            NULL, HFILL }
1603
14
        },
1604
14
        { &hf_mpsse_open_drain_enable_low_b4,
1605
14
          { "Bit 4", "ftdi-mpsse.open_drain_enable_low.b4",
1606
14
            FT_UINT8, BASE_DEC, NULL, (1 << 4),
1607
14
            NULL, HFILL }
1608
14
        },
1609
14
        { &hf_mpsse_open_drain_enable_low_b5,
1610
14
          { "Bit 5", "ftdi-mpsse.open_drain_enable_low.b5",
1611
14
            FT_UINT8, BASE_DEC, NULL, (1 << 5),
1612
14
            NULL, HFILL }
1613
14
        },
1614
14
        { &hf_mpsse_open_drain_enable_low_b6,
1615
14
          { "Bit 6", "ftdi-mpsse.open_drain_enable_low.b6",
1616
14
            FT_UINT8, BASE_DEC, NULL, (1 << 6),
1617
14
            NULL, HFILL }
1618
14
        },
1619
14
        { &hf_mpsse_open_drain_enable_low_b7,
1620
14
          { "Bit 7", "ftdi-mpsse.open_drain_enable_low.b7",
1621
14
            FT_UINT8, BASE_DEC, NULL, (1 << 7),
1622
14
            NULL, HFILL }
1623
14
        },
1624
14
        { &hf_mpsse_open_drain_enable_high,
1625
14
          { "High Byte", "ftdi-mpsse.open_drain_enable_high",
1626
14
            FT_UINT8, BASE_HEX, NULL, 0x0,
1627
14
            NULL, HFILL }
1628
14
        },
1629
14
        { &hf_mpsse_open_drain_enable_high_b0,
1630
14
          { "Bit 0", "ftdi-mpsse.open_drain_enable_high.b0",
1631
14
            FT_UINT8, BASE_DEC, NULL, (1 << 0),
1632
14
            NULL, HFILL }
1633
14
        },
1634
14
        { &hf_mpsse_open_drain_enable_high_b1,
1635
14
          { "Bit 1", "ftdi-mpsse.open_drain_enable_high.b1",
1636
14
            FT_UINT8, BASE_DEC, NULL, (1 << 1),
1637
14
            NULL, HFILL }
1638
14
        },
1639
14
        { &hf_mpsse_open_drain_enable_high_b2,
1640
14
          { "Bit 2", "ftdi-mpsse.open_drain_enable_high.b2",
1641
14
            FT_UINT8, BASE_DEC, NULL, (1 << 2),
1642
14
            NULL, HFILL }
1643
14
        },
1644
14
        { &hf_mpsse_open_drain_enable_high_b3,
1645
14
          { "Bit 3", "ftdi-mpsse.open_drain_enable_high.b3",
1646
14
            FT_UINT8, BASE_DEC, NULL, (1 << 3),
1647
14
            NULL, HFILL }
1648
14
        },
1649
14
        { &hf_mpsse_open_drain_enable_high_b4,
1650
14
          { "Bit 4", "ftdi-mpsse.open_drain_enable_high.b4",
1651
14
            FT_UINT8, BASE_DEC, NULL, (1 << 4),
1652
14
            NULL, HFILL }
1653
14
        },
1654
14
        { &hf_mpsse_open_drain_enable_high_b5,
1655
14
          { "Bit 5", "ftdi-mpsse.open_drain_enable_high.b5",
1656
14
            FT_UINT8, BASE_DEC, NULL, (1 << 5),
1657
14
            NULL, HFILL }
1658
14
        },
1659
14
        { &hf_mpsse_open_drain_enable_high_b6,
1660
14
          { "Bit 6", "ftdi-mpsse.open_drain_enable_high.b6",
1661
14
            FT_UINT8, BASE_DEC, NULL, (1 << 6),
1662
14
            NULL, HFILL }
1663
14
        },
1664
14
        { &hf_mpsse_open_drain_enable_high_b7,
1665
14
          { "Bit 7", "ftdi-mpsse.open_drain_enable_high.b7",
1666
14
            FT_UINT8, BASE_DEC, NULL, (1 << 7),
1667
14
            NULL, HFILL }
1668
14
        },
1669
14
    };
1670
1671
14
    static ei_register_info ei[] = {
1672
14
        { &ei_undecoded, { "ftdi-mpsse.undecoded", PI_UNDECODED, PI_WARN, "Not dissected yet (report to wireshark.org)", EXPFILL }},
1673
14
        { &ei_response_without_command, { "ftdi-mpsse.response_without_command", PI_PROTOCOL, PI_ERROR, "Unable to associate response with command (response without command?)", EXPFILL }},
1674
14
        { &ei_skipped_response_data, { "ftdi-mpsse.skipped_response_data", PI_PROTOCOL, PI_WARN, "Skipped response data while looking for Bad Command response", EXPFILL }},
1675
14
        { &ei_reassembly_unavailable, { "ftdi-mpsse.reassembly_unavailable", PI_UNDECODED, PI_ERROR, "Data source dissector does not support reassembly. Dissection will get out of sync.", EXPFILL }},
1676
14
    };
1677
1678
14
    static int *ett[] = {
1679
14
        &ett_ftdi_mpsse,
1680
14
        &ett_mpsse_command,
1681
14
        &ett_mpsse_command_with_parameters,
1682
14
        &ett_mpsse_response_data,
1683
14
        &ett_mpsse_value,
1684
14
        &ett_mpsse_direction,
1685
14
        &ett_mpsse_open_drain_enable,
1686
14
        &ett_mpsse_skipped_response_data,
1687
14
    };
1688
1689
14
    rx_command_info = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
1690
14
    tx_command_info = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
1691
1692
14
    proto_ftdi_mpsse = proto_register_protocol("FTDI Multi-Protocol Synchronous Serial Engine", "FTDI MPSSE", "ftdi-mpsse");
1693
14
    proto_register_field_array(proto_ftdi_mpsse, hf, array_length(hf));
1694
14
    proto_register_subtree_array(ett, array_length(ett));
1695
14
    ftdi_mpsse_handle = register_dissector("ftdi-mpsse", dissect_ftdi_mpsse, proto_ftdi_mpsse);
1696
1697
14
    expert_module = expert_register_protocol(proto_ftdi_mpsse);
1698
14
    expert_register_field_array(expert_module, ei, array_length(ei));
1699
14
}
1700
1701
/*
1702
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1703
 *
1704
 * Local variables:
1705
 * c-basic-offset: 4
1706
 * tab-width: 8
1707
 * indent-tabs-mode: nil
1708
 * End:
1709
 *
1710
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1711
 * :indentSize=4:tabSize=8:noTabs=true:
1712
 */