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