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