]> sigrok.org Git - sigrok-util.git/blame - debug/sysclk-lwla/sysclk-lwla-dissector.lua
Consistently use the "Sysclk" spelling everywhere.
[sigrok-util.git] / debug / sysclk-lwla / sysclk-lwla-dissector.lua
CommitLineData
f16da0bd
DE
1-- SysClk LWLA protocol dissector for Wireshark
2--
a73dda58 3-- Copyright (c) 2014,2015 Daniel Elstner <daniel.kitta@gmail.com>
f16da0bd
DE
4--
5-- This program is free software; you can redistribute it and/or modify
6-- it under the terms of the GNU General Public License as published by
7-- the Free Software Foundation; either version 3 of the License, or
8-- (at your option) any later version.
9--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-- GNU General Public License for more details.
14--
15-- You should have received a copy of the GNU General Public License
16-- along with this program; if not, see <http://www.gnu.org/licenses/>.
17
18-- Usage: wireshark -X lua_script:sysclk-lwla-dissector.lua
19--
20-- It is not advisable to install this dissector globally, since
21-- it will try to interpret the communication of any USB device
22-- using the vendor-specific interface class.
23
24-- Create custom protocol for the LWLA logic analyzer.
25p_lwla = Proto("lwla", "LWLA USB Protocol")
26
27-- LWLA message type. For simplicity, the numerical value is the same
28-- as the USB end point number the message is sent to or comes from.
29local message_types = {
30 [2] = "Control command",
31 [4] = "Firmware transfer",
32 [6] = "Control response"
33}
34
35-- Known IDs of LWLA control commands.
36local control_commands = {
37 [1] = "Read register",
38 [2] = "Write register",
a73dda58 39 [3] = "Read memory",
f16da0bd
DE
40 [5] = "Write ???",
41 [6] = "Read memory",
42 [7] = "Capture setup",
43 [8] = "Capture status"
44}
45
46-- Create the fields exhibited by the protocol.
47p_lwla.fields.msgtype = ProtoField.uint8("lwla.msgtype", "Message Type", base.DEC, message_types)
48p_lwla.fields.command = ProtoField.uint16("lwla.cmd", "Command ID", base.DEC, control_commands)
49p_lwla.fields.memaddr = ProtoField.uint32("lwla.memaddr", "Memory Address", base.HEX_DEC)
50p_lwla.fields.memlen = ProtoField.uint32("lwla.memlen", "Memory Read Length", base.HEX_DEC)
51p_lwla.fields.regaddr = ProtoField.uint16("lwla.regaddr", "Register Address", base.HEX)
52p_lwla.fields.regdata = ProtoField.uint32("lwla.regdata", "Register Value", base.HEX_DEC)
53p_lwla.fields.stataddr = ProtoField.uint16("lwla.stataddr", "Status Memory Address", base.HEX)
54p_lwla.fields.statlen = ProtoField.uint16("lwla.statlen", "Status Memory Read/Write Length", base.HEX_DEC)
55p_lwla.fields.statdata = ProtoField.bytes("lwla.statdata", "Status Word")
a73dda58 56p_lwla.fields.secdata = ProtoField.uint32("lwla.secdata", "Security Hash", base.HEX_DEC)
f16da0bd
DE
57p_lwla.fields.unknown = ProtoField.bytes("lwla.unknown", "Unidentified message data")
58
59-- Referenced USB URB dissector fields.
60local f_urb_type = Field.new("usb.urb_type")
61local f_transfer_type = Field.new("usb.transfer_type")
62local f_endpoint = Field.new("usb.endpoint_number.endpoint")
63
64-- Insert warning for undecoded leftover data.
65local function warn_undecoded(tree, range)
66 local item = tree:add(p_lwla.fields.unknown, range)
67 item:add_expert_info(PI_UNDECODED, PI_WARN, "Leftover data")
68end
69
a73dda58 70-- Extract a 32-bit mixed endian word.
f16da0bd
DE
71local function read_mixed_endian(range)
72 return range(0,2):le_uint() * 65536 + range(2,2):le_uint()
73end
74
a73dda58 75-- Un-shuffle the bytes of a 64-bit stat field.
f16da0bd
DE
76local function read_stat_field(range)
77 return string.char(range(5,1):uint(), range(4,1):uint(), range(7,1):uint(), range(6,1):uint(),
78 range(1,1):uint(), range(0,1):uint(), range(3,1):uint(), range(2,1):uint())
79end
80
a73dda58 81-- Dissect LWLA1034 capture state.
f16da0bd
DE
82local function dissect_capture_state(range, tree)
83 for i = 0, range:len() - 8, 8 do
84 tree:add(p_lwla.fields.statdata, range(i,8), read_stat_field(range(i,8)))
85 end
86end
87
88-- Dissect LWLA control command messages.
89local function dissect_command(range, pinfo, tree)
90 if range:len() < 4 then
91 return 0
92 end
93
94 tree:add_le(p_lwla.fields.command, range(0,2))
95 local command = range(0,2):le_uint()
96
97 if command == 1 then -- read register
98 if range:len() == 4 then
99 tree:add_le(p_lwla.fields.regaddr, range(2,2))
100 pinfo.cols.info = string.format("Cmd %d: read reg 0x%04X",
101 command, range(2,2):le_uint())
102 return 4
103 end
104 elseif command == 2 then -- write register
105 if range:len() == 8 then
106 tree:add_le(p_lwla.fields.regaddr, range(2,2))
107 local regval = read_mixed_endian(range(4,4))
108 tree:add(p_lwla.fields.regdata, range(4,4), regval)
109 pinfo.cols.info = string.format("Cmd %d: write reg 0x%04X value 0x%08X",
110 command, range(2,2):le_uint(), regval)
111 return 8
112 end
a73dda58 113 elseif command == 5 then -- write security hash?
f16da0bd 114 if range:len() == 66 then
a73dda58 115 local infotext = string.format("Cmd %d: write security hash?", command)
f16da0bd
DE
116 for i = 2, 62, 4 do
117 local value = read_mixed_endian(range(i,4))
a73dda58 118 tree:add(p_lwla.fields.secdata, range(i,4), value)
f16da0bd
DE
119 infotext = string.format("%s %02X", infotext, value)
120 end
121 pinfo.cols.info = infotext
122 return 66
123 end
a73dda58 124 elseif command == 3 or command == 6 then -- read memory at address
f16da0bd
DE
125 if range:len() == 10 then
126 local memaddr = read_mixed_endian(range(2,4))
127 local memlen = read_mixed_endian(range(6,4))
128 tree:add(p_lwla.fields.memaddr, range(2,4), memaddr)
129 tree:add(p_lwla.fields.memlen, range(6,4), memlen)
130 pinfo.cols.info = string.format("Cmd %d: read mem 0x%06X length %d",
131 command, memaddr, memlen)
132 return 10
133 end
a73dda58 134 elseif command == 7 then -- capture setup (LWLA1034)
f16da0bd
DE
135 if range:len() >= 6 then
136 tree:add_le(p_lwla.fields.stataddr, range(2,2))
137 tree:add_le(p_lwla.fields.statlen, range(4,2))
138 local len = 8 * range(4,2):le_uint()
139 if range:len() ~= len + 6 then
140 warn_undecoded(tree, range(6))
141 return 6
142 end
143 dissect_capture_state(range(6,len), tree)
144 pinfo.cols.info = string.format("Cmd %d: setup 0x%X length %d",
145 command, range(2,2):le_uint(), range(4,2):le_uint())
146 return 6 + len
147 end
a73dda58 148 elseif command == 8 then -- capture status (LWLA1034)
f16da0bd
DE
149 if range:len() == 6 then
150 tree:add_le(p_lwla.fields.stataddr, range(2,2))
151 tree:add_le(p_lwla.fields.statlen, range(4,2))
152 pinfo.cols.info = string.format("Cmd %d: state 0x%X length %d",
153 command, range(2,2):le_uint(), range(4,2):le_uint())
154 return 6
155 end
156 end
157 warn_undecoded(tree, range(2))
158 return 2
159end
160
161-- Dissect LWLA control response messages.
162local function dissect_response(range, pinfo, tree)
163 -- The heuristics below are ugly and prone to fail, but they do the job
164 -- for the purposes this dissector was written.
165 if range:len() == 40 or range:len() == 80 then -- heuristic: response to command 8
166 dissect_capture_state(range, tree)
167 pinfo.cols.info = string.format("Ret 8: state length %d", range:len() / 8)
168 return range:len()
169 elseif range:len() == 4 then -- heuristic: response to command 1
170 local value = read_mixed_endian(range(0,4))
171 tree:add(p_lwla.fields.regdata, range(0,4), value)
172 pinfo.cols.info = string.format("Ret 1: reg value 0x%08X", value)
173 return 4
a73dda58
DE
174 elseif range:len() == 1000 or range:len() == 552 then -- heuristic: response to command 3
175 pinfo.cols.info = string.format("Ret 3: mem data length %d", range:len() / 4)
176 return 0
f16da0bd
DE
177 elseif range:len() >= 18 and range:len() % 18 == 0 then -- heuristic: response to command 6
178 pinfo.cols.info = string.format("Ret 6: mem data length %d", range:len() * 2 / 9)
179 return 0
180 else
181 return 0
182 end
183end
184
185-- Main LWLA dissector function.
186function p_lwla.dissector(tvb, pinfo, tree)
187 local transfer_type = tonumber(tostring(f_transfer_type()))
188
189 -- Bulk transfers only.
190 if transfer_type == 3 then
191 local urb_type = tonumber(tostring(f_urb_type()))
192 local endpoint = tonumber(tostring(f_endpoint()))
193
194 -- Payload-carrying packets only.
195 if (urb_type == 83 and (endpoint == 2 or endpoint == 4))
196 or (urb_type == 67 and endpoint == 6)
197 then
198 pinfo.cols.protocol = p_lwla.name
199
200 local subtree = tree:add(p_lwla, tvb(), "LWLA")
201 subtree:add(p_lwla.fields.msgtype, endpoint):set_generated()
202
203 -- Dispatch to message-specific dissection handler.
204 if endpoint == 2 then
205 return dissect_command(tvb, pinfo, subtree)
206 elseif endpoint == 4 then
207 pinfo.cols.info = "FPGA bitstream"
208 return 0
209 elseif endpoint == 6 then
210 return dissect_response(tvb, pinfo, subtree)
211 end
212 end
213 end
214 return 0
215end
216
217-- Register LWLA protocol dissector during initialization.
218function p_lwla.init()
219-- local usb_product_dissectors = DissectorTable.get("usb.product")
220
221 -- Dissection by vendor+product ID requires that Wireshark can get the
222 -- the device descriptor. Making a USB device available inside VirtualBox
223 -- will make it inaccessible from Linux, so Wireshark cannot fetch the
224 -- descriptor by itself. However, it is sufficient if the VirtualBox
225 -- guest requests the descriptor once while Wireshark is capturing.
2743bd7c
UH
226-- usb_product_dissectors:add(0x29616688, p_lwla) -- Sysclk LWLA1016
227-- usb_product_dissectors:add(0x29616689, p_lwla) -- Sysclk LWLA1034
f16da0bd
DE
228
229 -- Addendum: Protocol registration based on product ID does not always
230 -- work as desired. Register the protocol on the interface class instead.
231 -- The downside is that it would be a bad idea to put this into the global
232 -- configuration, so one has to make do with -X lua_script: for now.
233 local usb_bulk_dissectors = DissectorTable.get("usb.bulk")
234
235 -- For some reason the "unknown" class ID is sometimes 0xFF and sometimes
236 -- 0xFFFF. Register both to make it work all the time.
237 usb_bulk_dissectors:add(0xFF, p_lwla)
238 usb_bulk_dissectors:add(0xFFFF, p_lwla)
239end