mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-07 01:13:18 +00:00
Add Wireshark packet dissector plugin and associated tools
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
# This file is machine-generated - editing it directly is not advised
|
||||
|
||||
julia_version = "1.10.0"
|
||||
manifest_format = "2.0"
|
||||
project_hash = "2d299f8e3abb5302e3c8ed5120acc4d9928016b1"
|
||||
|
||||
[[deps.ArgTools]]
|
||||
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
|
||||
version = "1.1.1"
|
||||
|
||||
[[deps.Artifacts]]
|
||||
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
|
||||
|
||||
[[deps.Base64]]
|
||||
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
|
||||
[[deps.Compat]]
|
||||
deps = ["UUIDs"]
|
||||
git-tree-sha1 = "886826d76ea9e72b35fcd000e535588f7b60f21d"
|
||||
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
|
||||
version = "4.10.1"
|
||||
|
||||
[deps.Compat.extensions]
|
||||
CompatLinearAlgebraExt = "LinearAlgebra"
|
||||
|
||||
[deps.Compat.weakdeps]
|
||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
|
||||
[[deps.Dates]]
|
||||
deps = ["Printf"]
|
||||
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
|
||||
[[deps.Downloads]]
|
||||
deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
|
||||
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
|
||||
version = "1.6.0"
|
||||
|
||||
[[deps.ExprTools]]
|
||||
git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec"
|
||||
uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
|
||||
version = "0.1.10"
|
||||
|
||||
[[deps.FileWatching]]
|
||||
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
|
||||
|
||||
[[deps.InlineStrings]]
|
||||
deps = ["Parsers"]
|
||||
git-tree-sha1 = "9cc2baf75c6d09f9da536ddf58eb2f29dedaf461"
|
||||
uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48"
|
||||
version = "1.4.0"
|
||||
|
||||
[[deps.InteractiveUtils]]
|
||||
deps = ["Markdown"]
|
||||
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
||||
|
||||
[[deps.JLLWrappers]]
|
||||
deps = ["Artifacts", "Preferences"]
|
||||
git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca"
|
||||
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
|
||||
version = "1.5.0"
|
||||
|
||||
[[deps.LazyArtifacts]]
|
||||
deps = ["Artifacts", "Pkg"]
|
||||
uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"
|
||||
|
||||
[[deps.LibCURL]]
|
||||
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
|
||||
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
|
||||
version = "0.6.4"
|
||||
|
||||
[[deps.LibCURL_jll]]
|
||||
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
|
||||
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
|
||||
version = "8.4.0+0"
|
||||
|
||||
[[deps.LibGit2]]
|
||||
deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"]
|
||||
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
||||
|
||||
[[deps.LibGit2_jll]]
|
||||
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"]
|
||||
uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5"
|
||||
version = "1.6.4+0"
|
||||
|
||||
[[deps.LibSSH2_jll]]
|
||||
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
|
||||
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
|
||||
version = "1.11.0+1"
|
||||
|
||||
[[deps.LibSerialPort]]
|
||||
deps = ["Libdl", "libserialport_jll"]
|
||||
git-tree-sha1 = "69403284a6217f6e60e76553032a103893b7d910"
|
||||
uuid = "a05a14c7-6e3b-5ba9-90a2-45558833e1df"
|
||||
version = "0.5.2"
|
||||
|
||||
[[deps.Libdl]]
|
||||
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
|
||||
|
||||
[[deps.Logging]]
|
||||
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||
|
||||
[[deps.Markdown]]
|
||||
deps = ["Base64"]
|
||||
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
||||
|
||||
[[deps.MbedTLS_jll]]
|
||||
deps = ["Artifacts", "Libdl"]
|
||||
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
|
||||
version = "2.28.2+1"
|
||||
|
||||
[[deps.Mmap]]
|
||||
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
|
||||
|
||||
[[deps.Mocking]]
|
||||
deps = ["Compat", "ExprTools"]
|
||||
git-tree-sha1 = "4cc0c5a83933648b615c36c2b956d94fda70641e"
|
||||
uuid = "78c3b35d-d492-501b-9361-3d52fe80e533"
|
||||
version = "0.7.7"
|
||||
|
||||
[[deps.MozillaCACerts_jll]]
|
||||
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
|
||||
version = "2023.1.10"
|
||||
|
||||
[[deps.NetworkOptions]]
|
||||
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
|
||||
version = "1.2.0"
|
||||
|
||||
[[deps.Parsers]]
|
||||
deps = ["Dates", "PrecompileTools", "UUIDs"]
|
||||
git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821"
|
||||
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
|
||||
version = "2.8.1"
|
||||
|
||||
[[deps.PcapTools]]
|
||||
deps = ["Dates", "Mmap", "UnixTimes", "UnsafeArrays"]
|
||||
path = "dev/PcapTools"
|
||||
uuid = "222fe7e8-3f39-464a-bf97-d9bbb753f246"
|
||||
version = "1.1.1"
|
||||
|
||||
[[deps.Pkg]]
|
||||
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
|
||||
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||
version = "1.10.0"
|
||||
|
||||
[[deps.PrecompileTools]]
|
||||
deps = ["Preferences"]
|
||||
git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f"
|
||||
uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
|
||||
version = "1.2.0"
|
||||
|
||||
[[deps.Preferences]]
|
||||
deps = ["TOML"]
|
||||
git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e"
|
||||
uuid = "21216c6a-2e73-6563-6e65-726566657250"
|
||||
version = "1.4.1"
|
||||
|
||||
[[deps.Printf]]
|
||||
deps = ["Unicode"]
|
||||
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||
|
||||
[[deps.REPL]]
|
||||
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
|
||||
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
|
||||
|
||||
[[deps.Random]]
|
||||
deps = ["SHA"]
|
||||
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
|
||||
[[deps.SHA]]
|
||||
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
|
||||
version = "0.7.0"
|
||||
|
||||
[[deps.Scratch]]
|
||||
deps = ["Dates"]
|
||||
git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386"
|
||||
uuid = "6c6a2e73-6563-6170-7368-637461726353"
|
||||
version = "1.2.1"
|
||||
|
||||
[[deps.Serialization]]
|
||||
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
|
||||
|
||||
[[deps.Sockets]]
|
||||
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
|
||||
|
||||
[[deps.TOML]]
|
||||
deps = ["Dates"]
|
||||
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
|
||||
version = "1.0.3"
|
||||
|
||||
[[deps.TZJData]]
|
||||
deps = ["Artifacts"]
|
||||
git-tree-sha1 = "d39314cdbaf5b90a047db33858626f8d1cc973e1"
|
||||
uuid = "dc5dba14-91b3-4cab-a142-028a31da12f7"
|
||||
version = "1.0.0+2023c"
|
||||
|
||||
[[deps.Tar]]
|
||||
deps = ["ArgTools", "SHA"]
|
||||
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
|
||||
version = "1.10.0"
|
||||
|
||||
[[deps.TimeZones]]
|
||||
deps = ["Artifacts", "Dates", "Downloads", "InlineStrings", "LazyArtifacts", "Mocking", "Printf", "Scratch", "TZJData", "Unicode", "p7zip_jll"]
|
||||
git-tree-sha1 = "89e64d61ef3cd9e80f7fc12b7d13db2d75a23c03"
|
||||
uuid = "f269a46b-ccf7-5d73-abea-4c690281aa53"
|
||||
version = "1.13.0"
|
||||
|
||||
[deps.TimeZones.extensions]
|
||||
TimeZonesRecipesBaseExt = "RecipesBase"
|
||||
|
||||
[deps.TimeZones.weakdeps]
|
||||
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
|
||||
|
||||
[[deps.UUIDs]]
|
||||
deps = ["Random", "SHA"]
|
||||
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
|
||||
[[deps.Unicode]]
|
||||
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
|
||||
|
||||
[[deps.UnixTimes]]
|
||||
deps = ["Dates", "TimeZones"]
|
||||
git-tree-sha1 = "406de9a6ee22f2030477ed7a50455aef3038bd4b"
|
||||
uuid = "ab1a18e7-b408-4913-896c-624bb82ed7f4"
|
||||
version = "1.3.0"
|
||||
|
||||
[[deps.UnsafeArrays]]
|
||||
git-tree-sha1 = "e7f1c67ba99ac6df440de191fa4d5cbfcbdddcd1"
|
||||
uuid = "c4a57d5a-5b31-53a6-b365-19f8c011fbd6"
|
||||
version = "1.0.5"
|
||||
|
||||
[[deps.Zlib_jll]]
|
||||
deps = ["Libdl"]
|
||||
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
|
||||
version = "1.2.13+1"
|
||||
|
||||
[[deps.libserialport_jll]]
|
||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "aacc5af8e8ba0b48a6feadac7afd4bca5d4e98d8"
|
||||
uuid = "220460dc-b50e-5ed0-8176-09b0fd261e90"
|
||||
version = "0.1.3+0"
|
||||
|
||||
[[deps.nghttp2_jll]]
|
||||
deps = ["Artifacts", "Libdl"]
|
||||
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
|
||||
version = "1.52.0+1"
|
||||
|
||||
[[deps.p7zip_jll]]
|
||||
deps = ["Artifacts", "Libdl"]
|
||||
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
|
||||
version = "17.4.0+2"
|
||||
@@ -0,0 +1,10 @@
|
||||
name = "AVCLANPipe"
|
||||
uuid = "bfacebfd-1bd5-49e7-81bd-5edd8f4074de"
|
||||
authors = ["Allen Hill <allenofthehills@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
|
||||
[deps]
|
||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
LibSerialPort = "a05a14c7-6e3b-5ba9-90a2-45558833e1df"
|
||||
PcapTools = "222fe7e8-3f39-464a-bf97-d9bbb753f246"
|
||||
UnixTimes = "ab1a18e7-b408-4913-896c-624bb82ed7f4"
|
||||
@@ -0,0 +1,11 @@
|
||||
# Packet analysis with Wireshark
|
||||
|
||||
- Install the Lua IEBUS/AVCLAN packet dissector to the Wireshark Lua Plugins folder
|
||||
- Look in Help > About Wireshark dialog, Folders tab, "Personal Lua Plugins". It was `~/.local/lib/wireshark/plugins` for me, but it didn't exist until I manually created it.
|
||||
- Copy or link `avclan_plugin.lua` to the Wireshark Lua Pluginds folder
|
||||
- Linking is more convenient if modifying/developing the dissector
|
||||
`ln -s $(pwd)/avclan_plugin.lua ~/.local/lib/wireshark/plugins/avclan_plugin.lua`
|
||||
- Install Julia and needed packages (I recommend using [juliaup](https://github.com/JuliaLang/juliaup) or an official binary from the Julialang website. Binaries from your distribution are typically out of date and/or built incorrectly.)
|
||||
- From this folder, start Julia with the local project environment using `julia --project=@.`
|
||||
- Run `] instantiate` to install the necessary Julia packages
|
||||
- Run `julia src/pipepackets.jl | wireshark -k -i -` to pipe packets logged by the Mockingboard over serial into Wireshark. If the Lua dissector is installed correctly, the packets should be correctly recognized and dissected as IEBUS/AVCLAN packets
|
||||
@@ -0,0 +1,583 @@
|
||||
--[[
|
||||
Copyright (C) 2023 Allen Hill <allenofthehills@gmail.com>
|
||||
|
||||
Portions of the following are based on code from libsigrokdecode PR that is
|
||||
copyright (C) 2023 Maciej Grela <enki@fsck.pl>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
local iebusproto = Proto("iebus", "IEBus protocol")
|
||||
|
||||
local f_broadcast = ProtoField.bool("iebus.broadcast", "Broadcast", base.NONE, { [1] = "false", [2] = "true" })
|
||||
local f_controller_addr = ProtoField.uint16("iebus.controller", "Controller address", base.HEX, nil, 0x0FFF)
|
||||
local f_peripheral_addr = ProtoField.uint16("iebus.peripheral", "Peripheral address", base.HEX, nil, 0x0FFF)
|
||||
local f_control = ProtoField.uint8("iebus.control", "Control field", base.HEX)
|
||||
local f_length = ProtoField.uint8("iebus.length", "Data length", base.DEC)
|
||||
local f_data = ProtoField.bytes("iebus.data", "Frame data", base.SPACE)
|
||||
|
||||
iebusproto.fields = {
|
||||
f_broadcast,
|
||||
f_controller_addr,
|
||||
f_peripheral_addr,
|
||||
f_control,
|
||||
f_length,
|
||||
f_data
|
||||
}
|
||||
|
||||
local controlbits = {
|
||||
[0x0] = "READ PERIPH STATUS (SSR)",
|
||||
[0x1] = "UNDEFINED",
|
||||
[0x2] = "UNDEFINED",
|
||||
[0x3] = "READ & LOCK DATA",
|
||||
[0x4] = "READ LOCK ADDR (lo8)",
|
||||
[0x5] = "READ LOCK ADDR (hi4)",
|
||||
[0x6] = "READ & UNLOCK PERIPH STATUS (SSR)",
|
||||
[0x7] = "READ DATA",
|
||||
[0x8] = "UNDEFINED",
|
||||
[0x9] = "UNDEFINED",
|
||||
[0xA] = "WRITE & LOCK CMD",
|
||||
[0xB] = "WRITE & LOCK DATA",
|
||||
[0xC] = "UNDEFINED",
|
||||
[0xD] = "UNDEFINED",
|
||||
[0xE] = "WRITE CMD",
|
||||
[0xF] = "WRITE DATA"
|
||||
}
|
||||
|
||||
local periphstatusbits = {
|
||||
[0] = "TX BUFFER EMPTY",
|
||||
[1] = "RX BUFFER EMPTY",
|
||||
[2] = "UNIT LOCKED",
|
||||
[4] = "TX ENABLED",
|
||||
[6] = "IEBUS MODE 1",
|
||||
[7] = "IEBUS MODE 2",
|
||||
}
|
||||
|
||||
-- Expert info for unknown packet types, set as group PI_UNDECODED, severity PI_NOTE or PI_WARN
|
||||
-- Expert info for LAN Check [0x3, SW_ID, 0x01, 0x20] as severity PI_CHAT
|
||||
|
||||
function iebusproto.dissector(buffer, pinfo, tree)
|
||||
length = buffer:len()
|
||||
if length == 0 then return end
|
||||
|
||||
pinfo.cols.protocol = iebusproto.name
|
||||
|
||||
local subtree = tree:add(iebusproto, buffer(), "IEBus frame")
|
||||
|
||||
subtree:add_le(f_broadcast, buffer(0,1))
|
||||
pinfo.cols.src = buffer(1,2):bytes():tohex():sub(2,-1)
|
||||
pinfo.cols.dst = buffer(3,2):bytes():tohex():sub(2,-1)
|
||||
|
||||
local control_item, control = subtree:add_packet_field(f_control, buffer(5,1), ENC_LITTLE_ENDIAN)
|
||||
-- print("control val" .. control)
|
||||
control_item:append_text(" (" .. controlbits[buffer(5,1):uint()] .. ")")
|
||||
|
||||
local _, datalen = subtree:add_packet_field(f_length, buffer(6,1), ENC_LITTLE_ENDIAN)
|
||||
|
||||
local data = subtree:add(iebusproto, buffer(), "IEBus message")
|
||||
data:add(f_data, buffer(7, datalen))
|
||||
|
||||
end
|
||||
|
||||
local udlt = DissectorTable.get("wtap_encap")
|
||||
udlt:add(wtap.USER15, iebusproto)
|
||||
|
||||
local avclanproto = Proto("avclan", "AVCLAN protocol")
|
||||
|
||||
local known_devices_names = {
|
||||
["COMM_CTRL"] = 0x01,
|
||||
["COMMUNICATION v1"] = 0x11,
|
||||
["COMMUNICATION v2"] = 0x12,
|
||||
["SW"] = 0x21,
|
||||
["SW_NAME"] = 0x23,
|
||||
["SW_CONVERTING"] = 0x24,
|
||||
["CMD_SW"] = 0x25,
|
||||
["STATUS"] = 0x31,
|
||||
["BEEP_HU"] = 0x28,
|
||||
["BEEP_SPEAKERS"] = 0x29,
|
||||
["FRONT_PSNG_MONITOR"] = 0x34,
|
||||
["CD_CHANGER2"] = 0x43,
|
||||
["BLUETOOTH_TEL"] = 0x55,
|
||||
["INFO_DRAWING"] = 0x56,
|
||||
["NAV_ECU"] = 0x58,
|
||||
["CAMERA"] = 0x5C,
|
||||
["CLIMATE_DRAWING"] = 0x5D,
|
||||
["AUDIO_DRAWING"] = 0x5E,
|
||||
["TRIP_INFO_DRAWING"] = 0x5F,
|
||||
["TUNER"] = 0x60,
|
||||
["TAPE_DECK"] = 0x61,
|
||||
["CD"] = 0x62,
|
||||
["CD_CHANGER"] = 0x63,
|
||||
["AUDIO_AMP"] = 0x74,
|
||||
["GPS"] = 0x80,
|
||||
["VOICE_CTRL"] = 0x85,
|
||||
["CLIMATE_CTRL_DEV"] = 0xE0,
|
||||
["TRIP_INFO"] = 0xE5,
|
||||
}
|
||||
|
||||
local known_devices = {
|
||||
[0x01] = "COMM_CTRL",
|
||||
[0x11] = "COMMUNICATION v1",
|
||||
[0x12] = "COMMUNICATION v2",
|
||||
[0x21] = "SW",
|
||||
[0x23] = "SW_NAME",
|
||||
[0x24] = "SW_CONVERTING",
|
||||
[0x25] = "CMD_SW",
|
||||
[0x31] = "STATUS",
|
||||
[0x28] = "BEEP_HU",
|
||||
[0x29] = "BEEP_SPEAKERS",
|
||||
[0x34] = "FRONT_PSNG_MONITOR",
|
||||
[0x43] = "CD_CHANGER2",
|
||||
[0x55] = "BLUETOOTH_TEL",
|
||||
[0x56] = "INFO_DRAWING",
|
||||
[0x58] = "NAV_ECU",
|
||||
[0x5C] = "CAMERA",
|
||||
[0x5D] = "CLIMATE_DRAWING",
|
||||
[0x5E] = "AUDIO_DRAWING",
|
||||
[0x5F] = "TRIP_INFO_DRAWING",
|
||||
[0x60] = "TUNER",
|
||||
[0x61] = "TAPE_DECK",
|
||||
[0x62] = "CD",
|
||||
[0x63] = "CD_CHANGER",
|
||||
[0x74] = "AUDIO_AMP",
|
||||
[0x80] = "GPS",
|
||||
[0x85] = "VOICE_CTRL",
|
||||
[0xE0] = "CLIMATE_CTRL_DEV",
|
||||
[0xE5] = "TRIP_INFO",
|
||||
}
|
||||
|
||||
local f_from_device = ProtoField.uint8("avclan.from_device", "From device", base.HEX, known_devices)
|
||||
local f_to_device = ProtoField.uint8("avclan.to_device", "To device", base.HEX, known_devices)
|
||||
local f_active_device = ProtoField.uint8("avclan.active_device", "Active device", base.HEX, known_devices)
|
||||
|
||||
local known_actions_names = {
|
||||
-- LAN related
|
||||
["LIST_FUNCTIONS_REQ"] = 0x00,
|
||||
["LIST_FUNCTIONS_RESP"] = 0x10,
|
||||
["RESTART_LAN"] = 0x01,
|
||||
["LANCHECK_END_REQ"] = 0x08,
|
||||
["LANCHECK_END_RESP"] = 0x18,
|
||||
["LANCHECK_SCAN_REQ"] = 0x0a,
|
||||
["LANCHECK_SCAN_RESP"] = 0x1a,
|
||||
["LANCHECK_REQ"] = 0x0c,
|
||||
["LANCHECK_RESP"] = 0x1c,
|
||||
["PING_REQ"] = 0x20,
|
||||
["PING_RESP"] = 0x30,
|
||||
|
||||
-- Device switching
|
||||
["DISABLE_FUNCTION_REQ"] = 0x43,
|
||||
["DISABLE_FUNCTION_RESP"] = 0x53,
|
||||
["ENABLE_FUNCTION_REQ"] = 0x42,
|
||||
["ENABLE_FUNCTION_RESP"] = 0x52,
|
||||
|
||||
["ADVERTISE_FUNCTION"] = 0x45,
|
||||
["GENERAL_QUERY"] = 0x46,
|
||||
|
||||
-- Physical interface
|
||||
["EJECT"] = 0x80,
|
||||
["DISC_UP"] = 0x90,
|
||||
["DISC_DOWN"] = 0x91,
|
||||
["PWRVOL_KNOB_RIGHTHAND_TURN"] = 0x9c,
|
||||
["PWRVOL_KNOB_LEFTHAND_TURN"] = 0x9d,
|
||||
["TRACK_SEEK_UP"] = 0x94,
|
||||
["TRACK_SEEK_DOWN"] = 0x95,
|
||||
["CD_ENABLE_SCAN"] = 0xa6,
|
||||
["CD_DISABLE_SCAN"] = 0xa7,
|
||||
["CD_ENABLE_REPEAT"] = 0xa0,
|
||||
["CD_DISABLE_REPEAT"] = 0xa1,
|
||||
["CD_ENABLE_RANDOM"] = 0xb0,
|
||||
["CD_DISABLE_RANDOM"] = 0xb1,
|
||||
|
||||
-- CD functions
|
||||
-- Events
|
||||
["INSERTED_CD"] = 0x50,
|
||||
["REMOVED_CD"] = 0x51,
|
||||
|
||||
-- Requests
|
||||
["REQUEST_REPORT"] = 0xe0,
|
||||
["REQUEST_REPORT2"] = 0xe2,
|
||||
["REQUEST_LOADER2"] = 0xe4,
|
||||
["REQUEST_TRACK_NAME"] = 0xed,
|
||||
|
||||
-- Reports
|
||||
["REPORT"] = 0xf1,
|
||||
["REPORT2"] = 0xf2,
|
||||
["REPORT_LOADER"] = 0xf3,
|
||||
["REPORT_LOADER2"] = 0xf4,
|
||||
["REPORT_TOC"] = 0xf9,
|
||||
["REPORT_TRACK_NAME"] = 0xfd,
|
||||
}
|
||||
local known_actions = {
|
||||
-- LAN related
|
||||
[0x00] = "LIST_FUNCTIONS_REQ",
|
||||
[0x10] = "LIST_FUNCTIONS_RESP",
|
||||
[0x01] = "RESTART_LAN",
|
||||
[0x08] = "LANCHECK_END_REQ",
|
||||
[0x18] = "LANCHECK_END_RESP",
|
||||
[0x0a] = "LANCHECK_SCAN_REQ",
|
||||
[0x1a] = "LANCHECK_SCAN_RESP",
|
||||
[0x0c] = "LANCHECK_REQ",
|
||||
[0x1c] = "LANCHECK_RESP",
|
||||
[0x20] = "PING_REQ",
|
||||
[0x30] = "PING_RESP",
|
||||
|
||||
-- Used when HU is switching between Radio and CD
|
||||
[0x43] = "DISABLE_FUNCTION_REQ",
|
||||
[0x53] = "DISABLE_FUNCTION_RESP",
|
||||
[0x42] = "ENABLE_FUNCTION_REQ",
|
||||
[0x52] = "ENABLE_FUNCTION_RESP",
|
||||
|
||||
[0x45] = "ADVERTISE_FUNCTION",
|
||||
[0x46] = "GENERAL_QUERY",
|
||||
|
||||
-- Physical interface
|
||||
[0x80] = "EJECT",
|
||||
[0x90] = "DISC_UP",
|
||||
[0x91] = "DISC_DOWN",
|
||||
[0x9c] = "PWRVOL_KNOB_RIGHTHAND_TURN",
|
||||
[0x9d] = "PWRVOL_KNOB_LEFTHAND_TURN",
|
||||
[0x94] = "TRACK_SEEK_UP",
|
||||
[0x95] = "TRACK_SEEK_DOWN",
|
||||
[0xa6] = "CD_ENABLE_SCAN",
|
||||
[0xa7] = "CD_DISABLE_SCAN",
|
||||
[0xa0] = "CD_ENABLE_REPEAT",
|
||||
[0xa1] = "CD_DISABLE_REPEAT",
|
||||
[0xb0] = "CD_ENABLE_RANDOM",
|
||||
[0xb1] = "CD_DISABLE_RANDOM",
|
||||
|
||||
-- CD functions
|
||||
-- Events
|
||||
[0x50] = "INSERTED_CD",
|
||||
[0x51] = "REMOVED_CD",
|
||||
|
||||
-- Requests
|
||||
[0xe0] = "REQUEST_REPORT",
|
||||
[0xe2] = "REQUEST_REPORT2",
|
||||
[0xe4] = "REQUEST_LOADER2",
|
||||
[0xed] = "REQUEST_TRACK_NAME",
|
||||
|
||||
-- Reports
|
||||
[0xf1] = "REPORT",
|
||||
[0xf2] = "REPORT2",
|
||||
[0xf3] = "REPORT_LOADER",
|
||||
[0xf4] = "REPORT_LOADER2",
|
||||
[0xf9] = "REPORT_TOC",
|
||||
[0xfd] = "REPORT_TRACK_NAME",
|
||||
}
|
||||
|
||||
local f_action = ProtoField.uint8("avclan.action", "Action", base.HEX, known_actions)
|
||||
local f_functions = ProtoField.bytes("avclan.functions", "Functions", base.SPACE, "Device functions")
|
||||
|
||||
local f_ping_count = ProtoField.uint8("avclan.ping.count", "Ping count")
|
||||
|
||||
local f_radio_active = ProtoField.bool("avclan.radio.active", "Radio", base.NONE, {"ON", "OFF"})
|
||||
local f_radio_status = ProtoField.uint8("avclan.radio.status", "Radio status", base.HEX,
|
||||
{
|
||||
[0x00] = "OFF",
|
||||
[0x01] = "READY",
|
||||
[0x06] = "SCAN UP",
|
||||
[0x07] = "SCAN DOWN",
|
||||
[0x0A] = "AST SEARCH",
|
||||
[0x27] = "MANUAL"
|
||||
}
|
||||
)
|
||||
local f_radio_flags = ProtoField.uint8("avclan.radio.flags", "Radio flags")
|
||||
local f_radio_flags2 = ProtoField.uint8("avclan.radio.flags2", "Radio flags (byte 2)")
|
||||
|
||||
local f_radioflag_st = ProtoField.bool("avclan.radio.flags.st", "Stereo", 8, {"Set", "Not set"}, 0x04)
|
||||
local f_radioflag_ta = ProtoField.bool("avclan.radio.flags.ta", "TA", 8, {"Set", "Not set"}, 0x08)
|
||||
local f_radioflag_reg = ProtoField.bool("avclan.radio.flags.reg", "REG", 8, {"Set", "Not set"}, 0x10)
|
||||
local f_radioflag_af = ProtoField.bool("avclan.radio.flags.af", "AF", 8, {"Set", "Not set"}, 0x40)
|
||||
|
||||
local f_radio_band = ProtoField.uint8("avclan.radio.band", "Radio band", base.HEX,
|
||||
{[0x8] = "FM", [0xC] = "AM (Long-wave)", [0x0] = "AM (Medium-wave)"}, 0xF0)
|
||||
local f_radio_bandnumber = ProtoField.int8("avclan.radio.bandnumber", "Radio band number", base.DEC, nil, 0x0F)
|
||||
local f_radio_freq = ProtoField.uint16("avclan.radio.freq", "Radio frequency")
|
||||
|
||||
local f_amp_volume = ProtoField.uint8("avclan.amp.volume", "Volume", base.DEC)
|
||||
local f_amp_bass = ProtoField.uint8("avclan.amp.bass", "Bass", base.HEX, {
|
||||
[0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1",
|
||||
[0x10] = "0",
|
||||
[0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5"
|
||||
})
|
||||
local f_amp_mid = ProtoField.uint8("avclan.amp.mid", "Mid", base.HEX, {
|
||||
[0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1",
|
||||
[0x10] = "0",
|
||||
[0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5"
|
||||
})
|
||||
local f_amp_treble = ProtoField.uint8("avclan.amp.treble", "Treble", base.HEX, {
|
||||
[0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1",
|
||||
[0x10] = "0",
|
||||
[0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5"
|
||||
})
|
||||
local f_amp_fade = ProtoField.uint8("avclan.amp.fade", "Fade (forward/rear)", base.HEX, {
|
||||
[0x09] = "-7", [0x0A] = "-6", [0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1",
|
||||
[0x10] = "0",
|
||||
[0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5", [0x16] = "+6", [0x17] = "+7"
|
||||
})
|
||||
local f_amp_balance = ProtoField.uint8("avclan.amp.balance", "Balance (right/left)", base.HEX, {
|
||||
[0x09] = "-7", [0x0A] = "-6", [0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1",
|
||||
[0x10] = "0",
|
||||
[0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5", [0x16] = "+6", [0x17] = "+7"
|
||||
})
|
||||
|
||||
local f_cd_slots = ProtoField.uint8("avclan.cd.slots", "CD player disc slots")
|
||||
local f_cd_slot1 = ProtoField.bool("avclan.cd.slot1", "Slot 1", 6, {"Filled", "Empty"}, 0x01)
|
||||
local f_cd_slot2 = ProtoField.bool("avclan.cd.slot1", "Slot 2", 6, {"Filled", "Empty"}, 0x02)
|
||||
local f_cd_slot3 = ProtoField.bool("avclan.cd.slot1", "Slot 3", 6, {"Filled", "Empty"}, 0x04)
|
||||
local f_cd_slot4 = ProtoField.bool("avclan.cd.slot1", "Slot 4", 6, {"Filled", "Empty"}, 0x08)
|
||||
local f_cd_slot5 = ProtoField.bool("avclan.cd.slot1", "Slot 5", 6, {"Filled", "Empty"}, 0x10)
|
||||
local f_cd_slot6 = ProtoField.bool("avclan.cd.slot1", "Slot 6", 6, {"Filled", "Empty"}, 0x20)
|
||||
|
||||
local f_cd_state = ProtoField.uint8("avclan.cd.state", "CD player state")
|
||||
local f_cd_open = ProtoField.bool("avclan.cd.state.open", "OPEN", 8, nil, 0x01)
|
||||
local f_cd_err1 = ProtoField.bool("avclan.cd.state.err1", "ERR1", 8, nil, 0x02)
|
||||
local f_cd_seeking = ProtoField.bool("avclan.cd.state.seeking", "SEEKING", 8, nil, 0x08)
|
||||
local f_cd_playback = ProtoField.bool("avclan.cd.state.playback", "PLAYBACK", 8, nil, 0x10)
|
||||
local f_cd_seeking_track = ProtoField.bool("avclan.cd.state.seeking_track", "SEEKING_TRACK", 8, nil, 0x20)
|
||||
local f_cd_loading = ProtoField.bool("avclan.cd.state.loading", "LOADING", 8, nil, 0x80)
|
||||
|
||||
local f_cd_disc = ProtoField.uint8("avclan.cd.disc", "Current disc", base.DEC)
|
||||
local f_cd_track = ProtoField.uint8("avclan.cd.track", "Track number", base.HEX)
|
||||
local f_cd_min = ProtoField.uint8("avclan.cd.mins", "CD track play time, minutes", base.HEX)
|
||||
local f_cd_sec = ProtoField.uint8("avclan.cd.secs", "CD track play time, seconds", base.HEX)
|
||||
|
||||
local f_cd_flags = ProtoField.uint8("avclan.cd.flags", "CD player flags", base.HEX, {
|
||||
[0x02] = "DISK_RANDOM",
|
||||
[0x04] = "RANDOM",
|
||||
[0x08] = "DISK_REPEAT",
|
||||
[0x10] = "REPEAT",
|
||||
[0x20] = "DISK_SCAN",
|
||||
[0x40] = "SCAN",
|
||||
})
|
||||
|
||||
avclanproto.fields = {
|
||||
f_from_device,
|
||||
f_to_device,
|
||||
f_active_device,
|
||||
f_action,
|
||||
f_functions,
|
||||
f_ping_count,
|
||||
f_radio_active,
|
||||
f_radio_status,
|
||||
f_radio_flags,
|
||||
f_radio_flags2,
|
||||
f_radio_band,
|
||||
f_radio_bandnumber,
|
||||
f_radio_freq,
|
||||
f_radioflag_af,
|
||||
f_radioflag_reg,
|
||||
f_radioflag_st,
|
||||
f_radioflag_ta,
|
||||
f_amp_volume,
|
||||
f_amp_bass,
|
||||
f_amp_mid,
|
||||
f_amp_treble,
|
||||
f_amp_fade,
|
||||
f_amp_balance,
|
||||
f_cd_slots,
|
||||
f_cd_slot1,
|
||||
f_cd_slot2,
|
||||
f_cd_slot3,
|
||||
f_cd_slot4,
|
||||
f_cd_slot5,
|
||||
f_cd_slot6,
|
||||
f_cd_state,
|
||||
f_cd_open,
|
||||
f_cd_err1,
|
||||
f_cd_seeking,
|
||||
f_cd_playback,
|
||||
f_cd_seeking_track,
|
||||
f_cd_loading,
|
||||
f_cd_disc,
|
||||
f_cd_track,
|
||||
f_cd_min,
|
||||
f_cd_sec,
|
||||
f_cd_flags,
|
||||
}
|
||||
|
||||
local pe_unhandled_msg = ProtoExpert.new("avclan.expert", "Message not decoded",
|
||||
expert.group.UNDECODED, expert.severity.WARN)
|
||||
-- local pe_lan_check = ProtoExpert.new("avclan.lan.expert", "")
|
||||
local pe_ping_req = ProtoExpert.new("avclan.ping_req.expert", "Ping request",
|
||||
expert.group.SEQUENCE, expert.severity.CHAT)
|
||||
local pe_ping_resp = ProtoExpert.new("avclan.ping_resp.expert", "Ping response",
|
||||
expert.group.SEQUENCE, expert.severity.CHAT)
|
||||
|
||||
avclanproto.experts = {
|
||||
pe_unhandled_msg,
|
||||
pe_ping_req,
|
||||
pe_ping_resp,
|
||||
}
|
||||
|
||||
local field_from_device = Field.new("avclan.from_device")
|
||||
local field_to_device = Field.new("avclan.to_device")
|
||||
local field_action = Field.new("avclan.action")
|
||||
local field_radio_freq = Field.new("avclan.radio.freq")
|
||||
local field_radio_band = Field.new("avclan.radio.band")
|
||||
local field_cd_disc = Field.new("avclan.cd.disc")
|
||||
local field_cd_track = Field.new("avclan.cd.track")
|
||||
local field_cd_min = Field.new("avclan.cd.mins")
|
||||
local field_cd_sec = Field.new("avclan.cd.secs")
|
||||
|
||||
function avclanproto.dissector(buffer, pinfo, tree)
|
||||
local length = buffer:len()
|
||||
if length == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
iebusproto.dissector(buffer, pinfo, tree)
|
||||
|
||||
local subtree = tree:add(avclanproto, buffer(7,-1), "AVCLAN message")
|
||||
local offset = 7
|
||||
if buffer(7,1):uint() == 0 then
|
||||
offset = 8
|
||||
subtree:add(f_from_device, buffer(offset+0,1))
|
||||
subtree:add(f_to_device, buffer(offset+1,1))
|
||||
else
|
||||
subtree:add(f_from_device, buffer(offset+0,1))
|
||||
subtree:add(f_to_device, buffer(offset+1,1))
|
||||
end
|
||||
|
||||
local from_device = field_from_device().value
|
||||
local to_device = field_to_device().value
|
||||
|
||||
if from_device == known_devices_names["CMD_SW"] then
|
||||
subtree:add(f_action, buffer(offset+2,1))
|
||||
elseif from_device == known_devices_names["COMMUNICATION v1"] or
|
||||
from_device == known_devices_names["COMMUNICATION v2"] then
|
||||
if to_device == known_devices_names["COMM_CTRL"] then
|
||||
subtree:add(f_action, buffer(offset+2,1))
|
||||
local action = field_action().value
|
||||
if action == known_actions_names["ADVERTISE_FUNCTION"] then
|
||||
subtree:add(f_active_device, buffer(offset+3,1))
|
||||
elseif action == known_actions_names["PING_REQ"] then
|
||||
subtree:add(f_ping_count, buffer(offset+3,1))
|
||||
subtree:add_proto_expert_info(pe_ping_req, "Ping request " .. buffer(offset+3,1):uint())
|
||||
elseif known_actions[action] then
|
||||
else
|
||||
subtree:add_proto_expert_info(pe_unhandled_msg)
|
||||
end
|
||||
else
|
||||
subtree:add_proto_expert_info(pe_unhandled_msg)
|
||||
end
|
||||
elseif from_device == known_devices_names["COMM_CTRL"] then
|
||||
if to_device == known_devices_names["COMMUNICATION v1"] or
|
||||
to_device == known_devices_names["COMMUNICATION v2"] then
|
||||
local action_tree = subtree:add(f_action, buffer(offset+2,1))
|
||||
local action = field_action().value
|
||||
if action == known_actions_names["PING_RESP"] then
|
||||
subtree:add(f_ping_count, buffer(offset+3,1))
|
||||
subtree:add_proto_expert_info(pe_ping_resp, "Ping response " .. buffer(offset+3,1):uint())
|
||||
elseif action == known_actions_names["LIST_FUNCTIONS_RESP"] then
|
||||
local functions = action_tree:add(f_functions, buffer(offset+3))
|
||||
functions:append_text(" (")
|
||||
for v = 0,(buffer:bytes(offset+3)):len()-1 do
|
||||
if known_devices[buffer(offset+3+v,1):uint()] then
|
||||
functions:append_text(" " .. known_devices[buffer(offset+3+v,1):uint()])
|
||||
else
|
||||
functions:append_text(" UNKNOWN_DEVICE")
|
||||
end
|
||||
end
|
||||
functions:append_text(" ) ")
|
||||
elseif known_actions[action] then
|
||||
else
|
||||
subtree:add_proto_expert_info(pe_unhandled_msg)
|
||||
end
|
||||
elseif to_device == known_actions_names["LANCHECK_SCAN_REQ"] or
|
||||
to_device == known_actions_names["LANCHECK_REQ"] or
|
||||
to_device == known_actions_names["LANCHECK_END_REQ"] then
|
||||
subtree:add(f_action, buffer(offset+1,1))
|
||||
elseif to_device == 0x00 then
|
||||
subtree:add(f_action, buffer(offset+2,1))
|
||||
else
|
||||
subtree:add_proto_expert_info(pe_unhandled_msg)
|
||||
end
|
||||
elseif from_device == known_devices_names["STATUS"] then
|
||||
subtree:add(f_action, buffer(offset+2,1))
|
||||
elseif from_device == known_devices_names["TUNER"] then
|
||||
subtree:add(f_action, buffer(offset+2,1))
|
||||
local radiotree = subtree:add(avclanproto, buffer(offset,10), "Device: Radio")
|
||||
radiotree:add_le(f_radio_active, buffer(offset+3,1))
|
||||
radiotree:add_le(f_radio_status, buffer(offset+4,1))
|
||||
radiotree:add_le(f_radio_band, buffer(offset+5,1))
|
||||
radiotree:add(f_radio_bandnumber, buffer(offset+5,1))
|
||||
local freqtree = radiotree:add(f_radio_freq, buffer(offset+6,2))
|
||||
local radio_band = buffer(offset+5,1):uint()
|
||||
local freq = field_radio_freq().value
|
||||
if bit32.band(radio_band, 0xF0) == 0x80 then
|
||||
freqtree:append_text(" (" .. 87.5+(freq-1)*.05 .. " MHz)")
|
||||
elseif bit32.band(radio_band, 0xF0) == 0xC0 then
|
||||
freqtree:append_text(" (" .. 153+(freq-1)*1 .. " kHz)")
|
||||
elseif bit32.band(radio_band, 0xF0) == 0x00 then
|
||||
freqtree:append_text(" (" .. 522+(freq-1)*9 .. " kHz)")
|
||||
end
|
||||
|
||||
local flags = radiotree:add(f_radio_flags, buffer(15,1))
|
||||
flags:add(f_radioflag_st, buffer(15,1))
|
||||
flags:add(f_radioflag_ta, buffer(15,1))
|
||||
flags:add(f_radioflag_reg, buffer(15,1))
|
||||
flags:add(f_radioflag_af, buffer(15,1))
|
||||
radiotree:add(f_radio_flags2, buffer(16,1))
|
||||
elseif from_device == known_devices_names["AUDIO_AMP"] then
|
||||
subtree:add(f_action, buffer(offset+2,1))
|
||||
local amptree = subtree:add(avclanproto, buffer(offset,10), "Device: Audio amplifier")
|
||||
|
||||
amptree:add(f_amp_volume, buffer(offset+4,1))
|
||||
amptree:add(f_amp_balance, buffer(offset+5,1))
|
||||
amptree:add(f_amp_fade, buffer(offset+6,1))
|
||||
amptree:add(f_amp_bass, buffer(offset+7,1))
|
||||
amptree:add(f_amp_mid, buffer(offset+8,1))
|
||||
amptree:add(f_amp_treble, buffer(offset+9,1))
|
||||
elseif from_device == known_devices_names["CD"] or
|
||||
from_device == known_devices_names["CD_CHANGER"] or
|
||||
from_device == known_devices_names["CD_CHANGER2"] then
|
||||
subtree:add(f_action, buffer(offset+2,1))
|
||||
|
||||
local action = field_action().value
|
||||
if action == known_actions_names["REPORT"] then
|
||||
local cdtree = subtree:add(avclanproto, buffer(offset,9), "Device: CD player")
|
||||
local cd_slots = cdtree:add(f_cd_slots, buffer(offset+3,1))
|
||||
cd_slots:add(f_cd_slot1, buffer(offset+3,1))
|
||||
cd_slots:add(f_cd_slot2, buffer(offset+3,1))
|
||||
cd_slots:add(f_cd_slot3, buffer(offset+3,1))
|
||||
cd_slots:add(f_cd_slot4, buffer(offset+3,1))
|
||||
cd_slots:add(f_cd_slot5, buffer(offset+3,1))
|
||||
cd_slots:add(f_cd_slot6, buffer(offset+3,1))
|
||||
|
||||
local cd_state = cdtree:add(f_cd_state, buffer(offset+4,1))
|
||||
cd_state:add(f_cd_open, buffer(offset+4,1))
|
||||
cd_state:add(f_cd_err1, buffer(offset+4,1))
|
||||
cd_state:add(f_cd_seeking, buffer(offset+4,1))
|
||||
cd_state:add(f_cd_playback, buffer(offset+4,1))
|
||||
cd_state:add(f_cd_seeking_track, buffer(offset+4,1))
|
||||
cd_state:add(f_cd_loading, buffer(offset+4,1))
|
||||
local cd_status = cdtree:add(avclanproto, buffer(offset+5,-1), "")
|
||||
cd_status:add(f_cd_disc, buffer(offset+5,1))
|
||||
cd_status:add(f_cd_track, buffer(offset+6,1))
|
||||
cd_status:add(f_cd_min, buffer(offset+7,1))
|
||||
cd_status:add(f_cd_sec, buffer(offset+8,1))
|
||||
cd_status:append_text("Disc " .. field_cd_disc().value .. ", ")
|
||||
cd_status:append_text("track " .. tostring(buffer(offset+6,1)):gsub("(.)(.)", "%1%2") .. ", ")
|
||||
cd_status:append_text("time " .. tostring(buffer(offset+7,1)):gsub("0x(.)(.)", "%1%2") .. ":")
|
||||
cd_status:append_text(tostring(buffer(offset+8,1)):gsub("(.)(.)", "%1%2"))
|
||||
cdtree:add(f_cd_flags, buffer(offset+9,1))
|
||||
end
|
||||
else
|
||||
subtree:add_proto_expert_info(pe_unhandled_msg)
|
||||
end
|
||||
end
|
||||
|
||||
-- for i,v in ipairs(DissectorTable.list()) do print(v) end
|
||||
udlt:add(wtap.USER15, avclanproto)
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2020 Christian Rorvik <christian.rorvik@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,21 @@
|
||||
name = "PcapTools"
|
||||
uuid = "222fe7e8-3f39-464a-bf97-d9bbb753f246"
|
||||
authors = ["Christian Rorvik <christian.rorvik@gmail.com>"]
|
||||
version = "1.1.1"
|
||||
|
||||
[deps]
|
||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
|
||||
UnixTimes = "ab1a18e7-b408-4913-896c-624bb82ed7f4"
|
||||
UnsafeArrays = "c4a57d5a-5b31-53a6-b365-19f8c011fbd6"
|
||||
|
||||
[compat]
|
||||
UnixTimes = "1"
|
||||
UnsafeArrays = "1"
|
||||
julia = "1.4"
|
||||
|
||||
[extras]
|
||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||
|
||||
[targets]
|
||||
test = ["Test"]
|
||||
@@ -0,0 +1,5 @@
|
||||
# PcapTools
|
||||
|
||||
[](https://travis-ci.com/ancapdev/PcapTools.jl)
|
||||
|
||||
[](https://coveralls.io/github/ancapdev/PcapTools.jl)
|
||||
@@ -0,0 +1,26 @@
|
||||
module PcapTools
|
||||
|
||||
using Dates
|
||||
using Mmap
|
||||
using UnixTimes
|
||||
using UnsafeArrays
|
||||
|
||||
export PcapHeader, RecordHeader
|
||||
export PcapRecord
|
||||
export PcapReader, PcapStreamReader, PcapBufferReader
|
||||
export PcapWriter, PcapStreamWriter
|
||||
export LINKTYPE_NULL, LINKTYPE_ETHERNET
|
||||
export splitcap
|
||||
|
||||
abstract type PcapReader end
|
||||
abstract type PcapWriter end
|
||||
|
||||
include("pcap_header.jl")
|
||||
include("record_header.jl")
|
||||
include("record.jl")
|
||||
include("buffer_reader.jl")
|
||||
include("stream_reader.jl")
|
||||
include("stream_writer.jl")
|
||||
include("splitcap.jl")
|
||||
|
||||
end
|
||||
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
Reads pcap data from an array of bytes.
|
||||
"""
|
||||
mutable struct PcapBufferReader <: PcapReader
|
||||
data::Vector{UInt8}
|
||||
raw_header::Vector{UInt8}
|
||||
header::PcapHeader
|
||||
offset::Int64
|
||||
mark::Int64
|
||||
usec_mul::Int64
|
||||
bswapped::Bool
|
||||
|
||||
@doc """
|
||||
PcapBufferReader(data::Vector{UInt8})
|
||||
|
||||
Create reader over `data`. Will read and process pcap header,
|
||||
and yield records through `read(::PcapBufferReader)`.
|
||||
"""
|
||||
function PcapBufferReader(data::Vector{UInt8})
|
||||
length(data) < sizeof(PcapHeader) && throw(EOFError())
|
||||
rh = data[1:sizeof(PcapHeader)]
|
||||
h = unsafe_load(Ptr{PcapHeader}(pointer(data)))
|
||||
h, bswapped, nanotime = process_header(h)
|
||||
new(data, rh, h, sizeof(h), -1, nanotime ? 1 : 1000, bswapped)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
PcapBufferReader(path::AbstractString)
|
||||
|
||||
Memory map file in `path` and create PcapBufferReader over its content.
|
||||
"""
|
||||
function PcapBufferReader(path::AbstractString)
|
||||
io = open(path)
|
||||
data = Mmap.mmap(io)
|
||||
PcapBufferReader(data)
|
||||
end
|
||||
|
||||
function Base.close(x::PcapBufferReader)
|
||||
x.data = UInt8[]
|
||||
x.offset = 0
|
||||
nothing
|
||||
end
|
||||
|
||||
Base.length(x::PcapBufferReader) = length(x.data)
|
||||
Base.position(x::PcapBufferReader) = x.offset; nothing
|
||||
Base.seek(x::PcapBufferReader, pos) = x.offset = pos; nothing
|
||||
|
||||
function Base.mark(x::PcapBufferReader)
|
||||
x.mark = x.offset
|
||||
x.mark
|
||||
end
|
||||
|
||||
function Base.unmark(x::PcapBufferReader)
|
||||
if x.mark >= 0
|
||||
x.mark = -1
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
Base.ismarked(x::PcapBufferReader) = x.mark >= 0
|
||||
|
||||
function Base.reset(x::PcapBufferReader)
|
||||
!ismarked(x) && error("PcapBufferReader not marked")
|
||||
x.offset = x.mark
|
||||
x.mark = -1
|
||||
x.offset
|
||||
end
|
||||
|
||||
Base.eof(x::PcapBufferReader) = (length(x) - x.offset) < sizeof(RecordHeader)
|
||||
|
||||
"""
|
||||
read(x::PcapBufferReader) -> PcapRecord
|
||||
|
||||
Read one record from pcap data.
|
||||
Throws `EOFError` if no more data available.
|
||||
"""
|
||||
@inline function Base.read(x::PcapBufferReader)
|
||||
eof(x) && throw(EOFError())
|
||||
record_offset = x.offset
|
||||
p = pointer(x.data) + record_offset
|
||||
GC.@preserve x begin
|
||||
h = unsafe_load(Ptr{RecordHeader}(p))
|
||||
end
|
||||
if x.bswapped
|
||||
h = bswap(h)
|
||||
end
|
||||
t1 = (h.ts_sec + x.header.thiszone) * 1_000_000_000
|
||||
t2 = Int64(h.ts_usec) * x.usec_mul
|
||||
t = UnixTime(Dates.UTInstant(Nanosecond(t1 + t2)))
|
||||
x.offset += sizeof(RecordHeader) + h.incl_len
|
||||
x.offset > length(x) && error("Insufficient data in pcap record")
|
||||
PcapRecord(h, t, x.data, record_offset)
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
# NOTE: Not using @enum because it craps out when displaying unknown values
|
||||
const LINKTYPE_NULL = UInt32(0)
|
||||
const LINKTYPE_ETHERNET = UInt32(1)
|
||||
|
||||
struct PcapHeader
|
||||
magic::UInt32
|
||||
version_major::UInt16
|
||||
version_minor::UInt16
|
||||
thiszone::Int32
|
||||
sigfigs::UInt32
|
||||
snaplen::UInt32
|
||||
linktype::UInt32
|
||||
end
|
||||
|
||||
function Base.bswap(x::PcapHeader)
|
||||
PcapHeader(
|
||||
bswap(x.magic),
|
||||
bswap(x.version_major),
|
||||
bswap(x.version_minor),
|
||||
bswap(x.thiszone),
|
||||
bswap(x.sigfigs),
|
||||
bswap(x.snaplen),
|
||||
bswap(x.linktype))
|
||||
end
|
||||
|
||||
function process_header(x::PcapHeader)
|
||||
if x.magic == 0xa1b2c3d4
|
||||
bswapped = false
|
||||
nanotime = false
|
||||
elseif x.magic == 0xd4c3b2a1
|
||||
bswapped = true
|
||||
nanotime = false
|
||||
elseif x.magic == 0xa1b23c4d
|
||||
bswapped = false
|
||||
nanotime = true
|
||||
elseif x.magic == 0x4d3cb2a1
|
||||
bswapped = true
|
||||
nanotime = true
|
||||
else
|
||||
throw(ArgumentError("Invalid pcap header"))
|
||||
end
|
||||
if bswapped
|
||||
x = bswap(x)
|
||||
end
|
||||
x, bswapped, nanotime
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
Record of pcap data
|
||||
"""
|
||||
struct PcapRecord
|
||||
header::RecordHeader
|
||||
timestamp::UnixTime
|
||||
underlying_data::Vector{UInt8}
|
||||
record_offset::Int
|
||||
end
|
||||
|
||||
@inline function record_field_(x::PcapRecord, ::Val{:data})
|
||||
offset = getfield(x, :record_offset) + sizeof(RecordHeader)
|
||||
len = Int(getfield(x, :header).incl_len)
|
||||
UnsafeArray{UInt8, 1}(pointer(getfield(x, :underlying_data)) + offset, (len,))
|
||||
end
|
||||
|
||||
@inline function record_field_(x::PcapRecord, ::Val{:raw})
|
||||
offset = getfield(x, :record_offset)
|
||||
len = sizeof(RecordHeader) + getfield(x, :header).incl_len
|
||||
UnsafeArray{UInt8, 1}(pointer(getfield(x, :underlying_data)) + offset, (len,))
|
||||
end
|
||||
|
||||
@inline record_field_(x::PcapRecord, ::Val{f}) where {f} = getfield(x, f)
|
||||
|
||||
@inline Base.getproperty(x::PcapRecord, f::Symbol) = record_field_(x, Val(f))
|
||||
@@ -0,0 +1,14 @@
|
||||
struct RecordHeader
|
||||
ts_sec::UInt32
|
||||
ts_usec::UInt32
|
||||
incl_len::UInt32
|
||||
orig_len::UInt32
|
||||
end
|
||||
|
||||
function Base.bswap(x::RecordHeader)
|
||||
RecordHeader(
|
||||
bswap(x.ts_sec),
|
||||
bswap(x.ts_usec),
|
||||
bswap(x.incl_len),
|
||||
bswap(x.orig_len))
|
||||
end
|
||||
@@ -0,0 +1,102 @@
|
||||
strip_nothing_(::Type{Union{Nothing, T}}) where T = T
|
||||
strip_nothing_(::Type{T}) where T = T
|
||||
|
||||
progress_noop_(n) = nothing
|
||||
|
||||
mutable struct SplitCapOutput{S}
|
||||
work_buffer::Vector{UInt8}
|
||||
complete_buffers::Channel{Vector{UInt8}}
|
||||
stream::S
|
||||
end
|
||||
|
||||
# Since Julia (as of 1.5) doesn't support task migration,
|
||||
# continue with new task after each buffer write, to rebalance across threads
|
||||
function write_one_and_continue_(output::SplitCapOutput, free_buffers::Channel, pending::Threads.Atomic{Int})
|
||||
i = iterate(output.complete_buffers)
|
||||
if i === nothing
|
||||
Threads.atomic_sub!(pending, 1)
|
||||
return nothing
|
||||
end
|
||||
b, _ = i
|
||||
write(output.stream, b)
|
||||
empty!(b)
|
||||
put!(free_buffers, b)
|
||||
Threads.@spawn write_one_and_continue_($output, $free_buffers, $pending)
|
||||
end
|
||||
|
||||
function splitcap(
|
||||
::Type{KeyType},
|
||||
::Type{StreamType},
|
||||
reader::PcapReader,
|
||||
record2key,
|
||||
key2stream,
|
||||
progress_callback = progress_noop_;
|
||||
own_streams::Bool = true
|
||||
) where {KeyType, StreamType}
|
||||
buffer_size = 1024 * 1024 * 2
|
||||
max_pending_buffers = 4
|
||||
outputs = Dict{KeyType, SplitCapOutput{StreamType}}()
|
||||
free_buffers = Channel{Vector{UInt8}}(Inf)
|
||||
n = 0
|
||||
pending = Threads.Atomic{Int}(0)
|
||||
try
|
||||
while !eof(reader)
|
||||
record = read(reader)
|
||||
dst = record2key(record)
|
||||
if dst isa KeyType
|
||||
output = get!(outputs, dst) do
|
||||
stream = key2stream(dst)
|
||||
buffer = sizehint!(UInt8[], buffer_size + 1500)
|
||||
own_streams && append!(buffer, reader.raw_header)
|
||||
output = SplitCapOutput{StreamType}(
|
||||
buffer,
|
||||
Channel{Vector{UInt8}}(max_pending_buffers),
|
||||
stream)
|
||||
Threads.atomic_add!(pending, 1)
|
||||
Threads.@spawn write_one_and_continue_($output, $free_buffers, $pending)
|
||||
output
|
||||
end
|
||||
append!(output.work_buffer, record.raw)
|
||||
if length(output.work_buffer) >= buffer_size
|
||||
put!(output.complete_buffers, output.work_buffer)
|
||||
if isready(free_buffers)
|
||||
output.work_buffer = take!(free_buffers)
|
||||
else
|
||||
output.work_buffer = sizehint!(UInt8[], buffer_size + 1500)
|
||||
end
|
||||
end
|
||||
end
|
||||
n += 1
|
||||
progress_callback(n)
|
||||
GC.safepoint()
|
||||
end
|
||||
for output in values(outputs)
|
||||
if !isempty(output.work_buffer)
|
||||
put!(output.complete_buffers, output.work_buffer)
|
||||
end
|
||||
close(output.complete_buffers)
|
||||
end
|
||||
while pending[] != 0
|
||||
sleep(0.1)
|
||||
end
|
||||
finally
|
||||
if own_streams
|
||||
for output in values(outputs)
|
||||
close(output.stream)
|
||||
end
|
||||
end
|
||||
end
|
||||
nothing
|
||||
end
|
||||
|
||||
function splitcap(
|
||||
reader::PcapReader,
|
||||
record2key,
|
||||
key2stream,
|
||||
progress_callback = progress_noop_;
|
||||
kwargs...
|
||||
)
|
||||
KeyType = strip_nothing_(Core.Compiler.return_type(record2key, Tuple{PcapRecord}))
|
||||
StreamType = Core.Compiler.return_type(key2stream, Tuple{KeyType})
|
||||
splitcap(KeyType, StreamType, reader, record2key, key2stream, progress_callback; kwargs...)
|
||||
end
|
||||
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
Reads pcap data from a stream.
|
||||
"""
|
||||
mutable struct PcapStreamReader{Src <: IO} <: PcapReader
|
||||
src::Src
|
||||
raw_header::Vector{UInt8}
|
||||
header::PcapHeader
|
||||
usec_mul::Int64
|
||||
bswapped::Bool
|
||||
record_buffer::Vector{UInt8}
|
||||
|
||||
@doc """
|
||||
PcapStreamReader(src::IO)
|
||||
|
||||
Create reader over `src`. Will read and process pcap header,
|
||||
and yield records through `read(::PcapStreamReader)`.
|
||||
"""
|
||||
function PcapStreamReader(src::Src) where {Src <: IO}
|
||||
raw_header = read(src, sizeof(PcapHeader))
|
||||
length(raw_header) != sizeof(PcapHeader) && throw(EOFError())
|
||||
h = GC.@preserve raw_header unsafe_load(Ptr{PcapHeader}(pointer(raw_header)))
|
||||
header, bswapped, nanotime = process_header(h)
|
||||
new{Src}(src, raw_header, header, nanotime ? 1 : 1000, bswapped, zeros(UInt8, 9000 + sizeof(RecordHeader)))
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
PcapStreamReader(path)
|
||||
|
||||
Open file at `path` and create PcapStreamReader over its content.
|
||||
"""
|
||||
PcapStreamReader(path::AbstractString) = PcapStreamReader(open(path))
|
||||
|
||||
Base.close(x::PcapStreamReader) = close(x.src)
|
||||
Base.position(x::PcapStreamReader) = position(x.src)
|
||||
Base.seek(x::PcapStreamReader, pos) = seek(x.src, pos)
|
||||
Base.mark(x::PcapStreamReader) = mark(x.src)
|
||||
Base.unmark(x::PcapStreamReader) = unmark(x.src)
|
||||
Base.ismarked(x::PcapStreamReader) = ismarked(x.src)
|
||||
Base.reset(x::PcapStreamReader) = reset(x.src)
|
||||
Base.eof(x::PcapStreamReader) = eof(x.src)
|
||||
|
||||
"""
|
||||
read(x::PcapStreamReader) -> PcapRecord
|
||||
|
||||
Read one record from pcap data. Record is valid until next read().
|
||||
Throws `EOFError` if no more data available.
|
||||
"""
|
||||
function Base.read(x::PcapStreamReader)
|
||||
p = pointer(x.record_buffer)
|
||||
GC.@preserve x begin
|
||||
unsafe_read(x.src, p, sizeof(RecordHeader))
|
||||
h = unsafe_load(Ptr{RecordHeader}(p))
|
||||
if x.bswapped
|
||||
h = bswap(h)
|
||||
end
|
||||
unsafe_read(x.src, p + sizeof(RecordHeader), h.incl_len)
|
||||
end
|
||||
t1 = (h.ts_sec + x.header.thiszone) * 1_000_000_000
|
||||
t2 = Int64(h.ts_usec) * x.usec_mul
|
||||
t = UnixTime(Dates.UTInstant(Nanosecond(t1 + t2)))
|
||||
PcapRecord(h, t, x.record_buffer, 0)
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
struct PcapStreamWriter{Dst <: IO} <: PcapWriter
|
||||
dst::Dst
|
||||
|
||||
function PcapStreamWriter{Dst}(dst::Dst; thiszone = 0, snaplen = 65535, linktype = LINKTYPE_ETHERNET) where {Dst <: IO}
|
||||
h = PcapHeader(
|
||||
0xa1b23c4d,
|
||||
0x0002,
|
||||
0x0004,
|
||||
thiszone,
|
||||
0,
|
||||
snaplen,
|
||||
linktype)
|
||||
write(dst, reinterpret(UInt8, [h]))
|
||||
new(dst)
|
||||
end
|
||||
end
|
||||
|
||||
PcapStreamWriter(io::IO; kwargs...) = PcapStreamWriter{typeof(io)}(io; kwargs...)
|
||||
PcapStreamWriter(path::AbstractString; kwargs...) = PcapStreamWriter(open(path, "w"); kwargs...)
|
||||
|
||||
Base.close(x::PcapStreamWriter) = close(x.dst)
|
||||
|
||||
function Base.write(x::PcapStreamWriter, timestamp::UnixTime, data)
|
||||
sec, nsec = fldmod(Dates.value(timestamp), 1_000_000_000)
|
||||
data_length = length(data)
|
||||
h = RecordHeader(sec, nsec, data_length, data_length)
|
||||
write(x.dst, reinterpret(UInt8, [h]))
|
||||
write(x.dst, collect(data))
|
||||
nothing
|
||||
end
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
module AVCLANPipe
|
||||
|
||||
using PcapTools, Dates, UnixTimes
|
||||
|
||||
export AVCLANframe, avclan_text_to_pcap, tobytes
|
||||
|
||||
mutable struct AVCLANframe
|
||||
broadcast::Bool
|
||||
controller_addr::UInt16
|
||||
peripheral_addr::UInt16
|
||||
control::UInt8
|
||||
length::UInt8
|
||||
data::NTuple{32,UInt8}
|
||||
end
|
||||
|
||||
AVCLANframe() = AVCLANframe(false, 0x0000, 0x0000, 0xf, 0x0, ntuple(x -> 0x0, 32))
|
||||
|
||||
function Base.tryparse(::Type{AVCLANframe}, str::String)
|
||||
vals = split(str)
|
||||
|
||||
broadcast = tryparse(Bool, vals[1])
|
||||
isnothing(broadcast) && return nothing
|
||||
|
||||
controller_addr = tryparse(UInt16, vals[2])
|
||||
isnothing(controller_addr) && return nothing
|
||||
|
||||
peripheral_addr = tryparse(UInt16, vals[3])
|
||||
isnothing(peripheral_addr) && return nothing
|
||||
|
||||
control = tryparse(UInt8, vals[4])
|
||||
isnothing(control) && return nothing
|
||||
|
||||
len = tryparse(UInt8, vals[5])
|
||||
isnothing(len) && return nothing
|
||||
|
||||
if (length(vals) - 5) != len || len > 32
|
||||
return nothing
|
||||
end
|
||||
_data = tryparse.(UInt8, vals[6:end])
|
||||
data = ntuple(i -> checkindex(Bool, axes(_data, 1), i) ? _data[i] : 0x0, 32)
|
||||
|
||||
return AVCLANframe(broadcast, controller_addr, peripheral_addr, control, len, data)
|
||||
end
|
||||
|
||||
function tobytes(frame::AVCLANframe)
|
||||
data = Vector{UInt8}(undef, 0)
|
||||
push!(data, frame.broadcast)
|
||||
append!(data, reverse(reinterpret(reshape, UInt8, [frame.controller_addr])),
|
||||
reverse(reinterpret(reshape, UInt8, [frame.peripheral_addr])),
|
||||
reinterpret(reshape, UInt8, [frame.control]),
|
||||
reinterpret(reshape, UInt8, [frame.length]),
|
||||
frame.data[1:frame.length])
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function avclan_text_to_pcap(textlog::String, pcap_fn::String)
|
||||
pcapstream = PcapStreamWriter(pcap_fn; snaplen=64, linktype = 162)
|
||||
t = UnixTime(now())
|
||||
|
||||
for line in eachline(textlog)
|
||||
frame = tryparse(AVCLANframe, line)
|
||||
t += Microsecond(rand(1:15))
|
||||
if !isnothing(frame)
|
||||
write(pcapstream, t, tobytes(frame))
|
||||
end
|
||||
end
|
||||
close(pcapstream)
|
||||
|
||||
return pcap_fn
|
||||
end
|
||||
|
||||
end # module AVCLANPipe
|
||||
@@ -0,0 +1,55 @@
|
||||
using AVCLANPipe, PcapTools, Dates, UnixTimes
|
||||
|
||||
file = ARGS[1]
|
||||
outfile = ARGS[2]
|
||||
|
||||
function convert_fields_to_pcap(file, outfile)
|
||||
|
||||
io = open(file, "r")
|
||||
outio = PcapStreamWriter(outfile; snaplen=64, linktype = 162)
|
||||
|
||||
packet = AVCLANframe()
|
||||
packetcomplete = false
|
||||
local t::UnixTime
|
||||
i = 1
|
||||
|
||||
for line in readlines(io)
|
||||
m = match(r"(\d+)-(\d+) IEBus: Raw Fields: (?<field>\w+)(:? ((?<hex>0x[0-9a-f]{2,3})|WRITE_DATA|Length: (?<length>\d+)))?",
|
||||
line)
|
||||
|
||||
if m[:field] == "Broadcast"
|
||||
packet.broadcast = true
|
||||
t = UnixTime(1970,1,1,0,0,0,0, something(tryparse(Int, m[1]), 0))
|
||||
elseif m[:field] == "Unicast"
|
||||
packet.broadcast = false
|
||||
t = UnixTime(1970,1,1,0,0,0,0, something(tryparse(Int, m[1]), 0))
|
||||
elseif m[:field] == "Master"
|
||||
packet.controller_addr = tryparse(UInt16, m[:hex])
|
||||
elseif m[:field] == "Slave"
|
||||
packet.peripheral_addr = tryparse(UInt16, m[:hex])
|
||||
elseif m[:field] == "Data"
|
||||
if !isnothing(m[:length])
|
||||
packet.length = convert(UInt8, tryparse(Int, m[:length]))
|
||||
else
|
||||
data = collect(packet.data)
|
||||
data[i] = tryparse(UInt8, m[:hex])
|
||||
packet.data = ntuple(n -> data[n], 32)
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
if i > packet.length
|
||||
packetcomplete = true
|
||||
end
|
||||
|
||||
if packetcomplete
|
||||
packetcomplete = false
|
||||
i = 1
|
||||
write(outio, t, tobytes(packet))
|
||||
end
|
||||
end
|
||||
|
||||
close(io)
|
||||
close(outio)
|
||||
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
include("AVCLANPipe.jl")
|
||||
|
||||
using .AVCLANPipe, PcapTools, Dates, UnixTimes, LibSerialPort
|
||||
|
||||
serial_port="/dev/ttyUSB0"
|
||||
baud=1200000
|
||||
|
||||
pcapstream = PcapStreamWriter(stdout; snaplen=64, linktype=162)
|
||||
serial = LibSerialPort.open(serial_port, baud)
|
||||
set_flow_control(serial) # Disable flow-control (stops it from eating raw byte 0x11)
|
||||
|
||||
write(serial, 'X')
|
||||
buf = IOBuffer()
|
||||
|
||||
function quit(serialio=serial)
|
||||
close(serialio)
|
||||
exit()
|
||||
end
|
||||
|
||||
while true
|
||||
iswritable(stdout) || isopen(stdout) || quit()
|
||||
if bytesavailable(serial) > 0
|
||||
t = UnixTime(now())
|
||||
line = readline(serial)
|
||||
# allthebytes = Vector{UInt8}(undef, bytesavailable(serial))
|
||||
# readbytes!(serial, allthebytes)
|
||||
# write(buf, allthebytes)
|
||||
# l = findfirst(==('\n'), buf.data)
|
||||
# if !isnothing(l)
|
||||
# println(stderr, "fucku")
|
||||
# bytes = Vector{UInt8}(undef, l)
|
||||
# seekstart(buf)
|
||||
# readbytes!(buf, bytes)
|
||||
# seekend(buf)
|
||||
bytes = Vector{UInt8}(line)
|
||||
if bytes[1] == 0x10 && bytes[end] == 0x17
|
||||
write(pcapstream, t, bytes[2:end-1])
|
||||
else
|
||||
@error line
|
||||
end
|
||||
# end
|
||||
# println(stderr, "wtf")
|
||||
end
|
||||
yield()
|
||||
end
|
||||
Reference in New Issue
Block a user