]> sigrok.org Git - sigrok-util.git/blob - debug/labnation-smartscope/labnation-smartscope-dissector.lua
macosx: Refine pulseview executable invocation in wrapper script.
[sigrok-util.git] / debug / labnation-smartscope / labnation-smartscope-dissector.lua
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