]> sigrok.org Git - sigrok-util.git/blame - debug/saleae-logic16/saleae-logic16-dissector.lua
sigrok-native-macosx: Fix Homebrew package paths
[sigrok-util.git] / debug / saleae-logic16 / saleae-logic16-dissector.lua
CommitLineData
a2491957
SB
1-- Logic16 protocol dissector for Wireshark
2--
3-- Copyright (C) 2015 Stefan Bruens <stefan.bruens@rwth-aachen.de>
4--
5-- based on the LWLA dissector, which is
6-- Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
7--
8-- This program is free software; you can redistribute it and/or modify
9-- it under the terms of the GNU General Public License as published by
10-- the Free Software Foundation; either version 3 of the License, or
11-- (at your option) any later version.
12--
13-- This program is distributed in the hope that it will be useful,
14-- but WITHOUT ANY WARRANTY; without even the implied warranty of
15-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16-- GNU General Public License for more details.
17--
18-- You should have received a copy of the GNU General Public License
19-- along with this program; if not, see <http://www.gnu.org/licenses/>.
20
21-- Usage: wireshark -X lua_script:saleae-logic16-dissector.lua
22--
23-- Create custom protocol for the Saleae Logic16 analyzer.
24p_logic16 = Proto("Logic16", "Saleae Logic16 USB Protocol")
25
26-- Known IDs of Logic16 control commands.
27local control_commands = {
28 [0x01] = "START_ACQUISITION",
29 [0x02] = "ABORT_ACQUISITION_ASYNC",
30 [0x06] = "WRITE_EEPROM",
31 [0x07] = "READ_EEPROM",
32 [0x7a] = "WRITE_LED_TABLE",
33 [0x7b] = "SET_LED_MODE",
34 [0x7c] = "RETURN_TO_BOOTLOADER",
35 [0x7d] = "ABORT_ACQUISITION_SYNC",
36 [0x7e] = "FPGA_UPLOAD_INIT",
37 [0x7f] = "FPGA_UPLOAD_DATA",
38 [0x80] = "FPGA_WRITE_REGISTER",
39 [0x81] = "FPGA_READ_REGISTER",
40 [0x82] = "GET_REVID"
41}
42
43-- Known Logic16 FPGA registers
44local fpga_registers = {
45 [0x00] = "Bitstream version",
46 [0x01] = "Status and control",
47 [0x02] = "Channel select low",
48 [0x03] = "Channel select high",
49 [0x04] = "Sample rate divisor",
50 [0x05] = "LED brightness",
51 [0x06] = "??? [0x06]",
52 [0x07] = "??? [0x07]",
53 [0x08] = "??? [0x08]",
54 [0x09] = "??? [0x09]",
55 [0x0a] = "Base clock",
56 [0x0c] = "??? [0x0c]",
57}
58
59-- Create the fields exhibited by the protocol.
60p_logic16.fields.command = ProtoField.uint8("logic16.cmd", "Command ID", base.HEX_DEC, control_commands)
61p_logic16.fields.regaddr = ProtoField.uint8("logic16.regaddr", "Register Address", base.HEX, fpga_registers)
62p_logic16.fields.regdata = ProtoField.uint8("logic16.regdata", "Register Value", base.HEX_DEC)
63p_logic16.fields.regcount = ProtoField.uint8("logic16.regcount", "Register Count", base.HEX_DEC)
64p_logic16.fields.EE_len = ProtoField.uint8("logic16.eeprom_len", "Read len", base.HEX_DEC)
65p_logic16.fields.unknown = ProtoField.bytes("logic16.unknown", "Unidentified message data")
66p_logic16.fields.reg_addrs = ProtoField.bytes("logic16.reg_addrs", "Register addresses")
67p_logic16.fields.reg_av_pairs = ProtoField.bytes("logic16.reg_av_pairs", "Register address/value pairs")
68
69p_logic16.fields.rawdata = ProtoField.bytes("logic16.rawdata", "Raw Message Data")
70p_logic16.fields.decrypted = ProtoField.bytes("logic16.ep1_decrypted", "Decrypted message data")
71
72-- Referenced USB URB dissector fields.
73local f_urb_type = Field.new("usb.urb_type")
74local f_transfer_type = Field.new("usb.transfer_type")
75local f_endpoint = Field.new("usb.endpoint_number.endpoint")
76local f_direction = Field.new("usb.endpoint_number.direction")
77
78-- Decrypt EP1 message
79local function decrypt_ep1_message(range)
80 local iv = {0x9b, 0x54}
81 local out = ByteArray.new()
82 out:set_size(range:len())
83
84 for i = 0, range:len() - 1, 1 do
85 local s = range(i,1):uint()
86 local dec = bit32.bxor(
87 (bit32.bxor((s + 0x45), 0x38) + 0xb0)
88 , 0x5a, iv[1])
89 dec = bit32.bxor(
90 (bit32.bxor((dec + 0x39), 0x35) + 0x05)
91 , 0x2b, iv[2])
92 local b = bit32.band(dec, 0xff)
93 out:set_index(i, b)
94 iv[1] = b
95 iv[2] = s
96 end
97
98 return ByteArray.tvb(out, "Decrypted")
99end
100
101-- Dissect control command messages.
102local function dissect_response(range, pinfo, tree)
103 pinfo.cols.info = string.format("<- response: %s",
104 tostring(range))
105 local item = tree:add(p_logic16.fields.unknown, range())
106 item:add_expert_info(PI_UNDECODED, PI_WARN, "Leftover data")
107 return range:len()
108end
109
110-- Dissect control command messages.
111local function dissect_command(range, pinfo, tree)
112
113 local command = range(0,1):uint()
114 tree:add(p_logic16.fields.command, range(0,1))
115
116 if command == 1 then -- start acquisition
117 pinfo.cols.info = string.format("-> [%d]: START acquisition",
118 command)
119 elseif command == 2 then -- ABORT_ACQUISITION_ASYNC
120 pinfo.cols.info = string.format("-> [%d]: ABORT acquisition",
121 command)
122 elseif command == 6 then -- WRITE_EEPROM
123 elseif command == 7 then -- READ_EEPROM
124 if range:len() == 5 then
125 local regaddr = range(3,1):uint()
126 local len = range(4,1):uint()
127 tree:add(p_logic16.fields.regaddr, range(3,1))
128 tree:add(p_logic16.fields.EE_len, range(4,1))
129 pinfo.cols.info = string.format("-> [%d]: read EEPROM 0x%02X len=%d",
130 command, regaddr, len)
131 return 3
132 end
133 elseif command == 0x7a then -- WRITE_LED_TABLE
134 local offset = range(1,1):uint()
135 local len = range(2,1):uint()
136 pinfo.cols.info = string.format("-> [%d]: write LED table offset=%d len=%d",
137 command, offset, len)
138
139 elseif command == 0x7b then -- SET_LED_MODE
140 pinfo.cols.info = string.format("-> [%d]: set LED mode flashing=%s",
141 command, range(1,1):uint() and "on" or "off")
142
143 elseif command == 0x7c then -- RETURN_TO_BOOTLOADER
144 elseif command == 0x7d then -- ABORT_ACQUISITION_SYNC
145 pinfo.cols.info = string.format("[%d]: ABORT acquisition SYNC",
146 command)
147 elseif command == 0x7e then -- FPGA_UPLOAD_INIT
148 elseif command == 0x7f then -- FPGA_UPLOAD_DATA
149 elseif command == 0x80 then -- FPGA_WRITE_REGISTER
150 tree:add(p_logic16.fields.regcount, range(1,1))
151 if range:len() == 4 then
152 local regaddr = range(2,1):uint()
153 local regval = range(3,1):uint()
154 tree:add(p_logic16.fields.regaddr, range(2,1))
155 tree:add(p_logic16.fields.regdata, range(3,1))
156 pinfo.cols.info = string.format("-> [%d]: FPGA write reg 0x%02X [%s] value 0x%02X",
157 command, regaddr, fpga_registers[regaddr], regval)
158 return 4
159 else
160 local n_regs = range(1,1):uint()
161 pinfo.cols.info = string.format("-> [%d]: FPGA write reg (%d x)",
162 command, n_regs)
163 tree:add(p_logic16.fields.reg_av_pairs, range(2))
164 return range:len()
165 end
166
167 elseif command == 0x81 then -- FPGA_READ_REGISTER
168 tree:add(p_logic16.fields.regcount, range(1,1))
169 if range:len() == 3 then
170 local regaddr = range(2,1):uint()
171 tree:add(p_logic16.fields.regaddr, range(2,1))
172 pinfo.cols.info = string.format("-> [%d]: FPGA read reg 0x%02X [%s]",
173 command, regaddr, fpga_registers[regaddr])
174 return 3
175 else
176 local n_regs = range(1,1):uint()
177 pinfo.cols.info = string.format("-> [%d]: FPGA read reg (%d x)",
178 command, n_regs)
179 tree:add(p_logic16.fields.reg_addrs, range(2))
180 return range:len()
181 end
182
183 elseif command == 0x82 then -- GET_REVID"
184 end
185 return range:len()
186end
187
188-- Main dissector function.
189function p_logic16.dissector(tvb, pinfo, tree)
190 local transfer_type = tonumber(tostring(f_transfer_type()))
191
192 -- Bulk transfers only.
193 if transfer_type == 3 then
194 local urb_type = tonumber(tostring(f_urb_type()))
195 local endpoint = tonumber(tostring(f_endpoint()))
196 local direction = tonumber(tostring(f_direction()))
197
198 -- Payload-carrying packets only.
199 if (urb_type == 83 and endpoint == 1) -- 'S' - Submit
200 or (urb_type == 67 and endpoint == 1) -- 'C' - Complete
201 then
202 pinfo.cols.protocol = p_logic16.name
203
204 local subtree = tree:add(p_logic16, tvb(), "Logic16")
205 subtree:add(p_logic16.fields.rawdata, tvb())
206
207 local dec = decrypt_ep1_message(tvb)
208
209 local dectree = subtree:add(p_logic16.fields.decrypted, dec())
210 dectree:set_generated()
211
212 -- Dispatch to message-specific dissection handler.
213 if (direction == 0) then
214 return dissect_command(dec, pinfo, dectree)
215 else
216 return dissect_response(dec, pinfo, dectree)
217 end
218 end
219 end
220 return 0
221end
222
223-- Register Logic16 protocol dissector during initialization.
224function p_logic16.init()
225 local usb_product_dissectors = DissectorTable.get("usb.product")
226
227 -- Dissection by vendor+product ID requires that Wireshark can get the
228 -- the device descriptor. Making a USB device available inside a VM
229 -- will make it inaccessible from Linux, so Wireshark cannot fetch the
230 -- descriptor by itself. However, it is sufficient if the guest requests
231 -- the descriptor once while Wireshark is capturing.
232 usb_product_dissectors:add(0x21a91001, p_logic16)
233
234 -- Addendum: Protocol registration based on product ID does not always
235 -- work as desired. Register the protocol on the interface class instead.
236 -- The downside is that it would be a bad idea to put this into the global
237 -- configuration, so one has to make do with -X lua_script: for now.
238 -- local usb_bulk_dissectors = DissectorTable.get("usb.bulk")
239
240 -- For some reason the "unknown" class ID is sometimes 0xFF and sometimes
241 -- 0xFFFF. Register both to make it work all the time.
242 -- usb_bulk_dissectors:add(0xFF, p_logic16)
243 -- usb_bulk_dissectors:add(0xFFFF, p_logic16)
244end