Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-ftdi-mpsse.c
blob7a91e1d1e83aa19a345e778afd4daa4070973359
1 /* packet-ftdi-mpsse.c
2 * Routines for FTDI Multi-Protocol Synchronous Serial Engine dissection
4 * Copyright 2020 Tomasz Mon
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include "config.h"
15 #include <epan/packet.h>
16 #include <epan/expert.h>
17 #include <wsutil/str_util.h>
18 #include "packet-ftdi-ft.h"
20 static int proto_ftdi_mpsse;
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;
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;
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;
99 static dissector_handle_t ftdi_mpsse_handle;
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.
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.
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.
122 static wmem_tree_t *rx_command_info;
123 static wmem_tree_t *tx_command_info;
125 typedef struct _command_data command_data_t;
127 struct _command_data {
128 ftdi_mpsse_info_t mpsse_info;
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;
139 command_data_t *next;
142 void proto_register_ftdi_mpsse(void);
144 #define BAD_COMMAND_SYNC_CODE 0xFA
146 #define CMD_SET_DATA_BITS_LOW_BYTE 0x80
147 #define CMD_READ_DATA_BITS_LOW_BYTE 0x81
148 #define CMD_SET_DATA_BITS_HIGH_BYTE 0x82
149 #define CMD_READ_DATA_BITS_HIGH_BYTE 0x83
150 #define CMD_CLOCK_SET_DIVISOR 0x86
151 #define CMD_CLOCK_N_BITS 0x8E
152 #define CMD_CLOCK_N_TIMES_8_BITS 0x8F
153 #define CMD_CPUMODE_READ_SHORT_ADDR 0x90
154 #define CMD_CPUMODE_READ_EXT_ADDR 0x91
155 #define CMD_CPUMODE_WRITE_SHORT_ADDR 0x92
156 #define CMD_CPUMODE_WRITE_EXT_ADDR 0x93
157 #define CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_HIGH 0x9C
158 #define CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_LOW 0x9D
159 #define CMD_IO_OPEN_DRAIN_ENABLE 0x9E
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}
205 static value_string_ext command_vals_ext = VALUE_STRING_EXT_INIT(command_vals);
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}
214 static value_string_ext cpumode_command_vals_ext = VALUE_STRING_EXT_INIT(cpumode_command_vals);
216 static const value_string ft2232d_only_command_vals[] = {
217 {CMD_CLOCK_SET_DIVISOR, "Set TCK/SK Divisor"},
218 {0, NULL}
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}
238 static value_string_ext h_only_command_vals_ext = VALUE_STRING_EXT_INIT(h_only_command_vals);
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}
245 static const value_string data_shifting_command_b1_vals[] = {
246 {0, "Byte mode"},
247 {1, "Bit mode"},
248 {0, NULL}
251 static const value_string data_shifting_command_b3_vals[] = {
252 {0, "MSB first"},
253 {1, "LSB first"},
254 {0, NULL}
257 static const value_string command_b7_vals[] = {
258 {0, "Data Shifting Command"},
259 {1, "Other (Not Data Shifting) Command"},
260 {0, NULL}
263 #define IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd) ((cmd & (1u << 7)) == 0)
264 #define IS_DATA_SHIFTING_BYTE_MODE(cmd) ((cmd & (1u << 1)) == 0)
265 #define IS_DATA_SHIFTING_MSB_FIRST(cmd) ((cmd & (1u << 3)) == 0)
266 #define IS_DATA_SHIFTING_WRITING_TDI(cmd) (cmd & (1u << 4))
267 #define IS_DATA_SHIFTING_READING_TDO(cmd) (cmd & (1u << 5))
268 #define IS_DATA_SHIFTING_WRITING_TMS(cmd) (cmd & (1u << 6))
270 static bool is_data_shifting_command(uint8_t cmd)
272 switch (cmd)
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.
277 * Commands listed below (with bit 7 clear) trigger BadCommand response on FT2232L, FT232H and FT2232H
279 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 case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49:
281 case 0x4C: case 0x4D:
282 case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59:
283 case 0x5C: case 0x5D:
284 case 0x60: case 0x61:
285 case 0x64: case 0x65:
286 case 0x68: case 0x69:
287 case 0x6C: case 0x6D:
288 case 0x70: case 0x71:
289 case 0x74: case 0x75:
290 case 0x78: case 0x79:
291 case 0x7C: case 0x7D:
292 return false;
293 default:
294 return IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd);
298 static bool is_data_shifting_command_returning_response(uint8_t cmd, ftdi_mpsse_info_t *mpsse_info)
300 DISSECTOR_ASSERT(is_data_shifting_command(cmd));
301 if (mpsse_info->mcu_mode)
303 /* MCU mode seems to consume data shifting payloads but do not actually return any response data */
304 return false;
307 return IS_DATA_SHIFTING_READING_TDO(cmd) ? true : false;
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)
314 const char *str;
316 /* First, try commands that are common on all chips */
317 str = try_val_to_str_ext(cmd, &command_vals_ext);
318 if (str)
320 return str;
323 if (is_data_shifting_command(cmd))
325 return "Undocumented Data Shifting Command";
328 /* Check chip specific commands */
329 switch (mpsse_info->chip)
331 case FTDI_CHIP_FT2232D:
332 str = try_val_to_str(cmd, ft2232d_only_command_vals);
333 break;
334 case FTDI_CHIP_FT232H:
335 str = try_val_to_str(cmd, ft232h_only_command_vals);
336 if (str)
338 break;
340 /* Fallthrough */
341 case FTDI_CHIP_FT2232H:
342 case FTDI_CHIP_FT4232H:
343 str = try_val_to_str_ext(cmd, &h_only_command_vals_ext);
344 break;
345 default:
346 break;
349 if (!str && mpsse_info->mcu_mode)
351 str = try_val_to_str_ext(cmd, &cpumode_command_vals_ext);
354 return str;
357 static bool is_valid_command(uint8_t cmd, ftdi_mpsse_info_t *mpsse_info)
359 return get_command_string(cmd, mpsse_info) != NULL;
362 static bool is_same_mpsse_instance(ftdi_mpsse_info_t *info1, ftdi_mpsse_info_t *info2)
364 return (info1->bus_id == info2->bus_id) &&
365 (info1->device_address == info2->device_address) &&
366 (info1->chip == info2->chip) &&
367 (info1->iface == info2->iface) &&
368 (info1->mcu_mode == info2->mcu_mode);
371 static command_data_t *
372 get_recorded_command_data(wmem_tree_t *command_tree, packet_info *pinfo, ftdi_mpsse_info_t *mpsse_info)
374 uint32_t k_bus_id = mpsse_info->bus_id;
375 uint32_t k_device_address = mpsse_info->device_address;
376 uint32_t k_chip = (uint32_t)mpsse_info->chip;
377 uint32_t k_interface = (uint32_t)mpsse_info->iface;
378 uint32_t k_mcu_mode = mpsse_info->mcu_mode;
379 wmem_tree_key_t key[] = {
380 {1, &k_bus_id},
381 {1, &k_device_address},
382 {1, &k_chip},
383 {1, &k_interface},
384 {1, &k_mcu_mode},
385 {1, &pinfo->num},
386 {0, NULL}
389 command_data_t *data = NULL;
390 data = (command_data_t *)wmem_tree_lookup32_array_le(command_tree, key);
391 if (data && is_same_mpsse_instance(mpsse_info, &data->mpsse_info))
393 return data;
396 return NULL;
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)
402 uint32_t k_bus_id = mpsse_info->bus_id;
403 uint32_t k_device_address = mpsse_info->device_address;
404 uint32_t k_chip = (uint32_t)mpsse_info->chip;
405 uint32_t k_interface = (uint32_t)mpsse_info->iface;
406 uint32_t k_mcu_mode = mpsse_info->mcu_mode;
407 wmem_tree_key_t key[] = {
408 {1, &k_bus_id},
409 {1, &k_device_address},
410 {1, &k_chip},
411 {1, &k_interface},
412 {1, &k_mcu_mode},
413 {1, &pinfo->num},
414 {0, NULL}
417 wmem_tree_insert32_array(command_tree, key, data);
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)
424 command_data_t *data = *cmd_data;
426 DISSECTOR_ASSERT(response_length > 0);
428 if (data && data->preliminary)
430 DISSECTOR_ASSERT(data->cmd == cmd);
431 DISSECTOR_ASSERT(data->response_length == response_length);
432 data->command_in_packet = pinfo->num;
433 data->preliminary = preliminary;
434 return;
437 data = wmem_new(wmem_file_scope(), command_data_t);
438 memcpy(&data->mpsse_info, mpsse_info, sizeof(ftdi_mpsse_info_t));
439 data->preliminary = preliminary;
440 data->is_response_set = false;
441 data->cmd = cmd;
442 data->response_length = response_length;
443 data->command_in_packet = pinfo->num;
444 data->response_in_packet = 0;
445 data->next = NULL;
447 if (*cmd_data && (!(*cmd_data)->is_response_set))
449 DISSECTOR_ASSERT((*cmd_data)->next == NULL);
450 (*cmd_data)->next = data;
451 if ((*cmd_data)->command_in_packet != pinfo->num)
453 insert_command_data_pointer(tx_command_info, pinfo, mpsse_info, data);
456 else
458 insert_command_data_pointer(rx_command_info, pinfo, mpsse_info, data);
459 insert_command_data_pointer(tx_command_info, pinfo, mpsse_info, data);
461 *cmd_data = data;
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)
467 if (pinfo->fd->visited)
469 DISSECTOR_ASSERT(*cmd_data);
470 DISSECTOR_ASSERT((*cmd_data)->cmd == cmd);
471 DISSECTOR_ASSERT((*cmd_data)->response_length == response_length);
472 if ((*cmd_data)->is_response_set)
474 proto_tree *response_in = proto_tree_add_uint(tree, hf_mpsse_response_in, NULL, 0, 0, (*cmd_data)->response_in_packet);
475 proto_item_set_generated(response_in);
476 DISSECTOR_ASSERT((*cmd_data)->command_in_packet == pinfo->num);
478 *cmd_data = (*cmd_data)->next;
480 else
482 record_command_data(cmd_data, pinfo, mpsse_info, cmd, response_length, false);
486 static char* freq_to_str(float freq)
488 if (freq < 1e3)
490 return ws_strdup_printf("%.12g Hz", freq);
492 else if (freq < 1e6)
494 return ws_strdup_printf("%.12g kHz", freq / 1e3);
496 else
498 return ws_strdup_printf("%.12g MHz", freq / 1e6);
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)
506 int offset_start = offset;
507 int32_t length;
509 DISSECTOR_ASSERT(is_data_shifting_command(cmd));
511 if (IS_DATA_SHIFTING_BYTE_MODE(cmd))
513 length = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN);
514 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 offset += 2;
517 if (IS_DATA_SHIFTING_WRITING_TDI(cmd))
519 proto_tree_add_item(tree, hf_mpsse_bytes_out, tvb, offset, length + 1, ENC_NA);
520 offset += length + 1;
523 else
525 length = tvb_get_uint8(tvb, offset);
526 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 offset += 1;
529 if (IS_DATA_SHIFTING_WRITING_TMS(cmd) && IS_DATA_SHIFTING_READING_TDO(cmd) && IS_DATA_SHIFTING_MSB_FIRST(cmd))
531 /* These undocumented commands do not seem to consume the data byte, only the length */
533 else if (IS_DATA_SHIFTING_WRITING_TDI(cmd) || IS_DATA_SHIFTING_WRITING_TMS(cmd))
535 proto_tree_add_item(tree, hf_mpsse_bits_out, tvb, offset, 1, ENC_LITTLE_ENDIAN);
536 offset += 1;
540 if (is_data_shifting_command_returning_response(cmd, mpsse_info))
542 expect_response(cmd_data, pinfo, tree, mpsse_info, cmd, IS_DATA_SHIFTING_BYTE_MODE(cmd) ? length + 1 : 1);
545 return offset - offset_start;
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)
552 static const int *value_bits_hf[] = {
553 &hf_mpsse_value_b0,
554 &hf_mpsse_value_b1,
555 &hf_mpsse_value_b2,
556 &hf_mpsse_value_b3,
557 &hf_mpsse_value_b4,
558 &hf_mpsse_value_b5,
559 &hf_mpsse_value_b6,
560 &hf_mpsse_value_b7,
562 static const int *direction_bits_hf[] = {
563 &hf_mpsse_direction_b0,
564 &hf_mpsse_direction_b1,
565 &hf_mpsse_direction_b2,
566 &hf_mpsse_direction_b3,
567 &hf_mpsse_direction_b4,
568 &hf_mpsse_direction_b5,
569 &hf_mpsse_direction_b6,
570 &hf_mpsse_direction_b7,
572 uint32_t value, direction;
573 proto_item *item;
574 proto_item *value_item, *direction_item;
575 proto_tree *value_tree, *direction_tree;
576 unsigned bit;
578 value_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_value, tvb, offset, 1, ENC_LITTLE_ENDIAN, &value);
579 direction_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_direction, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN, &direction);
581 value_tree = proto_item_add_subtree(value_item, ett_mpsse_value);
582 for (bit = 0; bit < 8; bit++)
584 const char *state;
585 if ((1 << bit) & direction)
587 state = ((1 << bit) & value) ? "Output High" : "Output Low";
589 else
591 state = "N/A (Input)";
593 item = proto_tree_add_uint_format_value(value_tree, *value_bits_hf[bit], tvb, offset, 1, value, "%s", signal_names[bit]);
594 if (pin_prefix && (bit < num_pins))
596 proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
598 proto_item_append_text(item, " %s", state);
601 direction_tree = proto_item_add_subtree(direction_item, ett_mpsse_direction);
602 for (bit = 0; bit < 8; bit++)
604 const char *type = ((1 << bit) & direction) ? "Output" : "Input";
605 item = proto_tree_add_uint_format_value(direction_tree, *direction_bits_hf[bit], tvb, offset + 1, 1, direction, "%s", signal_names[bit]);
606 if (pin_prefix && (bit < num_pins))
608 proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
610 proto_item_append_text(item, " %s", type);
613 return 2;
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)
620 int offset_start = offset;
622 /* Address is either short (1 byte) or extended (2 bytes) */
623 if ((cmd == CMD_CPUMODE_READ_SHORT_ADDR) || (cmd == CMD_CPUMODE_WRITE_SHORT_ADDR))
625 proto_tree_add_item(tree, hf_mpsse_cpumode_address_short, tvb, offset, 1, ENC_BIG_ENDIAN);
626 offset += 1;
628 else if ((cmd == CMD_CPUMODE_READ_EXT_ADDR) || (cmd == CMD_CPUMODE_WRITE_EXT_ADDR))
630 proto_tree_add_item(tree, hf_mpsse_cpumode_address_extended, tvb, offset, 2, ENC_BIG_ENDIAN);
631 offset += 2;
634 /* Write commands have data parameter (1 byte) */
635 if ((cmd == CMD_CPUMODE_WRITE_SHORT_ADDR) || (cmd == CMD_CPUMODE_WRITE_EXT_ADDR))
637 proto_tree_add_item(tree, hf_mpsse_cpumode_data, tvb, offset, 1, ENC_LITTLE_ENDIAN);
638 offset += 1;
641 if ((cmd == CMD_CPUMODE_READ_SHORT_ADDR) || (cmd == CMD_CPUMODE_READ_EXT_ADDR))
643 expect_response(cmd_data, pinfo, tree, mpsse_info, cmd, 1);
646 return offset - offset_start;
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)
652 int offset_start = offset;
653 uint32_t value;
654 proto_item *item;
655 char *str_old, *str;
657 item = proto_tree_add_item_ret_uint(tree, hf_mpsse_clk_divisor, tvb, offset, 2, ENC_LITTLE_ENDIAN, &value);
658 offset += 2;
660 str_old = freq_to_str((float) 12e6 / ((1 + value) * 2));
661 str = freq_to_str((float) 60e6 / ((1 + value) * 2));
663 if (mpsse_info->chip == FTDI_CHIP_FT2232D)
665 proto_item_append_text(item, ", TCK/SK Max: %s", str_old);
667 else
669 proto_item_append_text(item, ", TCK Max: %s (60 MHz master clock) or %s (12 MHz master clock)", str, str_old);
672 g_free(str_old);
673 g_free(str);
675 return offset - offset_start;
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_)
681 uint32_t length = tvb_get_uint8(tvb, offset);
682 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 return 1;
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_)
689 uint32_t length = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN);
690 proto_tree_add_uint_format(tree, hf_mpsse_length_uint16, tvb, offset, 2, length, "Length: %d clocks", (length + 1) * 8);
691 return 2;
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])
697 static const char *low_byte_signal_names[8] = {
698 "TCK/SK",
699 "TDI/DO",
700 "TDO/DI",
701 "TMS/CS",
702 "GPIOL0",
703 "GPIOL1",
704 "GPIOL2",
705 "GPIOL3",
707 static const char *high_byte_signal_names[8] = {
708 "GPIOH0",
709 "GPIOH1",
710 "GPIOH2",
711 "GPIOH3",
712 "GPIOH4",
713 "GPIOH5",
714 "GPIOH6",
715 "GPIOH7",
718 *out_names = (is_high_byte) ? &high_byte_signal_names : &low_byte_signal_names;
720 /* Based on table from FTDI AN_108 chapter 2.1 Data bit Definition */
721 switch (mpsse_info->chip)
723 case FTDI_CHIP_FT2232D:
724 if (mpsse_info->iface == FTDI_INTERFACE_A)
726 *out_num_pins = (is_high_byte) ? 4 : 8;
727 return (is_high_byte) ? "ACBUS" : "ADBUS";
729 break;
730 case FTDI_CHIP_FT232H:
731 *out_num_pins = 8;
732 return (is_high_byte) ? "ACBUS" : "ADBUS";
733 case FTDI_CHIP_FT2232H:
734 if (mpsse_info->iface == FTDI_INTERFACE_A)
736 *out_num_pins = 8;
737 return (is_high_byte) ? "ACBUS" : "ADBUS";
739 else if (mpsse_info->iface == FTDI_INTERFACE_B)
741 *out_num_pins = 8;
742 return (is_high_byte) ? "BCBUS" : "BDBUS";
744 break;
745 case FTDI_CHIP_FT4232H:
746 if (!is_high_byte)
748 if (mpsse_info->iface == FTDI_INTERFACE_A)
750 *out_num_pins = 8;
751 return "ADBUS";
753 else if (mpsse_info->iface == FTDI_INTERFACE_B)
755 *out_num_pins = 8;
756 return "BDBUS";
759 break;
760 default:
761 break;
764 *out_num_pins = 0;
765 return NULL;
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_)
771 static const int *low_byte_bits_hf[] = {
772 &hf_mpsse_open_drain_enable_low_b0,
773 &hf_mpsse_open_drain_enable_low_b1,
774 &hf_mpsse_open_drain_enable_low_b2,
775 &hf_mpsse_open_drain_enable_low_b3,
776 &hf_mpsse_open_drain_enable_low_b4,
777 &hf_mpsse_open_drain_enable_low_b5,
778 &hf_mpsse_open_drain_enable_low_b6,
779 &hf_mpsse_open_drain_enable_low_b7,
781 static const int *high_byte_bits_hf[] = {
782 &hf_mpsse_open_drain_enable_high_b0,
783 &hf_mpsse_open_drain_enable_high_b1,
784 &hf_mpsse_open_drain_enable_high_b2,
785 &hf_mpsse_open_drain_enable_high_b3,
786 &hf_mpsse_open_drain_enable_high_b4,
787 &hf_mpsse_open_drain_enable_high_b5,
788 &hf_mpsse_open_drain_enable_high_b6,
789 &hf_mpsse_open_drain_enable_high_b7,
791 int offset_start = offset;
792 const char *pin_prefix = NULL;
793 unsigned num_pins = 0;
794 const char *(*signal_names)[8] = NULL;
795 uint32_t value;
796 proto_item *item;
797 proto_item *byte_item;
798 proto_tree *byte_tree;
799 unsigned bit;
801 pin_prefix = get_data_bit_pin_prefix(false, mpsse_info, &num_pins, &signal_names);
802 byte_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_open_drain_enable_low, tvb, offset, 1, ENC_LITTLE_ENDIAN, &value);
803 byte_tree = proto_item_add_subtree(byte_item, ett_mpsse_open_drain_enable);
804 for (bit = 0; bit < 8; bit++)
806 const char *output_type = ((1 << bit) & value) ? "Open-Drain" : "Push-Pull";
807 item = proto_tree_add_uint_format_value(byte_tree, *low_byte_bits_hf[bit], tvb, offset, 1, value, "%s", (*signal_names)[bit]);
808 if (pin_prefix && (bit < num_pins))
810 proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
812 proto_item_append_text(item, " %s", output_type);
814 offset++;
816 pin_prefix = get_data_bit_pin_prefix(true, mpsse_info, &num_pins, &signal_names);
817 byte_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_open_drain_enable_high, tvb, offset, 1, ENC_LITTLE_ENDIAN, &value);
818 byte_tree = proto_item_add_subtree(byte_item, ett_mpsse_open_drain_enable);
819 for (bit = 0; bit < 8; bit++)
821 const char *output_type = ((1 << bit) & value) ? "Open-Drain" : "Push-Pull";
822 item = proto_tree_add_uint_format_value(byte_tree, *high_byte_bits_hf[bit], tvb, offset, 1, value, "%s", (*signal_names)[bit]);
823 if (pin_prefix && (bit < num_pins))
825 proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
827 proto_item_append_text(item, " %s", output_type);
829 offset++;
831 return offset - offset_start;
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)
838 const char *pin_prefix = NULL;
839 unsigned num_pins = 0;
840 const char *(*signal_names)[8] = NULL;
842 DISSECTOR_ASSERT(!is_data_shifting_command(cmd) && is_valid_command(cmd, mpsse_info));
844 switch (cmd)
846 case CMD_SET_DATA_BITS_LOW_BYTE:
847 pin_prefix = get_data_bit_pin_prefix(false, mpsse_info, &num_pins, &signal_names);
848 return dissect_set_data_bits_parameters(cmd, tvb, pinfo, tree, offset, *signal_names, pin_prefix, num_pins);
849 case CMD_SET_DATA_BITS_HIGH_BYTE:
850 pin_prefix = get_data_bit_pin_prefix(true, mpsse_info, &num_pins, &signal_names);
851 return dissect_set_data_bits_parameters(cmd, tvb, pinfo, tree, offset, *signal_names, pin_prefix, num_pins);
852 case CMD_READ_DATA_BITS_LOW_BYTE:
853 case CMD_READ_DATA_BITS_HIGH_BYTE:
854 expect_response(cmd_data, pinfo, tree, mpsse_info, cmd, 1);
855 return 0;
856 case CMD_CPUMODE_READ_SHORT_ADDR:
857 case CMD_CPUMODE_READ_EXT_ADDR:
858 case CMD_CPUMODE_WRITE_SHORT_ADDR:
859 case CMD_CPUMODE_WRITE_EXT_ADDR:
860 return dissect_cpumode_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info, cmd_data);
861 case CMD_CLOCK_SET_DIVISOR:
862 return dissect_clock_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info);
863 case CMD_CLOCK_N_BITS:
864 return dissect_clock_n_bits_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info);
865 case CMD_CLOCK_N_TIMES_8_BITS:
866 case CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_HIGH:
867 case CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_LOW:
868 return dissect_clock_n_times_8_bits_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info);
869 case CMD_IO_OPEN_DRAIN_ENABLE:
870 return dissect_io_open_drain_enable_parameters(cmd, tvb, pinfo, tree, offset, mpsse_info);
871 default:
872 return 0;
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)
879 int parameters_length = 0;
881 if (!is_valid_command(cmd, mpsse_info))
883 return 0;
886 if (is_data_shifting_command(cmd))
888 int32_t data_length = 0;
889 if (IS_DATA_SHIFTING_BYTE_MODE(cmd))
891 parameters_length = 2;
892 if (IS_DATA_SHIFTING_WRITING_TDI(cmd))
894 if (tvb_reported_length_remaining(tvb, offset) >= 2)
896 data_length = (int32_t)tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN) + 1;
897 parameters_length += data_length;
899 /* else length is not available already so the caller will know that reassembly is needed */
902 else /* bit mode */
904 parameters_length = (IS_DATA_SHIFTING_WRITING_TDI(cmd) || IS_DATA_SHIFTING_WRITING_TMS(cmd)) ? 2 : 1;
905 data_length = 1;
906 if (IS_DATA_SHIFTING_WRITING_TMS(cmd) && IS_DATA_SHIFTING_READING_TDO(cmd) && IS_DATA_SHIFTING_MSB_FIRST(cmd))
908 /* These undocumented commands do not seem to consume the data byte, only the length */
909 parameters_length = 1;
913 if (!pinfo->fd->visited)
915 if (is_data_shifting_command_returning_response(cmd, mpsse_info) && data_length)
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.
922 record_command_data(cmd_data, pinfo, mpsse_info, cmd, data_length, true);
926 else
928 switch (cmd)
930 case CMD_CPUMODE_WRITE_EXT_ADDR:
931 parameters_length = 3;
932 break;
933 case CMD_SET_DATA_BITS_LOW_BYTE:
934 case CMD_SET_DATA_BITS_HIGH_BYTE:
935 case CMD_CPUMODE_READ_EXT_ADDR:
936 case CMD_CPUMODE_WRITE_SHORT_ADDR:
937 case CMD_CLOCK_SET_DIVISOR:
938 case 0x8F: case 0x9C: case 0x9D: case 0x9E:
939 parameters_length = 2;
940 break;
941 case CMD_CPUMODE_READ_SHORT_ADDR:
942 case 0x8E:
943 parameters_length = 1;
944 break;
945 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 parameters_length = 0;
947 break;
948 default:
949 DISSECTOR_ASSERT_NOT_REACHED();
953 return parameters_length;
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_)
959 proto_item *cmd_item;
960 proto_tree *cmd_tree;
961 int * const *cmd_bits;
962 static int * const data_shifting_cmd_bits[] = {
963 &hf_mpsse_command_b7,
964 &hf_mpsse_command_b6,
965 &hf_mpsse_command_b5,
966 &hf_mpsse_command_b4,
967 &hf_mpsse_command_b3,
968 &hf_mpsse_command_b2,
969 &hf_mpsse_command_b1,
970 &hf_mpsse_command_b0,
971 NULL
974 static int * const non_data_shifting_cmd_bits[] = {
975 &hf_mpsse_command_b7,
976 NULL
979 cmd_item = proto_tree_add_uint_format(tree, hf_mpsse_command, tvb, offset, 1, cmd, "Command: %s (0x%02x)", cmd_str, cmd);
980 cmd_tree = proto_item_add_subtree(cmd_item, ett_mpsse_command);
981 cmd_bits = IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd) ? data_shifting_cmd_bits : non_data_shifting_cmd_bits;
983 proto_tree_add_bitmask_list_value(cmd_tree, tvb, offset, 1, cmd_bits, cmd);
985 return cmd;
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)
992 uint8_t cmd;
993 const char *cmd_str;
994 int offset_start = offset;
995 int parameters_length;
996 int dissected;
997 proto_item *cmd_with_parameters;
998 proto_tree *cmd_tree;
1000 cmd = tvb_get_uint8(tvb, offset);
1001 cmd_str = get_command_string(cmd, mpsse_info);
1002 parameters_length = estimated_command_parameters_length(cmd, tvb, pinfo, offset + 1, mpsse_info, cmd_data);
1003 if (tvb_reported_length_remaining(tvb, offset + 1) < parameters_length)
1005 *need_reassembly = true;
1006 return 0;
1009 if (!cmd_str)
1011 cmd_str = "Bad Command";
1014 cmd_with_parameters = proto_tree_add_bytes_format(tree, hf_mpsse_command_with_parameters, tvb, offset, 1 + parameters_length, NULL, "%s", cmd_str);
1015 cmd_tree = proto_item_add_subtree(cmd_with_parameters, ett_mpsse_command_with_parameters);
1017 cmd = dissect_command_code(cmd, cmd_str, tvb, cmd_tree, offset, mpsse_info);
1018 offset += 1;
1020 *need_reassembly = false;
1021 if (is_valid_command(cmd, mpsse_info))
1023 if (IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd))
1025 dissected = dissect_data_shifting_command_parameters(cmd, tvb, pinfo, cmd_tree, offset, mpsse_info, cmd_data);
1026 DISSECTOR_ASSERT(dissected == parameters_length);
1027 offset += dissected;
1029 else
1031 dissected = dissect_non_data_shifting_command_parameters(cmd, tvb, pinfo, cmd_tree, offset, mpsse_info, cmd_data);
1032 if (parameters_length > dissected)
1034 proto_tree_add_expert(cmd_tree, pinfo, &ei_undecoded, tvb, offset + dissected, parameters_length - dissected);
1036 offset += parameters_length;
1039 else
1041 /* Expect Bad Command response */
1042 expect_response(cmd_data, pinfo, cmd_tree, mpsse_info, cmd, 2);
1045 return offset - offset_start;
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)
1053 static const int *value_bits_hf[] = {
1054 &hf_mpsse_value_b0,
1055 &hf_mpsse_value_b1,
1056 &hf_mpsse_value_b2,
1057 &hf_mpsse_value_b3,
1058 &hf_mpsse_value_b4,
1059 &hf_mpsse_value_b5,
1060 &hf_mpsse_value_b6,
1061 &hf_mpsse_value_b7,
1063 uint32_t value;
1064 proto_item *item;
1065 proto_item *value_item;
1066 proto_tree *value_tree;
1067 unsigned bit;
1069 value_item = proto_tree_add_item_ret_uint(tree, hf_mpsse_value, tvb, offset, 1, ENC_LITTLE_ENDIAN, &value);
1070 value_tree = proto_item_add_subtree(value_item, ett_mpsse_value);
1071 for (bit = 0; bit < 8; bit++)
1073 const char *state;
1074 state = ((1 << bit) & value) ? "High" : "Low";
1075 item = proto_tree_add_uint_format_value(value_tree, *value_bits_hf[bit], tvb, offset, 1, value, "%s", signal_names[bit]);
1076 if (pin_prefix && (bit < num_pins))
1078 proto_item_append_text(item, " [%s%d]", pin_prefix, bit);
1080 proto_item_append_text(item, " %s", state);
1083 return 1;
1086 static int
1087 dissect_cpumode_response(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset)
1089 proto_tree_add_item(tree, hf_mpsse_cpumode_data, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1090 return 1;
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)
1096 const char *pin_prefix = NULL;
1097 unsigned num_pins = 0;
1098 const char *(*signal_names)[8] = NULL;
1100 DISSECTOR_ASSERT(!is_data_shifting_command(cmd_data->cmd) && is_valid_command(cmd_data->cmd, &cmd_data->mpsse_info));
1102 switch (cmd_data->cmd)
1104 case CMD_READ_DATA_BITS_LOW_BYTE:
1105 pin_prefix = get_data_bit_pin_prefix(false, &cmd_data->mpsse_info, &num_pins, &signal_names);
1106 return dissect_read_data_bits_response(tvb, pinfo, tree, offset, *signal_names, pin_prefix, num_pins);
1107 case CMD_READ_DATA_BITS_HIGH_BYTE:
1108 pin_prefix = get_data_bit_pin_prefix(true, &cmd_data->mpsse_info, &num_pins, &signal_names);
1109 return dissect_read_data_bits_response(tvb, pinfo, tree, offset, *signal_names, pin_prefix, num_pins);
1110 case CMD_CPUMODE_READ_SHORT_ADDR:
1111 case CMD_CPUMODE_READ_EXT_ADDR:
1112 return dissect_cpumode_response(tvb, pinfo, tree, offset);
1113 default:
1114 return 0;
1117 static int
1118 dissect_response_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, command_data_t *cmd_data)
1120 int offset_start = offset;
1122 if (pinfo->fd->visited)
1124 DISSECTOR_ASSERT(cmd_data->is_response_set && cmd_data->response_in_packet == pinfo->num);
1126 else
1128 DISSECTOR_ASSERT(!cmd_data->is_response_set);
1129 cmd_data->response_in_packet = pinfo->num;
1130 cmd_data->is_response_set = true;
1133 if (is_valid_command(cmd_data->cmd, &cmd_data->mpsse_info))
1135 if (IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd_data->cmd))
1137 if (IS_DATA_SHIFTING_BYTE_MODE(cmd_data->cmd))
1139 proto_tree_add_item(tree, hf_mpsse_bytes_in, tvb, offset, cmd_data->response_length, ENC_NA);
1141 else
1143 proto_tree_add_item(tree, hf_mpsse_bits_in, tvb, offset, cmd_data->response_length, ENC_LITTLE_ENDIAN);
1145 offset += cmd_data->response_length;
1147 else
1149 int dissected;
1151 dissected = dissect_non_data_shifting_command_response(tvb, pinfo, tree, offset, cmd_data);
1152 offset += dissected;
1154 DISSECTOR_ASSERT(dissected <= cmd_data->response_length);
1155 if (cmd_data->response_length > dissected)
1157 proto_tree_add_expert(tree, pinfo, &ei_undecoded, tvb, offset, cmd_data->response_length - dissected);
1158 offset += (cmd_data->response_length - dissected);
1162 else
1164 proto_tree_add_item(tree, hf_mpsse_bad_command_error, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1165 offset++;
1167 proto_tree_add_item(tree, hf_mpsse_bad_command_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1168 offset++;
1171 return offset - offset_start;
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)
1178 const char *cmd_str;
1179 int offset_start = offset;
1180 proto_item *rsp_data;
1181 proto_tree *rsp_tree;
1182 proto_item *command_in;
1184 cmd_str = get_command_string(cmd_data->cmd, &cmd_data->mpsse_info);
1185 if (!cmd_str)
1187 bool found = false;
1188 bool request_reassembly = false;
1190 DISSECTOR_ASSERT(cmd_data->response_length == 2);
1191 cmd_str = "Bad Command";
1193 /* Look for Bad Command response in data */
1194 while (tvb_reported_length_remaining(tvb, offset) >= 2)
1196 if (tvb_get_uint8(tvb, offset) == BAD_COMMAND_SYNC_CODE)
1198 if (tvb_get_uint8(tvb, offset + 1) == cmd_data->cmd)
1200 found = true;
1201 break;
1204 offset++;
1207 if (!found)
1209 if (tvb_get_uint8(tvb, offset) == BAD_COMMAND_SYNC_CODE)
1211 /* Request reassembly only if there is chance it will help */
1212 request_reassembly = true;
1214 else
1216 offset++;
1220 if (offset != offset_start)
1222 proto_item *item;
1223 proto_tree *expert_tree;
1225 item = proto_tree_add_expert(tree, pinfo, &ei_skipped_response_data, tvb, offset_start, offset - offset_start);
1226 expert_tree = proto_item_add_subtree(item, ett_mpsse_skipped_response_data);
1228 command_in = proto_tree_add_uint_format(expert_tree, hf_mpsse_command_in, NULL, 0, 0, cmd_data->command_in_packet,
1229 "Bad Command 0x%02x in: %" PRIu32, cmd_data->cmd, cmd_data->command_in_packet);
1230 proto_item_set_generated(command_in);
1231 if (cmd_data->is_response_set)
1233 proto_item *response_in;
1235 response_in = proto_tree_add_uint(expert_tree, hf_mpsse_response_in, NULL, 0, 0, cmd_data->response_in_packet);
1236 proto_item_set_generated(response_in);
1240 if (!found)
1242 *need_reassembly = request_reassembly;
1243 return offset - offset_start;
1247 if (tvb_reported_length_remaining(tvb, offset) < cmd_data->response_length)
1249 *need_reassembly = true;
1250 return 0;
1253 rsp_data = proto_tree_add_bytes_format(tree, hf_mpsse_response, tvb, offset, cmd_data->response_length, NULL, "%s", cmd_str);
1254 rsp_tree = proto_item_add_subtree(rsp_data, ett_mpsse_response_data);
1256 command_in = proto_tree_add_uint_format(rsp_tree, hf_mpsse_command_in, NULL, 0, 0, cmd_data->command_in_packet,
1257 "Command 0x%02x in: %" PRIu32, cmd_data->cmd, cmd_data->command_in_packet);
1258 proto_item_set_generated(command_in);
1260 offset += dissect_response_data(tvb, pinfo, rsp_tree, offset, cmd_data);
1262 return offset - offset_start;
1265 static int
1266 dissect_ftdi_mpsse(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1268 bool need_reassembly = false;
1269 ftdi_mpsse_info_t *mpsse_info = (ftdi_mpsse_info_t *)data;
1270 int offset = 0;
1271 proto_item *main_item;
1272 proto_tree *main_tree;
1274 if (!mpsse_info)
1276 return offset;
1279 main_item = proto_tree_add_item(tree, proto_ftdi_mpsse, tvb, offset, -1, ENC_NA);
1280 main_tree = proto_item_add_subtree(main_item, ett_ftdi_mpsse);
1282 col_set_str(pinfo->cinfo, COL_PROTOCOL, "FTDI MPSSE");
1284 if (pinfo->p2p_dir == P2P_DIR_SENT)
1286 command_data_t *iter = get_recorded_command_data(tx_command_info, pinfo, mpsse_info);
1288 if (!pinfo->fd->visited)
1290 /* Not visited yet - advance iterator to last element */
1291 while (iter && iter->next)
1293 DISSECTOR_ASSERT(!iter->preliminary);
1294 iter = iter->next;
1298 while ((tvb_reported_length_remaining(tvb, offset) > 0) && (!need_reassembly))
1300 offset += dissect_command(tvb, pinfo, main_tree, offset, &need_reassembly, mpsse_info, &iter);
1303 else if (pinfo->p2p_dir == P2P_DIR_RECV)
1305 command_data_t *head = get_recorded_command_data(rx_command_info, pinfo, mpsse_info);
1306 command_data_t *iter = head;
1308 if (!pinfo->fd->visited)
1310 while (iter && iter->is_response_set)
1312 iter = iter->next;
1315 if (iter != head)
1317 insert_command_data_pointer(rx_command_info, pinfo, mpsse_info, iter);
1321 while ((tvb_reported_length_remaining(tvb, offset) > 0) && (!need_reassembly))
1323 if (!iter)
1325 proto_tree_add_expert(main_tree, pinfo, &ei_response_without_command, tvb, offset, -1);
1326 offset += tvb_reported_length_remaining(tvb, offset);
1328 else
1330 offset += dissect_response(tvb, pinfo, main_tree, offset, &need_reassembly, iter);
1331 iter = iter->next;
1336 if (need_reassembly)
1338 if (pinfo->can_desegment)
1340 pinfo->desegment_offset = offset;
1341 pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
1343 else
1345 proto_tree_add_expert(main_tree, pinfo, &ei_reassembly_unavailable, tvb, offset, -1);
1347 offset += tvb_reported_length_remaining(tvb, offset);
1350 if (tvb_reported_length_remaining(tvb, offset) > 0)
1352 proto_tree_add_expert(main_tree, pinfo, &ei_undecoded, tvb, offset, -1);
1355 return tvb_reported_length(tvb);
1358 void
1359 proto_register_ftdi_mpsse(void)
1361 expert_module_t *expert_module;
1363 static hf_register_info hf[] = {
1364 { &hf_mpsse_command,
1365 { "Command", "ftdi-mpsse.command",
1366 FT_UINT8, BASE_HEX, NULL, 0x0,
1367 NULL, HFILL }
1369 { &hf_mpsse_command_b0,
1370 { "-ve CLK on write", "ftdi-mpsse.command.b0",
1371 FT_BOOLEAN, 8, NULL, (1 << 0),
1372 NULL, HFILL }
1374 { &hf_mpsse_command_b1,
1375 { "Mode", "ftdi-mpsse.command.b1",
1376 FT_UINT8, BASE_DEC, VALS(data_shifting_command_b1_vals), (1 << 1),
1377 NULL, HFILL }
1379 { &hf_mpsse_command_b2,
1380 { "-ve CLK on read", "ftdi-mpsse.command.b2",
1381 FT_BOOLEAN, 8, NULL, (1 << 2),
1382 NULL, HFILL }
1384 { &hf_mpsse_command_b3,
1385 { "Endianness", "ftdi-mpsse.command.b3",
1386 FT_UINT8, BASE_DEC, VALS(data_shifting_command_b3_vals), (1 << 3),
1387 NULL, HFILL }
1389 { &hf_mpsse_command_b4,
1390 { "Do write TDI", "ftdi-mpsse.command.b4",
1391 FT_BOOLEAN, 8, NULL, (1 << 4),
1392 NULL, HFILL }
1394 { &hf_mpsse_command_b5,
1395 { "Do read TDO", "ftdi-mpsse.command.b5",
1396 FT_BOOLEAN, 8, NULL, (1 << 5),
1397 NULL, HFILL }
1399 { &hf_mpsse_command_b6,
1400 { "Do write TMS", "ftdi-mpsse.command.b6",
1401 FT_BOOLEAN, 8, NULL, (1 << 6),
1402 NULL, HFILL }
1404 { &hf_mpsse_command_b7,
1405 { "Type", "ftdi-mpsse.command.b7",
1406 FT_UINT8, BASE_DEC, VALS(command_b7_vals), (1 << 7),
1407 NULL, HFILL }
1409 { &hf_mpsse_command_with_parameters,
1410 { "Command with parameters", "ftdi-mpsse.command_with_parameters",
1411 FT_BYTES, BASE_NONE, NULL, 0x0,
1412 "Command including optional parameter bytes", HFILL }
1414 { &hf_mpsse_bad_command_error,
1415 { "Error code", "ftdi-mpsse.bad_command.error",
1416 FT_UINT8, BASE_HEX, NULL, 0x0,
1417 "Bad Command error code 0xFA", HFILL }
1419 { &hf_mpsse_bad_command_code,
1420 { "Received invalid command", "ftdi-mpsse.bad_command.command",
1421 FT_UINT8, BASE_HEX, NULL, 0x0,
1422 "Byte which caused the bad command", HFILL }
1424 { &hf_mpsse_response,
1425 { "Command response data", "ftdi-mpsse.response",
1426 FT_BYTES, BASE_NONE, NULL, 0x0,
1427 NULL, HFILL }
1429 { &hf_mpsse_command_in,
1430 { "Command in", "ftdi-mpsse.command.in",
1431 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
1432 NULL, HFILL }
1434 { &hf_mpsse_response_in,
1435 { "Response in", "ftdi-mpsse.response.in",
1436 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
1437 NULL, HFILL }
1439 { &hf_mpsse_length_uint8,
1440 { "Length", "ftdi-mpsse.length",
1441 FT_UINT8, BASE_DEC, NULL, 0x0,
1442 NULL, HFILL }
1444 { &hf_mpsse_length_uint16,
1445 { "Length", "ftdi-mpsse.length",
1446 FT_UINT16, BASE_DEC, NULL, 0x0,
1447 NULL, HFILL }
1449 { &hf_mpsse_bytes_out,
1450 { "Bytes out", "ftdi-mpsse.bytes_out",
1451 FT_BYTES, BASE_NONE, NULL, 0x0,
1452 NULL, HFILL }
1454 { &hf_mpsse_bytes_in,
1455 { "Bytes in", "ftdi-mpsse.bytes_in",
1456 FT_BYTES, BASE_NONE, NULL, 0x0,
1457 NULL, HFILL }
1459 { &hf_mpsse_bits_out,
1460 { "Bits out", "ftdi-mpsse.bits_out",
1461 FT_UINT8, BASE_HEX, NULL, 0x0,
1462 NULL, HFILL }
1464 { &hf_mpsse_bits_in,
1465 { "Bits in", "ftdi-mpsse.bits_in",
1466 FT_UINT8, BASE_HEX, NULL, 0x0,
1467 NULL, HFILL }
1469 { &hf_mpsse_value,
1470 { "Value", "ftdi-mpsse.value",
1471 FT_UINT8, BASE_HEX, NULL, 0x0,
1472 NULL, HFILL }
1474 { &hf_mpsse_value_b0,
1475 { "Bit 0", "ftdi-mpsse.value.b0",
1476 FT_UINT8, BASE_DEC, NULL, (1 << 0),
1477 NULL, HFILL }
1479 { &hf_mpsse_value_b1,
1480 { "Bit 1", "ftdi-mpsse.value.b1",
1481 FT_UINT8, BASE_DEC, NULL, (1 << 1),
1482 NULL, HFILL }
1484 { &hf_mpsse_value_b2,
1485 { "Bit 2", "ftdi-mpsse.value.b2",
1486 FT_UINT8, BASE_DEC, NULL, (1 << 2),
1487 NULL, HFILL }
1489 { &hf_mpsse_value_b3,
1490 { "Bit 3", "ftdi-mpsse.value.b3",
1491 FT_UINT8, BASE_DEC, NULL, (1 << 3),
1492 NULL, HFILL }
1494 { &hf_mpsse_value_b4,
1495 { "Bit 4", "ftdi-mpsse.value.b4",
1496 FT_UINT8, BASE_DEC, NULL, (1 << 4),
1497 NULL, HFILL }
1499 { &hf_mpsse_value_b5,
1500 { "Bit 5", "ftdi-mpsse.value.b5",
1501 FT_UINT8, BASE_DEC, NULL, (1 << 5),
1502 NULL, HFILL }
1504 { &hf_mpsse_value_b6,
1505 { "Bit 6", "ftdi-mpsse.value.b6",
1506 FT_UINT8, BASE_DEC, NULL, (1 << 6),
1507 NULL, HFILL }
1509 { &hf_mpsse_value_b7,
1510 { "Bit 7", "ftdi-mpsse.value.b7",
1511 FT_UINT8, BASE_DEC, NULL, (1 << 7),
1512 NULL, HFILL }
1514 { &hf_mpsse_direction,
1515 { "Direction", "ftdi-mpsse.direction",
1516 FT_UINT8, BASE_HEX, NULL, 0x0,
1517 NULL, HFILL }
1519 { &hf_mpsse_direction_b0,
1520 { "Bit 0", "ftdi-mpsse.direction.b0",
1521 FT_UINT8, BASE_DEC, NULL, (1 << 0),
1522 NULL, HFILL }
1524 { &hf_mpsse_direction_b1,
1525 { "Bit 1", "ftdi-mpsse.direction.b1",
1526 FT_UINT8, BASE_DEC, NULL, (1 << 1),
1527 NULL, HFILL }
1529 { &hf_mpsse_direction_b2,
1530 { "Bit 2", "ftdi-mpsse.direction.b2",
1531 FT_UINT8, BASE_DEC, NULL, (1 << 2),
1532 NULL, HFILL }
1534 { &hf_mpsse_direction_b3,
1535 { "Bit 3", "ftdi-mpsse.direction.b3",
1536 FT_UINT8, BASE_DEC, NULL, (1 << 3),
1537 NULL, HFILL }
1539 { &hf_mpsse_direction_b4,
1540 { "Bit 4", "ftdi-mpsse.direction.b4",
1541 FT_UINT8, BASE_DEC, NULL, (1 << 4),
1542 NULL, HFILL }
1544 { &hf_mpsse_direction_b5,
1545 { "Bit 5", "ftdi-mpsse.direction.b5",
1546 FT_UINT8, BASE_DEC, NULL, (1 << 5),
1547 NULL, HFILL }
1549 { &hf_mpsse_direction_b6,
1550 { "Bit 6", "ftdi-mpsse.direction.b6",
1551 FT_UINT8, BASE_DEC, NULL, (1 << 6),
1552 NULL, HFILL }
1554 { &hf_mpsse_direction_b7,
1555 { "Bit 7", "ftdi-mpsse.direction.b7",
1556 FT_UINT8, BASE_DEC, NULL, (1 << 7),
1557 NULL, HFILL }
1559 { &hf_mpsse_cpumode_address_short,
1560 { "Address", "ftdi-mpsse.cpumode_address",
1561 FT_UINT8, BASE_HEX, NULL, 0x0,
1562 "CPUMode Short Address", HFILL }
1564 { &hf_mpsse_cpumode_address_extended,
1565 { "Address", "ftdi-mpsse.cpumode_address",
1566 FT_UINT16, BASE_HEX, NULL, 0x0,
1567 "CPUMode Extended Address", HFILL }
1569 { &hf_mpsse_cpumode_data,
1570 { "Data", "ftdi-mpsse.cpumode_data",
1571 FT_UINT8, BASE_HEX, NULL, 0x0,
1572 NULL, HFILL }
1574 { &hf_mpsse_clk_divisor,
1575 { "Divisor", "ftdi-mpsse.clk_divisor",
1576 FT_UINT16, BASE_HEX, NULL, 0x0,
1577 NULL, HFILL }
1579 { &hf_mpsse_open_drain_enable_low,
1580 { "Low Byte", "ftdi-mpsse.open_drain_enable_low",
1581 FT_UINT8, BASE_HEX, NULL, 0x0,
1582 NULL, HFILL }
1584 { &hf_mpsse_open_drain_enable_low_b0,
1585 { "Bit 0", "ftdi-mpsse.open_drain_enable_low.b0",
1586 FT_UINT8, BASE_DEC, NULL, (1 << 0),
1587 NULL, HFILL }
1589 { &hf_mpsse_open_drain_enable_low_b1,
1590 { "Bit 1", "ftdi-mpsse.open_drain_enable_low.b1",
1591 FT_UINT8, BASE_DEC, NULL, (1 << 1),
1592 NULL, HFILL }
1594 { &hf_mpsse_open_drain_enable_low_b2,
1595 { "Bit 2", "ftdi-mpsse.open_drain_enable_low.b2",
1596 FT_UINT8, BASE_DEC, NULL, (1 << 2),
1597 NULL, HFILL }
1599 { &hf_mpsse_open_drain_enable_low_b3,
1600 { "Bit 3", "ftdi-mpsse.open_drain_enable_low.b3",
1601 FT_UINT8, BASE_DEC, NULL, (1 << 3),
1602 NULL, HFILL }
1604 { &hf_mpsse_open_drain_enable_low_b4,
1605 { "Bit 4", "ftdi-mpsse.open_drain_enable_low.b4",
1606 FT_UINT8, BASE_DEC, NULL, (1 << 4),
1607 NULL, HFILL }
1609 { &hf_mpsse_open_drain_enable_low_b5,
1610 { "Bit 5", "ftdi-mpsse.open_drain_enable_low.b5",
1611 FT_UINT8, BASE_DEC, NULL, (1 << 5),
1612 NULL, HFILL }
1614 { &hf_mpsse_open_drain_enable_low_b6,
1615 { "Bit 6", "ftdi-mpsse.open_drain_enable_low.b6",
1616 FT_UINT8, BASE_DEC, NULL, (1 << 6),
1617 NULL, HFILL }
1619 { &hf_mpsse_open_drain_enable_low_b7,
1620 { "Bit 7", "ftdi-mpsse.open_drain_enable_low.b7",
1621 FT_UINT8, BASE_DEC, NULL, (1 << 7),
1622 NULL, HFILL }
1624 { &hf_mpsse_open_drain_enable_high,
1625 { "High Byte", "ftdi-mpsse.open_drain_enable_high",
1626 FT_UINT8, BASE_HEX, NULL, 0x0,
1627 NULL, HFILL }
1629 { &hf_mpsse_open_drain_enable_high_b0,
1630 { "Bit 0", "ftdi-mpsse.open_drain_enable_high.b0",
1631 FT_UINT8, BASE_DEC, NULL, (1 << 0),
1632 NULL, HFILL }
1634 { &hf_mpsse_open_drain_enable_high_b1,
1635 { "Bit 1", "ftdi-mpsse.open_drain_enable_high.b1",
1636 FT_UINT8, BASE_DEC, NULL, (1 << 1),
1637 NULL, HFILL }
1639 { &hf_mpsse_open_drain_enable_high_b2,
1640 { "Bit 2", "ftdi-mpsse.open_drain_enable_high.b2",
1641 FT_UINT8, BASE_DEC, NULL, (1 << 2),
1642 NULL, HFILL }
1644 { &hf_mpsse_open_drain_enable_high_b3,
1645 { "Bit 3", "ftdi-mpsse.open_drain_enable_high.b3",
1646 FT_UINT8, BASE_DEC, NULL, (1 << 3),
1647 NULL, HFILL }
1649 { &hf_mpsse_open_drain_enable_high_b4,
1650 { "Bit 4", "ftdi-mpsse.open_drain_enable_high.b4",
1651 FT_UINT8, BASE_DEC, NULL, (1 << 4),
1652 NULL, HFILL }
1654 { &hf_mpsse_open_drain_enable_high_b5,
1655 { "Bit 5", "ftdi-mpsse.open_drain_enable_high.b5",
1656 FT_UINT8, BASE_DEC, NULL, (1 << 5),
1657 NULL, HFILL }
1659 { &hf_mpsse_open_drain_enable_high_b6,
1660 { "Bit 6", "ftdi-mpsse.open_drain_enable_high.b6",
1661 FT_UINT8, BASE_DEC, NULL, (1 << 6),
1662 NULL, HFILL }
1664 { &hf_mpsse_open_drain_enable_high_b7,
1665 { "Bit 7", "ftdi-mpsse.open_drain_enable_high.b7",
1666 FT_UINT8, BASE_DEC, NULL, (1 << 7),
1667 NULL, HFILL }
1671 static ei_register_info ei[] = {
1672 { &ei_undecoded, { "ftdi-mpsse.undecoded", PI_UNDECODED, PI_WARN, "Not dissected yet (report to wireshark.org)", EXPFILL }},
1673 { &ei_response_without_command, { "ftdi-mpsse.response_without_command", PI_PROTOCOL, PI_ERROR, "Unable to associate response with command (response without command?)", EXPFILL }},
1674 { &ei_skipped_response_data, { "ftdi-mpsse.skipped_response_data", PI_PROTOCOL, PI_WARN, "Skipped response data while looking for Bad Command response", EXPFILL }},
1675 { &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 }},
1678 static int *ett[] = {
1679 &ett_ftdi_mpsse,
1680 &ett_mpsse_command,
1681 &ett_mpsse_command_with_parameters,
1682 &ett_mpsse_response_data,
1683 &ett_mpsse_value,
1684 &ett_mpsse_direction,
1685 &ett_mpsse_open_drain_enable,
1686 &ett_mpsse_skipped_response_data,
1689 rx_command_info = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
1690 tx_command_info = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
1692 proto_ftdi_mpsse = proto_register_protocol("FTDI Multi-Protocol Synchronous Serial Engine", "FTDI MPSSE", "ftdi-mpsse");
1693 proto_register_field_array(proto_ftdi_mpsse, hf, array_length(hf));
1694 proto_register_subtree_array(ett, array_length(ett));
1695 ftdi_mpsse_handle = register_dissector("ftdi-mpsse", dissect_ftdi_mpsse, proto_ftdi_mpsse);
1697 expert_module = expert_register_protocol(proto_ftdi_mpsse);
1698 expert_register_field_array(expert_module, ei, array_length(ei));
1702 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1704 * Local variables:
1705 * c-basic-offset: 4
1706 * tab-width: 8
1707 * indent-tabs-mode: nil
1708 * End:
1710 * vi: set shiftwidth=4 tabstop=8 expandtab:
1711 * :indentSize=4:tabSize=8:noTabs=true: