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