1 -- SmartScope protocol dissector for Wireshark
3 -- Copyright (C) 2015 Marcus Comstedt <marcus@mc.pp.se>
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>
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.
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.
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/>.
23 -- Usage: wireshark -X lua_script:labnation-smartscope-dissector.lua
25 -- Create custom protocol for the LabNation SmartScope analyzer.
26 p_smartscope
= Proto("SmartScope", "LabNation SmartScope USB Protocol")
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")
37 [0xAD] = "Answer Dude",
42 [0x01] = "PIC_VERSION",
46 [0x05] = "PIC_BOOTLOADER",
47 [0x06] = "EEPROM_READ",
48 [0x07] = "EEPROM_WRITE",
49 [0x08] = "FLASH_ROM_READ",
50 [0x09] = "FLASH_ROM_WRITE",
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",
61 local pic_addresses
= {
62 [0x00] = "FORCE_STREAMING",
65 local i2c_addresses
= {
71 local settings_addresses
= {
72 [0] = "STROBE_UPDATE",
74 [2] = "SPI_WRITE_VALUE",
75 [3] = "DIVIDER_MULTIPLIER",
76 [4] = "CHA_YOFFSET_VOLTAGE",
77 [5] = "CHB_YOFFSET_VOLTAGE",
79 [7] = "TRIGGER_LEVEL",
80 [8] = "TRIGGER_THRESHOLD",
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",
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",
103 [31] = "AWG_DECIMATION",
104 [32] = "AWG_SAMPLES_B0",
105 [33] = "AWG_SAMPLES_B1",
108 local rom_addresses
= {
115 [6] = "SPI_RECEIVED_VALUE",
119 local strobe_numbers
= {
120 [0] = "GLOBAL_RESET",
121 [1] = "INIT_SPI_TRANSFER",
124 [4] = "SCOPE_ENABLE",
125 [5] = "SCOPE_UPDATE",
126 [6] = "FORCE_TRIGGER",
128 [8] = "VIEW_SEND_OVERVIEW",
129 [9] = "VIEW_SEND_PARTIAL",
132 [12] = "CHA_DCCOUPLING",
133 [13] = "CHB_DCCOUPLING",
135 [15] = "OVERFLOW_DETECT",
148 [0x4C4E] = "LabNation",
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")
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)
224 p_smartscope
.fields
.acq_data
= ProtoField
.bytes("smartscope.acquisition_data", "Acquisition data")
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()
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))
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
))
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
))
300 tree
:add(p_smartscope
.fields
.i2c_write_payload
, payload
)
301 pinfo
.cols
.info
:append(string.format(" len=%d", payload
:len()))
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
]))
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()
321 tree
:add(p_smartscope
.fields
.i2c_write_payload
, range(1,len
))
322 pinfo
.cols
.info
:append(string.format(" len=%d", len
))
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()
328 pinfo
.cols
.info
:append(string.format(" len=%d", len
))
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
]))
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
))
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))
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
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)")
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()))
434 -- Main dissector function.
435 function p_smartscope
.dissector(tvb
, pinfo
, tree
)
436 local transfer_type
= tonumber(tostring(f_transfer_type()))
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()))
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
449 pinfo
.cols
.protocol
= p_smartscope
.name
451 local subtree
= tree
:add(p_smartscope
, tvb(), "SmartScope")
452 subtree
:add(p_smartscope
.fields
.rawdata
, tvb())
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()
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())
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()
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())
489 if endpoint
== 1 then
490 dissect_data(tvb
, pinfo
, subtree
)
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
)
507 -- Register SmartScope protocol dissector during initialization.
508 function p_smartscope
.init()
515 local usb_product_dissectors
= DissectorTable
.get("usb.product")
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
)