]>
Commit | Line | Data |
---|---|---|
1 | -- SmartScope protocol dissector for Wireshark | |
2 | -- | |
3 | -- Copyright (C) 2015 Marcus Comstedt <marcus@mc.pp.se> | |
4 | -- | |
5 | -- based on the Logic16 dissector, which is | |
6 | -- Copyright (C) 2015 Stefan Bruens <stefan.bruens@rwth-aachen.de> | |
7 | -- based on the LWLA dissector, which is | |
8 | -- Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com> | |
9 | -- | |
10 | -- This program is free software; you can redistribute it and/or modify | |
11 | -- it under the terms of the GNU General Public License as published by | |
12 | -- the Free Software Foundation; either version 3 of the License, or | |
13 | -- (at your option) any later version. | |
14 | -- | |
15 | -- This program is distributed in the hope that it will be useful, | |
16 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | -- GNU General Public License for more details. | |
19 | -- | |
20 | -- You should have received a copy of the GNU General Public License | |
21 | -- along with this program; if not, see <http://www.gnu.org/licenses/>. | |
22 | ||
23 | -- Usage: wireshark -X lua_script:labnation-smartscope-dissector.lua | |
24 | -- | |
25 | -- Create custom protocol for the LabNation SmartScope analyzer. | |
26 | p_smartscope = Proto("SmartScope", "LabNation SmartScope USB Protocol") | |
27 | ||
28 | -- Referenced USB URB dissector fields. | |
29 | local f_urb_type = Field.new("usb.urb_type") | |
30 | local f_transfer_type = Field.new("usb.transfer_type") | |
31 | local f_endpoint = Field.new("usb.endpoint_number.endpoint") | |
32 | local f_direction = Field.new("usb.endpoint_number.direction") | |
33 | ||
34 | -- Header values | |
35 | local headers = { | |
36 | [0xC0] = "Command", | |
37 | [0xAD] = "Answer Dude", | |
38 | } | |
39 | ||
40 | -- Commands | |
41 | local commands = { | |
42 | [0x01] = "PIC_VERSION", | |
43 | [0x02] = "PIC_WRITE", | |
44 | [0x03] = "PIC_READ", | |
45 | [0x04] = "PIC_RESET", | |
46 | [0x05] = "PIC_BOOTLOADER", | |
47 | [0x06] = "EEPROM_READ", | |
48 | [0x07] = "EEPROM_WRITE", | |
49 | [0x08] = "FLASH_ROM_READ", | |
50 | [0x09] = "FLASH_ROM_WRITE", | |
51 | [0x0a] = "I2C_WRITE", | |
52 | [0x0b] = "I2C_READ", | |
53 | [0x0c] = "PROGRAM_FPGA_START", | |
54 | [0x0d] = "PROGRAM_FPGA_END", | |
55 | [0x0e] = "I2C_WRITE_START", | |
56 | [0x0f] = "I2C_WRITE_BULK", | |
57 | [0x10] = "I2C_WRITE_STOP", | |
58 | } | |
59 | ||
60 | -- Addresses | |
61 | local pic_addresses = { | |
62 | [0x00] = "FORCE_STREAMING", | |
63 | } | |
64 | ||
65 | local i2c_addresses = { | |
66 | [0x0C] = "SETTINGS", | |
67 | [0x0D] = "ROM", | |
68 | [0x0E] = "AWG", | |
69 | } | |
70 | ||
71 | local settings_addresses = { | |
72 | [0] = "STROBE_UPDATE", | |
73 | [1] = "SPI_ADDRESS", | |
74 | [2] = "SPI_WRITE_VALUE", | |
75 | [3] = "DIVIDER_MULTIPLIER", | |
76 | [4] = "CHA_YOFFSET_VOLTAGE", | |
77 | [5] = "CHB_YOFFSET_VOLTAGE", | |
78 | [6] = "TRIGGER_PWM", | |
79 | [7] = "TRIGGER_LEVEL", | |
80 | [8] = "TRIGGER_THRESHOLD", | |
81 | [9] = "TRIGGER_MODE", | |
82 | [10] = "TRIGGER_WIDTH", | |
83 | [11] = "INPUT_DECIMATION", | |
84 | [12] = "ACQUISITION_DEPTH", | |
85 | [13] = "TRIGGERHOLDOFF_B0", | |
86 | [14] = "TRIGGERHOLDOFF_B1", | |
87 | [15] = "TRIGGERHOLDOFF_B2", | |
88 | [16] = "TRIGGERHOLDOFF_B3", | |
89 | [17] = "VIEW_DECIMATION", | |
90 | [18] = "VIEW_OFFSET_B0", | |
91 | [19] = "VIEW_OFFSET_B1", | |
92 | [20] = "VIEW_OFFSET_B2", | |
93 | [21] = "VIEW_ACQUISITIONS", | |
94 | [22] = "VIEW_BURSTS", | |
95 | [23] = "VIEW_EXCESS_B0", | |
96 | [24] = "VIEW_EXCESS_B1", | |
97 | [25] = "DIGITAL_TRIGGER_RISING", | |
98 | [26] = "DIGITAL_TRIGGER_FALLING", | |
99 | [27] = "DIGITAL_TRIGGER_HIGH", | |
100 | [28] = "DIGITAL_TRIGGER_LOW", | |
101 | [29] = "DIGITAL_OUT", | |
102 | [30] = "AWG_DEBUG", | |
103 | [31] = "AWG_DECIMATION", | |
104 | [32] = "AWG_SAMPLES_B0", | |
105 | [33] = "AWG_SAMPLES_B1", | |
106 | } | |
107 | ||
108 | local rom_addresses = { | |
109 | [0] = "FW_MSB", | |
110 | [1] = "FW_LSB", | |
111 | [2] = "FW_GIT0", | |
112 | [3] = "FW_GIT1", | |
113 | [4] = "FW_GIT2", | |
114 | [5] = "FW_GIT3", | |
115 | [6] = "SPI_RECEIVED_VALUE", | |
116 | [7] = "STROBES", | |
117 | } | |
118 | ||
119 | local strobe_numbers = { | |
120 | [0] = "GLOBAL_RESET", | |
121 | [1] = "INIT_SPI_TRANSFER", | |
122 | [2] = "AWG_ENABLE", | |
123 | [3] = "LA_ENABLE", | |
124 | [4] = "SCOPE_ENABLE", | |
125 | [5] = "SCOPE_UPDATE", | |
126 | [6] = "FORCE_TRIGGER", | |
127 | [7] = "VIEW_UPDATE", | |
128 | [8] = "VIEW_SEND_OVERVIEW", | |
129 | [9] = "VIEW_SEND_PARTIAL", | |
130 | [10] = "ACQ_START", | |
131 | [11] = "ACQ_STOP", | |
132 | [12] = "CHA_DCCOUPLING", | |
133 | [13] = "CHB_DCCOUPLING", | |
134 | [14] = "ENABLE_ADC", | |
135 | [15] = "OVERFLOW_DETECT", | |
136 | [16] = "ENABLE_NEG", | |
137 | [17] = "ENABLE_RAM", | |
138 | [18] = "DOUT_3V_5V", | |
139 | [19] = "EN_OPAMP_B", | |
140 | [20] = "AWG_DEBUG", | |
141 | [21] = "DIGI_DEBUG", | |
142 | [22] = "ROLL", | |
143 | [23] = "LA_CHANNEL", | |
144 | } | |
145 | ||
146 | -- Magic values | |
147 | local magic = { | |
148 | [0x4C4E] = "LabNation", | |
149 | } | |
150 | ||
151 | ||
152 | -- Create the fields exhibited by the protocol. | |
153 | p_smartscope.fields.header = ProtoField.uint8("smartscope.header", "Header", base.HEX_DEC, headers) | |
154 | p_smartscope.fields.command = ProtoField.uint8("smartscope.cmd", "Command ID", base.HEX_DEC, commands) | |
155 | p_smartscope.fields.pic_version = ProtoField.string("smartscope.pic_version", "PIC version") | |
156 | p_smartscope.fields.pic_address = ProtoField.uint8("smartscope.pic_address", "PIC address", base.HEX, pic_addresses) | |
157 | p_smartscope.fields.pic_length = ProtoField.uint8("smartscope.pic_length", "PIC length", base.HEX_DEC) | |
158 | p_smartscope.fields.pic_data = ProtoField.bytes("smartscope.pic_data", "PIC data") | |
159 | p_smartscope.fields.eeprom_address = ProtoField.uint8("smartscope.eeprom_address", "EEPROM address", base.HEX) | |
160 | p_smartscope.fields.eeprom_length = ProtoField.uint8("smartscope.eeprom_length", "EEPROM length", base.HEX_DEC) | |
161 | p_smartscope.fields.eeprom_data = ProtoField.bytes("smartscope.eeprom_data", "EEPROM data") | |
162 | p_smartscope.fields.flash_rom_address = ProtoField.uint16("smartscope.flash_rom_address", "Flash ROM address", base.HEX) | |
163 | p_smartscope.fields.flash_rom_length = ProtoField.uint8("smartscope.flash_rom_length", "Flash ROM length", base.HEX_DEC) | |
164 | p_smartscope.fields.flash_rom_data = ProtoField.bytes("smartscope.flash_rom_data", "Flash ROM data") | |
165 | p_smartscope.fields.i2c_write_length = ProtoField.uint8("smartscope.i2c_write_length", "I2C write length") | |
166 | p_smartscope.fields.i2c_write_rawdata = ProtoField.bytes("smartscope.i2c_write_rawdata", "Raw I2C write data") | |
167 | p_smartscope.fields.i2c_write_slave_address = ProtoField.uint8("smartscope.i2c_write_slave_address", "I2C write slave address", base.HEX_DEC, i2c_addresses, 0xfe) | |
168 | p_smartscope.fields.i2c_write_mode = ProtoField.bool("smartscope.i2c_write_mode", "I2C write mode", 8, {"READ", "WRITE"}, 0x01) | |
169 | p_smartscope.fields.settings_subaddress = ProtoField.uint8("smartscope.settings_subaddress", "I2C subaddress (SETTINGS)", base.HEX, settings_addresses) | |
170 | p_smartscope.fields.rom_subaddress = ProtoField.uint8("smartscope.rom_subaddress", "I2C subaddress (ROM)", base.HEX, rom_addresses) | |
171 | p_smartscope.fields.awg_subaddress = ProtoField.uint8("smartscope.awg_subaddress", "I2C subaddress (AWG)", base.HEX) | |
172 | p_smartscope.fields.strobe_number = ProtoField.uint8("smartscope.strobe_number", "Strobe number", base.DEC, strobe_numbers, 0xfe) | |
173 | p_smartscope.fields.strobe_value = ProtoField.uint8("smartscope.strobe_value", "Strobe value", base.DEC, nil, 0x01) | |
174 | p_smartscope.fields.i2c_write_payload = ProtoField.bytes("smartscope.i2c_write_payload", "I2C write payload") | |
175 | p_smartscope.fields.i2c_read_slave_address = ProtoField.uint8("smartscope.i2c_read_slave_address", "I2C read slave address", base.HEX_DEC, i2c_addresses) | |
176 | p_smartscope.fields.i2c_read_length = ProtoField.uint8("smartscope.i2c_read_length", "I2C read length") | |
177 | p_smartscope.fields.i2c_read_payload = ProtoField.bytes("smartscope.i2c_read_payload", "I2C read payload") | |
178 | p_smartscope.fields.fpga_packets = ProtoField.uint16("smartscope.fpga_packets", "FPGA packet count", base.HEX_DEC) | |
179 | p_smartscope.fields.fpga_data = ProtoField.bytes("smartscope.fpga_data", "FPGA bitstream data") | |
180 | p_smartscope.fields.rawdata = ProtoField.bytes("smartscope.rawdata", "Raw Message Data") | |
181 | ||
182 | p_smartscope.fields.magic = ProtoField.uint16("smartscope.magic", "Magic", base.HEX_DEC, magic) | |
183 | p_smartscope.fields.header_offset = ProtoField.uint8("smartscope.header_offset", "Header offset", base.HEX_DEC) | |
184 | p_smartscope.fields.bytes_per_burst = ProtoField.uint8("smartscope.bytes_per_burst", "Bytes per burst", base.HEX_DEC) | |
185 | p_smartscope.fields.number_of_payload_bursts = ProtoField.uint16("smartscope.number_of_payload_bursts", "Number of payload bursts", base.HEX_DEC) | |
186 | p_smartscope.fields.package_offset = ProtoField.uint16("smartscope.package_offset", "Package offset", base.HEX_DEC) | |
187 | p_smartscope.fields.acquiring = ProtoField.bool("smartscope.acquiring", "Acquiring", 8, nil, 0x01) | |
188 | p_smartscope.fields.overview_buffer = ProtoField.bool("smartscope.overview_buffer", "Overview Buffer", 8, nil, 0x02) | |
189 | p_smartscope.fields.last_acquisition = ProtoField.bool("smartscope.last_acquisition", "Last Acquisition", 8, nil, 0x04) | |
190 | p_smartscope.fields.rolling = ProtoField.bool("smartscope.rolling", "Rolling", 8, nil, 0x08) | |
191 | p_smartscope.fields.timed_out = ProtoField.bool("smartscope.timed_out", "Timed Out", 8, nil, 0x10) | |
192 | p_smartscope.fields.awaiting_trigger = ProtoField.bool("smartscope.awaiting_trigger", "Awaiting Trigger", 8, nil, 0x20) | |
193 | p_smartscope.fields.armed = ProtoField.bool("smartscope.armed", "Armed", 8, nil, 0x40) | |
194 | p_smartscope.fields.full_acquisition_dump = ProtoField.bool("smartscope.full_acquisition_dump", "Full Acquisition Dump", 8, nil, 0x80) | |
195 | p_smartscope.fields.acquisition_id = ProtoField.uint8("smartscope.acquisition_id", "Acquisition ID", base.HEX_DEC) | |
196 | p_smartscope.fields.header_trigger_level = ProtoField.uint8("smartscope.header.trigger_level", "Trigger Level", base.HEX_DEC) | |
197 | p_smartscope.fields.header_trigger_mode = ProtoField.uint8("smartscope.header.trigger_mode", "Trigger Mode", base.HEX_DEC) | |
198 | p_smartscope.fields.header_trigger_width = ProtoField.uint8("smartscope.header.trigger_width", "Trigger Width", base.HEX_DEC) | |
199 | p_smartscope.fields.header_trigger_holdoff = ProtoField.uint32("smartscope.header.trigger_holdoff", "Trigger Holdoff", base.HEX_DEC) | |
200 | p_smartscope.fields.header_cha_yoffset_voltage = ProtoField.uint8("smartscope.header.cha_yoffset_voltage", "Channel A Y-Offset Voltage", base.HEX_DEC) | |
201 | p_smartscope.fields.header_chb_yoffset_voltage = ProtoField.uint8("smartscope.header.chb_yoffset_voltage", "Channel B Y-Offset Voltage", base.HEX_DEC) | |
202 | p_smartscope.fields.header_divider_multiplier = ProtoField.uint8("smartscope.header.divider_multiplier", "Divider Multiplier", base.HEX_DEC) | |
203 | p_smartscope.fields.header_input_decimation = ProtoField.uint8("smartscope.header.input_decimation", "Input Decimation", base.HEX_DEC) | |
204 | p_smartscope.fields.header_trigger_threshold = ProtoField.uint8("smartscope.header.trigger_threshold", "Trigger Threshold", base.HEX_DEC) | |
205 | p_smartscope.fields.header_trigger_pwm = ProtoField.uint8("smartscope.header.trigger_pwm", "Trigger PWM", base.HEX_DEC) | |
206 | p_smartscope.fields.header_trigger_rising = ProtoField.uint8("smartscope.header.trigger_rising", "Trigger Rising", base.HEX_DEC) | |
207 | p_smartscope.fields.header_trigger_falling = ProtoField.uint8("smartscope.header.trigger_falling", "Trigger Falling", base.HEX_DEC) | |
208 | p_smartscope.fields.header_trigger_high = ProtoField.uint8("smartscope.header.trigger_high", "Trigger High", base.HEX_DEC) | |
209 | p_smartscope.fields.header_trigger_low = ProtoField.uint8("smartscope.header.trigger_low", "Trigger Low", base.HEX_DEC) | |
210 | p_smartscope.fields.header_acquisition_depth = ProtoField.uint8("smartscope.header.acquisition_depth", "Acquisition Depth", base.HEX_DEC) | |
211 | p_smartscope.fields.header_view_decimation = ProtoField.uint8("smartscope.header.view_decimation", "View Decimation", base.HEX_DEC) | |
212 | p_smartscope.fields.header_view_offset = ProtoField.uint24("smartscope.header.view_offset", "View Offset", base.HEX_DEC) | |
213 | p_smartscope.fields.header_view_acquisitions = ProtoField.uint8("smartscope.header.view_acquisitions", "View Acquisitions", base.HEX_DEC) | |
214 | p_smartscope.fields.header_view_bursts = ProtoField.uint8("smartscope.header.view_bursts", "View Bursts", base.HEX_DEC) | |
215 | p_smartscope.fields.header_view_excess = ProtoField.uint16("smartscope.header.view_excess", "View Excess", base.HEX_DEC) | |
216 | p_smartscope.fields.header_awg_enable = ProtoField.bool("smartscope.header.awg_enable", "AWG Enable", 8, nil, 0x01) | |
217 | p_smartscope.fields.header_la_enable = ProtoField.bool("smartscope.header.la_enable", "LA Enable", 8, nil, 0x02) | |
218 | p_smartscope.fields.header_cha_coupling = ProtoField.bool("smartscope.header.cha_coupling", "Channel A Coupling", 8, {"DC", "AC"}, 0x04) | |
219 | p_smartscope.fields.header_chb_coupling = ProtoField.bool("smartscope.header.chb_coupling", "Channel B Coupling", 8, {"DC", "AC"}, 0x08) | |
220 | p_smartscope.fields.header_digi_debug = ProtoField.bool("smartscope.header.digi_debug", "Digi Debug", 8, nil, 0x10) | |
221 | p_smartscope.fields.header_roll = ProtoField.bool("smartscope.header.rolling", "Rolling", 8, nil, 0x20) | |
222 | p_smartscope.fields.header_la_channel = ProtoField.bool("smartscope.header.la_channel", "LA Channel", 8, {"Channel B", "Channel A"}, 0x40) | |
223 | ||
224 | p_smartscope.fields.acq_data = ProtoField.bytes("smartscope.acquisition_data", "Acquisition data") | |
225 | ||
226 | ||
227 | -- State variables | |
228 | local pktFpgaData | |
229 | local fpgaDataCount | |
230 | local pktAcqData | |
231 | local acqDataCount | |
232 | ||
233 | -- Dissect control command messages. | |
234 | local function dissect_command(range, pinfo, tree, command) | |
235 | pinfo.cols.info = string.format("-> [%d]: %s", command, commands[command] or "???") | |
236 | if command == 2 then -- pic write | |
237 | local addr = range(0,1):uint() | |
238 | tree:add(p_smartscope.fields.pic_address, range(0,1)) | |
239 | tree:add(p_smartscope.fields.pic_length, range(1,1)) | |
240 | tree:add(p_smartscope.fields.pic_data, range(2,range(1,1):uint())) | |
241 | pinfo.cols.info:append(string.format(" %s len=%d", (pic_addresses[addr] or string.format("0x%02X", addr)), range(1,1):uint())) | |
242 | elseif command == 3 then -- pic read | |
243 | local addr = range(0,1):uint() | |
244 | tree:add(p_smartscope.fields.pic_address, range(0,1)) | |
245 | tree:add(p_smartscope.fields.pic_length, range(1,1)) | |
246 | pinfo.cols.info:append(string.format(" %s len=%d", (pic_addresses[addr] or string.format("0x%02X", addr)), range(1,1):uint())) | |
247 | elseif command == 6 then -- eeprom read | |
248 | local addr = range(0,1):uint() | |
249 | tree:add(p_smartscope.fields.eeprom_address, range(0,1)) | |
250 | tree:add(p_smartscope.fields.eeprom_length, range(1,1)) | |
251 | pinfo.cols.info:append(string.format(" 0x%02X len=%d", addr, range(1,1):uint())) | |
252 | elseif command == 7 then -- eeprom write | |
253 | local addr = range(0,1):uint() | |
254 | tree:add(p_smartscope.fields.eeprom_address, range(0,1)) | |
255 | tree:add(p_smartscope.fields.eeprom_length, range(1,1)) | |
256 | tree:add(p_smartscope.fields.eeprom_data, range(2,range(1,1):uint())) | |
257 | pinfo.cols.info:append(string.format(" 0x%02X len=%d", addr, range(1,1):uint())) | |
258 | elseif command == 8 then -- flash rom read | |
259 | local addr = range(0,1):uint()+256*range(2,1):uint() | |
260 | tree:add(p_smartscope.fields.flash_rom_address, range(0,3), addr) | |
261 | tree:add(p_smartscope.fields.flash_rom_length, range(1,1)) | |
262 | pinfo.cols.info:append(string.format(" 0x%03X len=%d", addr, range(1,1):uint())) | |
263 | elseif command == 9 then -- flash rom write | |
264 | local addr = range(0,1):uint()+256*range(2,1):uint() | |
265 | tree:add(p_smartscope.fields.flash_rom_address, range(0,3), addr) | |
266 | tree:add(p_smartscope.fields.flash_rom_length, range(1,1)) | |
267 | tree:add(p_smartscope.fields.flash_rom_data, range(3,range(1,1):uint())) | |
268 | pinfo.cols.info:append(string.format(" 0x%03X len=%d", addr, range(1,1):uint())) | |
269 | elseif command == 10 or command == 14 then -- i2c write / i2c write start | |
270 | tree:add(p_smartscope.fields.i2c_write_length, range(0,1)) | |
271 | tree:add(p_smartscope.fields.i2c_write_rawdata, range(1)) | |
272 | local len = range(0,1):uint() | |
273 | if len > 0 then | |
274 | local slave = bit.rshift(range(1,1):uint(), 1) | |
275 | tree:add(p_smartscope.fields.i2c_write_slave_address, range(1,1)) | |
276 | tree:add(p_smartscope.fields.i2c_write_mode, range(1,1)) | |
277 | if len > 1 then | |
278 | local subaddress = range(2,1):uint() | |
279 | local payload = len > 2 and range(3) | |
280 | if slave == 12 then -- settings | |
281 | tree:add(p_smartscope.fields.settings_subaddress, range(2,1)) | |
282 | pinfo.cols.info:append(" SETTINGS["..(settings_addresses[subaddress] or "???").."]") | |
283 | elseif slave == 13 then -- rom | |
284 | tree:add(p_smartscope.fields.rom_subaddress, range(2,1)) | |
285 | pinfo.cols.info:append(" ROM["..(rom_addresses[subaddress] or "???").."]") | |
286 | elseif slave == 14 then -- awg | |
287 | tree:add(p_smartscope.fields.awg_subaddress, range(2,1)) | |
288 | pinfo.cols.info:append(string.format(" AWG[%d]", subaddress)) | |
289 | else | |
290 | payload = range(2) | |
291 | end | |
292 | if payload and payload:len() == 1 and slave == 12 and subaddress == 0 then | |
293 | local strobe = payload(0,1):uint() | |
294 | local value = bit.band(strobe, 1) | |
295 | strobe = bit.rshift(strobe, 1) | |
296 | tree:add(p_smartscope.fields.strobe_number, payload(0,1)) | |
297 | tree:add(p_smartscope.fields.strobe_value, payload(0,1)) | |
298 | pinfo.cols.info:append(string.format(" STROBE[%s] = %d", (strobe_numbers[strobe] or string.format("%d", strobe)), value)) | |
299 | elseif payload then | |
300 | tree:add(p_smartscope.fields.i2c_write_payload, payload) | |
301 | pinfo.cols.info:append(string.format(" len=%d", payload:len())) | |
302 | end | |
303 | end | |
304 | end | |
305 | elseif command == 11 then -- i2c read | |
306 | local slave = range(0,1):uint() | |
307 | tree:add(p_smartscope.fields.i2c_read_slave_address, range(0,1)) | |
308 | tree:add(p_smartscope.fields.i2c_read_length, range(1,1)) | |
309 | if i2c_addresses[slave] then | |
310 | pinfo.cols.info:append(string.format(" %s", i2c_addresses[slave])) | |
311 | end | |
312 | pinfo.cols.info:append(string.format(" len=%d", range(1,1):uint())) | |
313 | elseif command == 12 then -- program fpga start | |
314 | tree:add(p_smartscope.fields.fpga_packets, range(0,2)) | |
315 | fpgaDataCount = range(0,2):uint()*32 | |
316 | elseif command == 15 then -- i2c write bulk | |
317 | tree:add(p_smartscope.fields.i2c_write_length, range(0,1)) | |
318 | tree:add(p_smartscope.fields.i2c_write_rawdata, range(1)) | |
319 | local len = range(0,1):uint() | |
320 | if len > 0 then | |
321 | tree:add(p_smartscope.fields.i2c_write_payload, range(1,len)) | |
322 | pinfo.cols.info:append(string.format(" len=%d", len)) | |
323 | end | |
324 | elseif command == 16 then -- i2c write stop | |
325 | tree:add(p_smartscope.fields.i2c_write_length, range(0,1)) | |
326 | local len = range(0,1):uint() | |
327 | if len > 0 then | |
328 | pinfo.cols.info:append(string.format(" len=%d", len)) | |
329 | end | |
330 | end | |
331 | end | |
332 | ||
333 | -- Dissect answers to control command messages. | |
334 | local function dissect_answer(range, pinfo, tree, command) | |
335 | pinfo.cols.info = string.format("<- [%d]: %s", command, commands[command] or "???") | |
336 | if command == 1 then -- pic version | |
337 | local version = string.format("%d.%d.%d", range(4,1):uint(), range(3,1):uint(), range(2,1):uint()) | |
338 | tree:add(p_smartscope.fields.pic_version, range(2,3), version) | |
339 | pinfo.cols.info:append(' "' .. version .. '"') | |
340 | elseif command == 3 then -- pic read | |
341 | local addr = range(0,1):uint() | |
342 | tree:add(p_smartscope.fields.pic_address, range(0,1)) | |
343 | tree:add(p_smartscope.fields.pic_length, range(1,1)) | |
344 | tree:add(p_smartscope.fields.pic_data, range(2,range(1,1):uint())) | |
345 | pinfo.cols.info:append(string.format(" %s len=%d", (pic_addresses[addr] or string.format("0x%02X", addr)), range(1,1):uint())) | |
346 | elseif command == 6 then -- eeprom read | |
347 | local addr = range(0,1):uint() | |
348 | tree:add(p_smartscope.fields.eeprom_address, range(0,1)) | |
349 | tree:add(p_smartscope.fields.eeprom_length, range(1,1)) | |
350 | tree:add(p_smartscope.fields.eeprom_data, range(2,range(1,1):uint())) | |
351 | pinfo.cols.info:append(string.format(" 0x%02X len=%d", addr, range(1,1):uint())) | |
352 | elseif command == 8 then -- flash rom read | |
353 | local addr = range(0,1):uint()+256*bit.band(range(2,1):uint(),0xf) | |
354 | tree:add(p_smartscope.fields.flash_rom_address, range(0,3), addr) | |
355 | tree:add(p_smartscope.fields.flash_rom_length, range(1,1)) | |
356 | tree:add(p_smartscope.fields.flash_rom_data, range(3,range(1,1):uint())) | |
357 | pinfo.cols.info:append(string.format(" 0x%03X len=%d", addr, range(1,1):uint())) | |
358 | elseif command == 11 then -- i2c read | |
359 | local slave = range(0,1):uint() | |
360 | tree:add(p_smartscope.fields.i2c_read_slave_address, range(0,1)) | |
361 | tree:add(p_smartscope.fields.i2c_read_length, range(1,1)) | |
362 | if i2c_addresses[slave] then | |
363 | pinfo.cols.info:append(string.format(" %s", i2c_addresses[slave])) | |
364 | end | |
365 | local len = range(1,1):uint() | |
366 | tree:add(p_smartscope.fields.i2c_read_payload, range(2,len)) | |
367 | pinfo.cols.info:append(string.format(" len=%d", len)) | |
368 | end | |
369 | end | |
370 | ||
371 | -- Dissect buld data header. | |
372 | local function dissect_dataheader(range, pinfo, tree) | |
373 | local subtree = tree:add(range, "Header") | |
374 | subtree:add(p_smartscope.fields.header_trigger_level, range(0,1)) | |
375 | subtree:add(p_smartscope.fields.header_trigger_mode, range(1,1)) | |
376 | subtree:add(p_smartscope.fields.header_trigger_width, range(2,1)) | |
377 | subtree:add_le(p_smartscope.fields.header_trigger_holdoff, range(3,4)) | |
378 | subtree:add(p_smartscope.fields.header_cha_yoffset_voltage, range(7,1)) | |
379 | subtree:add(p_smartscope.fields.header_chb_yoffset_voltage, range(8,1)) | |
380 | subtree:add(p_smartscope.fields.header_divider_multiplier, range(9,1)) | |
381 | subtree:add(p_smartscope.fields.header_input_decimation, range(10,1)) | |
382 | subtree:add(p_smartscope.fields.header_trigger_threshold, range(11,1)) | |
383 | subtree:add(p_smartscope.fields.header_trigger_pwm, range(12,1)) | |
384 | subtree:add(p_smartscope.fields.header_trigger_rising, range(13,1)) | |
385 | subtree:add(p_smartscope.fields.header_trigger_falling, range(14,1)) | |
386 | subtree:add(p_smartscope.fields.header_trigger_high, range(15,1)) | |
387 | subtree:add(p_smartscope.fields.header_trigger_low, range(16,1)) | |
388 | subtree:add(p_smartscope.fields.header_acquisition_depth, range(17,1)) | |
389 | subtree:add(p_smartscope.fields.header_view_decimation, range(18,1)) | |
390 | subtree:add_le(p_smartscope.fields.header_view_offset, range(19,3)) | |
391 | subtree:add(p_smartscope.fields.header_view_acquisitions, range(22,1)) | |
392 | subtree:add(p_smartscope.fields.header_view_bursts, range(23,1)) | |
393 | subtree:add_le(p_smartscope.fields.header_view_excess, range(24,2)) | |
394 | subtree:add(p_smartscope.fields.header_awg_enable, range(26,1)) | |
395 | subtree:add(p_smartscope.fields.header_la_enable, range(26,1)) | |
396 | subtree:add(p_smartscope.fields.header_cha_coupling, range(26,1)) | |
397 | subtree:add(p_smartscope.fields.header_chb_coupling, range(26,1)) | |
398 | subtree:add(p_smartscope.fields.header_digi_debug, range(26,1)) | |
399 | subtree:add(p_smartscope.fields.header_roll, range(26,1)) | |
400 | subtree:add(p_smartscope.fields.header_la_channel, range(26,1)) | |
401 | end | |
402 | ||
403 | -- Dissect bulk data. | |
404 | local function dissect_data(range, pinfo, tree) | |
405 | tree:add(p_smartscope.fields.magic, range(0,2)) | |
406 | if (range(0,2):uint() ~= 0x4C4E) then | |
407 | return | |
408 | end | |
409 | pinfo.cols.info = string.format("<- Acquisition header") | |
410 | tree:add(p_smartscope.fields.header_offset, range(2,1)) | |
411 | tree:add(p_smartscope.fields.bytes_per_burst, range(3,1)) | |
412 | tree:add_le(p_smartscope.fields.number_of_payload_bursts, range(4,2)) | |
413 | tree:add_le(p_smartscope.fields.package_offset, range(6,2)) | |
414 | tree:add(p_smartscope.fields.acquiring, range(10,1)) | |
415 | tree:add(p_smartscope.fields.overview_buffer, range(10,1)) | |
416 | tree:add(p_smartscope.fields.last_acquisition, range(10,1)) | |
417 | tree:add(p_smartscope.fields.rolling, range(10,1)) | |
418 | tree:add(p_smartscope.fields.timed_out, range(10,1)) | |
419 | tree:add(p_smartscope.fields.awaiting_trigger, range(10,1)) | |
420 | tree:add(p_smartscope.fields.armed, range(10,1)) | |
421 | tree:add(p_smartscope.fields.full_acquisition_dump, range(10,1)) | |
422 | tree:add(p_smartscope.fields.acquisition_id, range(11,1)) | |
423 | dissect_dataheader(range(range(2,1):uint(), 27), pinfo, tree) | |
424 | pinfo.cols.info:append(string.format(" id=0x%02x", range(11,1):uint())) | |
425 | acqDataCount = range(3,1):uint() * range(4,2):le_uint() | |
426 | if (bit.band(range(10,1):uint(), 0x02) ~= 0) then | |
427 | pinfo.cols.info:append(" (overview buffer)") | |
428 | acqDataCount = 4096 | |
429 | elseif (bit.band(range(10,1):uint(), 0x80) ~= 0) then | |
430 | pinfo.cols.info:append(string.format(" (full acquisition dump offset=%d)", range(6,2):le_uint())) | |
431 | end | |
432 | end | |
433 | ||
434 | -- Main dissector function. | |
435 | function p_smartscope.dissector(tvb, pinfo, tree) | |
436 | local transfer_type = tonumber(tostring(f_transfer_type())) | |
437 | ||
438 | -- Bulk transfers only. | |
439 | if transfer_type == 3 then | |
440 | local urb_type = tonumber(tostring(f_urb_type())) | |
441 | local endpoint = tonumber(tostring(f_endpoint())) | |
442 | local direction = tonumber(tostring(f_direction())) | |
443 | ||
444 | -- Payload-carrying packets only. | |
445 | if (urb_type == 67 and endpoint == 1 and direction == 1) -- 'C' - Complete | |
446 | or (urb_type == 83 and endpoint == 2 and direction == 0) -- 'S' - Submit | |
447 | or (urb_type == 67 and endpoint == 3 and direction == 1) -- 'C' - Complete | |
448 | then | |
449 | pinfo.cols.protocol = p_smartscope.name | |
450 | ||
451 | local subtree = tree:add(p_smartscope, tvb(), "SmartScope") | |
452 | subtree:add(p_smartscope.fields.rawdata, tvb()) | |
453 | ||
454 | if endpoint == 1 and direction == 1 then | |
455 | if pktAcqData[pinfo.number] == nil then | |
456 | pktAcqData[pinfo.number] = (acqDataCount > 0) | |
457 | if acqDataCount > tvb:len() then | |
458 | acqDataCount = acqDataCount - tvb:len() | |
459 | else | |
460 | acqDataCount = 0 | |
461 | end | |
462 | end | |
463 | ||
464 | if pktAcqData[pinfo.number] == true then | |
465 | subtree:add(p_smartscope.fields.acq_data, tvb()) | |
466 | pinfo.cols.info = string.format("<- Acquisition data (%d bytes)", tvb:len()) | |
467 | return | |
468 | end | |
469 | end | |
470 | ||
471 | if endpoint == 2 and direction == 0 then | |
472 | if pktFpgaData[pinfo.number] == nil then | |
473 | pktFpgaData[pinfo.number] = (fpgaDataCount > 0) | |
474 | if fpgaDataCount > tvb:len() then | |
475 | fpgaDataCount = fpgaDataCount - tvb:len() | |
476 | else | |
477 | fpgaDataCount = 0 | |
478 | end | |
479 | end | |
480 | ||
481 | if pktFpgaData[pinfo.number] == true then | |
482 | subtree:add(p_smartscope.fields.fpga_data, tvb()) | |
483 | pinfo.cols.info = string.format("-> FPGA bitstream data (%d bytes)", tvb:len()) | |
484 | return | |
485 | end | |
486 | ||
487 | end | |
488 | ||
489 | if endpoint == 1 then | |
490 | dissect_data(tvb, pinfo, subtree) | |
491 | return | |
492 | end | |
493 | ||
494 | local header = tvb(0,1):uint() | |
495 | subtree:add(p_smartscope.fields.header, tvb(0,1)) | |
496 | local command = tvb(1,1):uint() | |
497 | subtree:add(p_smartscope.fields.command, tvb(1,1)) | |
498 | if endpoint == 2 and header == 0xc0 then | |
499 | dissect_command(tvb(2), pinfo, subtree, command) | |
500 | elseif endpoint == 3 and header == 0xad then | |
501 | dissect_answer(tvb(2), pinfo, subtree, command) | |
502 | end | |
503 | end | |
504 | end | |
505 | end | |
506 | ||
507 | -- Register SmartScope protocol dissector during initialization. | |
508 | function p_smartscope.init() | |
509 | ||
510 | pktFpgaData = {} | |
511 | fpgaDataCount = 0 | |
512 | pktAcqData = {} | |
513 | acqDataCount = 0 | |
514 | ||
515 | local usb_product_dissectors = DissectorTable.get("usb.product") | |
516 | ||
517 | -- Dissection by vendor+product ID requires that Wireshark can get the | |
518 | -- the device descriptor. Making a USB device available inside a VM | |
519 | -- will make it inaccessible from Linux, so Wireshark cannot fetch the | |
520 | -- descriptor by itself. However, it is sufficient if the guest requests | |
521 | -- the descriptor once while Wireshark is capturing. | |
522 | usb_product_dissectors:add(0x04d8f4b5, p_smartscope) | |
523 | end |