summaryrefslogtreecommitdiffstats
path: root/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0
diff options
context:
space:
mode:
Diffstat (limited to 'meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0')
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/CHANGELOG.md15
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.lock1049
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.toml41
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-APACHE202
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause26
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/README.md147
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs268
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs393
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs96
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs2145
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs144
11 files changed, 4526 insertions, 0 deletions
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/CHANGELOG.md b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/CHANGELOG.md
new file mode 100755
index 00000000..6c84888c
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/CHANGELOG.md
@@ -0,0 +1,15 @@
+# Changelog
+## Unreleased
+
+### Added
+
+### Changed
+
+### Fixed
+
+### Deprecated
+
+## v0.1.0
+
+First release
+
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.lock b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.lock
new file mode 100755
index 00000000..50985ab2
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.lock
@@ -0,0 +1,1049 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
+[[package]]
+name = "arc-swap"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
+
+[[package]]
+name = "assert_matches"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "byte_conv"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "649972315d4931137a26fc2bf3ca95ee257ad796a5b57bdeb04205c91a4b5780"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.5.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "embedded-can"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438"
+dependencies = [
+ "nb",
+]
+
+[[package]]
+name = "enumn"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
+name = "env_filter"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "humantime",
+ "log",
+]
+
+[[package]]
+name = "epoll"
+version = "4.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
+dependencies = [
+ "bitflags 2.6.0",
+ "libc",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+ "num_cpus",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-timer"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "indexmap"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memoffset"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "nb"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
+
+[[package]]
+name = "neli"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1100229e06604150b3becd61a4965d5c70f3be1759544ea7274166f4be41ef43"
+dependencies = [
+ "byteorder",
+ "libc",
+ "log",
+ "neli-proc-macros",
+]
+
+[[package]]
+name = "neli-proc-macros"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4"
+dependencies = [
+ "either",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+ "memoffset",
+ "pin-utils",
+]
+
+[[package]]
+name = "nix"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
+dependencies = [
+ "bitflags 2.6.0",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "queues"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1475abae4f8ad4998590fe3acfe20104f0a5d48fc420c817cd2c09c3f56151f0"
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "relative-path"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
+
+[[package]]
+name = "rstest"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b423f0e62bdd61734b67cd21ff50871dfaeb9cc74f869dcd6af974fbcb19936"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "rstest_macros",
+ "rustc_version",
+]
+
+[[package]]
+name = "rstest_macros"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5e1711e7d14f74b12a58411c542185ef7fb7f2e7f8ee6e2940a883628522b42"
+dependencies = [
+ "cfg-if",
+ "glob",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "relative-path",
+ "rustc_version",
+ "syn 2.0.72",
+ "unicode-ident",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags 2.6.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+
+[[package]]
+name = "serde"
+version = "1.0.209"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.209"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "socketcan"
+version = "3.4.0-pre.0"
+source = "git+https://github.com/socketcan-rs/socketcan-rs.git?rev=f004ee91e142a37fea36c5d719a57852c7076e87#f004ee91e142a37fea36c5d719a57852c7076e87"
+dependencies = [
+ "bitflags 1.3.2",
+ "byte_conv",
+ "embedded-can",
+ "hex",
+ "itertools",
+ "libc",
+ "log",
+ "nb",
+ "neli",
+ "nix 0.26.4",
+ "socket2",
+ "thiserror",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+
+[[package]]
+name = "toml_edit"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "v4l2r"
+version = "0.0.1"
+source = "git+https://github.com/Gnurou/v4l2r?rev=110fd77#110fd773d15c787cbc6c2ded1bf8459c8df89f72"
+dependencies = [
+ "anyhow",
+ "bitflags 2.6.0",
+ "enumn",
+ "log",
+ "nix 0.27.1",
+ "thiserror",
+]
+
+[[package]]
+name = "vhost"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6be08d1166d41a78861ad50212ab3f9eca0729c349ac3a7a8f557c62406b87cc"
+dependencies = [
+ "bitflags 2.6.0",
+ "libc",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vhost-device-can"
+version = "0.1.0"
+dependencies = [
+ "assert_matches",
+ "clap",
+ "env_logger",
+ "log",
+ "queues",
+ "socketcan",
+ "thiserror",
+ "vhost",
+ "vhost-user-backend",
+ "virtio-bindings",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vhost-device-video"
+version = "0.1.0"
+dependencies = [
+ "assert_matches",
+ "bitflags 2.6.0",
+ "clap",
+ "env_logger",
+ "epoll",
+ "futures-executor",
+ "libc",
+ "log",
+ "num_enum",
+ "rstest",
+ "tempfile",
+ "thiserror",
+ "v4l2r",
+ "vhost",
+ "vhost-user-backend",
+ "virtio-bindings",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vhost-user-backend"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f0ffb1dd8e00a708a0e2c32d5efec5812953819888591fff9ff68236b8a5096"
+dependencies = [
+ "libc",
+ "log",
+ "vhost",
+ "virtio-bindings",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "virtio-bindings"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "878bcb1b2812a10c30d53b0ed054999de3d98f25ece91fc173973f9c57aaae86"
+
+[[package]]
+name = "virtio-queue"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07d8406e7250c934462de585d8f2d2781c31819bca1fbb7c5e964ca6bbaabfe8"
+dependencies = [
+ "log",
+ "virtio-bindings",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vm-memory"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3aba5064cc5f6f7740cddc8dae34d2d9a311cac69b60d942af7f3ab8fc49f4"
+dependencies = [
+ "arc-swap",
+ "bitflags 2.6.0",
+ "libc",
+ "thiserror",
+ "vmm-sys-util",
+ "winapi",
+]
+
+[[package]]
+name = "vmm-sys-util"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1435039746e20da4f8d507a72ee1b916f7b4b05af7a91c093d2c6561934ede"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winnow"
+version = "0.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
+dependencies = [
+ "memchr",
+]
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.toml b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.toml
new file mode 100755
index 00000000..5890f7e1
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.toml
@@ -0,0 +1,41 @@
+[package]
+name = "vhost-device-can"
+version = "0.1.0"
+authors = ["Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>"]
+description = "vhost can backend device"
+repository = "https://github.com/rust-vmm/vhost-device"
+readme = "README.md"
+keywords = ["can", "vhost", "virt", "backend"]
+license = "Apache-2.0 OR BSD-3-Clause"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[features]
+xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"]
+
+[dependencies]
+clap = { version = "4.5", features = ["derive"] }
+env_logger = "0.11"
+log = "0.4"
+thiserror = "1.0"
+queues = "1.0.2"
+# TODO: Update socketcan to version "v3.4.0" when this is released.
+# Socketcan "v3.3.0" includes the following issue: https://github.com/socketcan-rs/socketcan-rs/pull/61.
+# The version was set to the commit "f004ee91e142a" where that issue has been resolved. As soon as, a
+# newer version is released we need to point socketcan dependency to it.
+# NOTE: If you are using rust version "1.80", the compiler might complain about "std::mem::size_of".
+# The solution to that problem is described in the following link:
+# - https://github.com/socketcan-rs/socketcan-rs/pull/72
+socketcan = { path = "../git/" }
+vhost = { version = "0.11", features = ["vhost-user-backend"] }
+vhost-user-backend = { version = "0.15" }
+virtio-bindings = "0.2.2"
+virtio-queue = "0.12"
+vm-memory = "0.14.1"
+vmm-sys-util = "0.12"
+
+[dev-dependencies]
+assert_matches = "1.5"
+virtio-queue = { version = "0.12", features = ["test-utils"] }
+vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic"] }
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-APACHE b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-APACHE
new file mode 100755
index 00000000..d6456956
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-APACHE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause
new file mode 100755
index 00000000..dd975d98
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause
@@ -0,0 +1,26 @@
+Copyright 2022 The rust-vmm authors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/README.md b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/README.md
new file mode 100755
index 00000000..ade79128
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/README.md
@@ -0,0 +1,147 @@
+# vhost-device-can - CAN emulation backend daemon
+
+## Description
+This program is a vhost-user backend that emulates a VirtIO CAN device.
+The device's binary takes two (2) parameters: a socket, a 'can-devices' list.
+The socket is commonly used across all vhost-devices to communicate with
+the vhost-user frontend device. The 'can-devices' represents a list of
+CAN/FD devices appears in the host system which vhost-device-can will
+forward messages to and from the frontend side.
+
+This program is tested with QEMU's `vhost-user-device-pci` device.
+Examples' section below.
+
+## Synopsis
+```
+**vhost-device-can** [*OPTIONS*]
+````
+
+## Options
+
+.. program:: vhost-device-can
+
+.. option:: -h, --help
+
+ Print help.
+
+.. option:: -s, --socket-path=PATH
+
+ Location of vhost-user Unix domain sockets, this path will be suffixed with
+ 0,1,2..socket_count-1.
+
+.. option:: -c, --socket-count=INT
+
+ Number of guests (sockets) to attach to, default set to 1.
+
+.. option:: -d, --can-devices='CAN/FD interfaces'
+
+ CAN/FD device list at the host OS in the format:
+ <can-_X_0> [<can_in_X_1>] ... [<can_in_X_N-1>]
+
+ Note 1: Where N (the number of CAN/FD interfaces) is equal with the number
+ provided via *socket_count* parameter.
+
+ Example: --can-devices "can0 can1 can2"
+
+## Features
+This device is still work-in-progress (WIP) and on [virtio-spec v1.4](https://github.com/oasis-tcs/virtio-spec/blob/virtio-1.4/device-types/can/) is based
+on virtio-can Linux's driver and QEMU's device presented in the following RFC:
+- https://lwn.net/Articles/934187/
+
+Vhost-device-can have be been tested in scenarios with multiple QEMU's VMs using
+host's *CAN/FD* devices.
+
+## Limitations
+
+1) The transmission of a CAN/FD frame to a host interface always is done
+ synchronously. This means that regardless the negotiation or not of the
+ feature *VIRTIO_CAN_F_LATE_TX_ACK*, the backend will always wait for the
+ transmission of the frame and after will mark the transmission request
+ as used.
+2) Does not check for undefined flags in CAN/FD frame when send and receive
+ a CAN/FD frame from the frontend (QEMU device).
+3) The host's CAN/FD devices should be already in *UP* state before staring
+ the vhost-device-can (by using `ip link set can0 [up,down]`).
+ - The control messages does not actually change host's device state
+4) Current version of the device has been tested only with *vcan* device.
+
+## Examples
+
+### Dependencies
+For testing the device the required dependencies are:
+- Linux:
+ - Integrate *virtio-can* driver implemented by OpenSynergy:
+ - https://lwn.net/Articles/934187/
+ - Set `CONFIG_VIRTIO_CAN=y`
+- QEMU
+ - Integrate *virtio-can* device implemented by OpenSynergy:
+ - https://lwn.net/Articles/934187/
+ - Clone vhost-user-can QEMU device (optional):
+ - A new vhost-user-can device has been implemented in the following repo:
+ - https://github.com/virtualopensystems/qemu/tree/vhu-can-rfc
+
+### Test the device
+
+The daemon should be started first:
+```shell
+host# vhost-device-can --socket-path=can.sock --can-devices="vcan0"
+```
+
+The QEMU invocation needs to create a chardev socket the device can
+use to communicate as well as share the guests memory over a memfd.
+
+There are two option for running QEMU with vhost-device-can:
+1) Using `vhost-user-device-pci` available upstream since QEMU `v8.2.0`:
+```text
+host# qemu-system \
+ -m 4096 \
+ -numa node,memdev=mem \
+ -object memory-backend-memfd,id=mem,size=4G,share=on \
+ -chardev socket,id=can0,path=/tmp/can.sock \
+ -device vhost-user-device-pci,chardev=can0,virtio-id=36,num_vqs=3,config_size=16 \
+ ...
+```
+2) Using `vhost-user-can-pci`:
+```text
+host# qemu-system \
+ -m 4096 \
+ -numa node,memdev=mem \
+ -object memory-backend-memfd,id=mem,size=4G,share=on \
+ -chardev socket,path=/tmp/can.sock,id=can0 \
+ -device vhost-user-can-pci,chardev=can0,id=can \
+ ...
+```
+
+> Note: For testing this scenario the reader needs to clone the QEMU version
+> from the following repo which implements `vhost-user-can` device:
+> - https://github.com/virtualopensystems/qemu/tree/vhu-can-rfc
+
+### Multi-Guest case
+
+Run vhost-device-can as:
+```text
+./vhost-device-can --socket-path /tmp/can.sock --socket-count 2 --can-devices "vcan0 vcan1"
+```
+This command will start the device and create two new sockets: */tmp/can.sock0* and */tmp/can.sock1*.
+
+From the other side we run two QEMU instances (VMs) with vhost-user-can:
+```text
+host# qemu-system \
+ -m 4096 \
+ -numa node,memdev=mem \
+ -object memory-backend-memfd,id=mem,size=4G,share=on \
+ -chardev socket,path=<SOCKET_PATH>,id=can0 \
+ -device vhost-user-can-pci,chardev=can0,id=can \
+ ...
+```
+In the first instance of QEMU *SOCKET_PATH* would be: */tmp/can.sock0*,
+and will use *can0* (host interface) as sender and receiver. The second
+QEMU VM would have: *SOCKET_PATH* = */tmp/can.sock1*, and will use *can1*
+as receiver and *can2* as sender.
+
+## License
+
+This project is licensed under either of
+
+- [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0
+- [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause)
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs
new file mode 100755
index 00000000..ce8d8511
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs
@@ -0,0 +1,268 @@
+// VIRTIO CAN Emulation via vhost-user
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use log::{error, info, warn};
+use std::any::Any;
+use std::collections::HashMap;
+use std::path::PathBuf;
+use std::sync::{Arc, RwLock};
+use std::thread;
+
+use thiserror::Error as ThisError;
+use vhost_user_backend::VhostUserDaemon;
+use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
+
+use crate::can::CanController;
+use crate::vhu_can::VhostUserCanBackend;
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug, ThisError)]
+/// Errors related to low level CAN helpers
+pub(crate) enum Error {
+ #[error("Invalid socket count: {0}")]
+ SocketCountInvalid(usize),
+ #[error("Could not find can devices")]
+ CouldNotFindCANDevs,
+ #[error("Could not create can controller: {0}")]
+ CouldNotCreateCanController(crate::can::Error),
+ #[error("Could not create can controller output socket: {0}")]
+ FailCreateCanControllerSocket(crate::can::Error),
+ #[error("Could not create can backend: {0}")]
+ CouldNotCreateBackend(crate::vhu_can::Error),
+ #[error("Could not create daemon: {0}")]
+ CouldNotCreateDaemon(vhost_user_backend::Error),
+ #[error("Fatal error: {0}")]
+ ServeFailed(vhost_user_backend::Error),
+ #[error("Thread `{0}` panicked")]
+ ThreadPanic(String, Box<dyn Any + Send>),
+}
+
+#[derive(PartialEq, Debug)]
+pub struct VuCanConfig {
+ pub socket_path: PathBuf,
+ pub socket_count: u32,
+ pub can_devices: Vec<String>,
+}
+
+impl VuCanConfig {
+ pub fn generate_socket_paths(&self) -> Vec<PathBuf> {
+ let socket_file_name = self
+ .socket_path
+ .file_name()
+ .expect("socket_path has no filename.");
+ let socket_file_parent = self
+ .socket_path
+ .parent()
+ .expect("socket_path has no parent directory.");
+
+ let make_socket_path = |i: u32| -> PathBuf {
+ let mut file_name = socket_file_name.to_os_string();
+ file_name.push(std::ffi::OsStr::new(&i.to_string()));
+ socket_file_parent.join(&file_name)
+ };
+
+ (0..self.socket_count).map(make_socket_path).collect()
+ }
+}
+
+/// This is the public API through which an external program starts the
+/// vhost-device-can backend server.
+pub(crate) fn start_backend_server(socket: PathBuf, can_devs: String) -> Result<()> {
+ loop {
+ let controller =
+ CanController::new(can_devs.clone()).map_err(Error::CouldNotCreateCanController)?;
+ let lockable_controller = Arc::new(RwLock::new(controller));
+ let vu_can_backend = Arc::new(RwLock::new(
+ VhostUserCanBackend::new(lockable_controller.clone())
+ .map_err(Error::CouldNotCreateBackend)?,
+ ));
+ lockable_controller
+ .write()
+ .unwrap()
+ .open_can_socket()
+ .map_err(Error::FailCreateCanControllerSocket)?;
+
+ let read_handle = CanController::start_read_thread(lockable_controller.clone());
+
+ let mut daemon = VhostUserDaemon::new(
+ String::from("vhost-device-can-backend"),
+ vu_can_backend.clone(),
+ GuestMemoryAtomic::new(GuestMemoryMmap::new()),
+ )
+ .map_err(Error::CouldNotCreateDaemon)?;
+
+ // Start the read thread -- need to handle it after termination
+ let vring_workers = daemon.get_epoll_handlers();
+ vu_can_backend
+ .read()
+ .unwrap()
+ .set_vring_worker(&vring_workers[0]);
+
+ daemon.serve(&socket).map_err(Error::ServeFailed)?;
+
+ // Terminate the thread which reads CAN messages from "can_devs"
+ lockable_controller.write().unwrap().exit_read_thread();
+
+ // Wait for read thread to exit
+ match read_handle.join() {
+ Ok(_) => info!("The read thread returned successfully"),
+ Err(e) => warn!("The read thread returned the error: {:?}", e),
+ }
+ }
+}
+
+pub fn start_backend(config: VuCanConfig) -> Result<()> {
+ let mut handles = HashMap::new();
+ let (senders, receiver) = std::sync::mpsc::channel();
+
+ for (thread_id, (socket, can_devs)) in config
+ .generate_socket_paths()
+ .into_iter()
+ .zip(config.can_devices.iter().cloned())
+ .map(|(a, b)| (a, b.to_string()))
+ .enumerate()
+ {
+ println!(
+ "thread_id: {}, socket: {:?}, can_devs: {:?}",
+ thread_id, socket, can_devs,
+ );
+
+ let name = format!("vhu-can-{}", can_devs);
+ let sender = senders.clone();
+ let handle = thread::Builder::new()
+ .name(name.clone())
+ .spawn(move || {
+ let result =
+ std::panic::catch_unwind(move || start_backend_server(socket, can_devs));
+
+ // Notify the main thread that we are done.
+ sender.send(thread_id).unwrap();
+
+ result.map_err(|e| Error::ThreadPanic(name, e))?
+ })
+ .unwrap();
+ handles.insert(thread_id, handle);
+ }
+
+ while !handles.is_empty() {
+ let thread_id = receiver.recv().unwrap();
+ handles
+ .remove(&thread_id)
+ .unwrap()
+ .join()
+ .map_err(std::panic::resume_unwind)
+ .unwrap()?;
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::backend::Error::FailCreateCanControllerSocket;
+ use crate::can::Error::SocketOpen;
+ use crate::CanArgs;
+ use assert_matches::assert_matches;
+
+ #[test]
+ fn test_can_valid_configuration() {
+ let valid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0".to_string(),
+ socket_count: 1,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(valid_args),
+ Err(Error::CouldNotFindCANDevs)
+ );
+ }
+
+ #[test]
+ fn test_can_valid_mult_device_configuration() {
+ let valid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0 can1".to_string(),
+ socket_count: 2,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(valid_args),
+ Err(Error::CouldNotFindCANDevs)
+ );
+ }
+
+ #[test]
+ fn test_can_invalid_socket_configuration() {
+ let invalid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0".to_string(),
+ socket_count: 0,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(invalid_args),
+ Err(Error::SocketCountInvalid(0))
+ );
+ }
+
+ #[test]
+ fn test_can_invalid_mult_socket_configuration_1() {
+ let invalid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0".to_string(),
+ socket_count: 2,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(invalid_args),
+ Err(Error::SocketCountInvalid(2))
+ );
+ }
+
+ #[test]
+ fn test_can_invalid_mult_socket_configuration_2() {
+ let invalid_args = CanArgs {
+ socket_path: "/tmp/vhost.sock".to_string().into(),
+ can_devices: "can0 can1".to_string(),
+ socket_count: 1,
+ };
+
+ assert_matches!(
+ VuCanConfig::try_from(invalid_args),
+ Err(Error::SocketCountInvalid(1))
+ );
+ }
+
+ #[test]
+ fn test_can_valid_configuration_start_backend_fail() {
+ // Instantiate the struct with the provided values
+ let config = VuCanConfig {
+ socket_path: PathBuf::from("/tmp/vhost.sock"),
+ socket_count: 1,
+ can_devices: vec!["can0".to_string()],
+ };
+
+ assert_matches!(
+ start_backend(config),
+ Err(FailCreateCanControllerSocket(SocketOpen))
+ );
+ }
+
+ #[test]
+ fn test_can_valid_configuration_start_backend_server_fail() {
+ let socket_path = PathBuf::from("/tmp/vhost.sock");
+ let can_devs = "can0".to_string();
+
+ assert_matches!(
+ start_backend_server(socket_path, can_devs),
+ Err(FailCreateCanControllerSocket(SocketOpen))
+ );
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs
new file mode 100755
index 00000000..6fc03550
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs
@@ -0,0 +1,393 @@
+// CAN backend device
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use log::{error, info, trace, warn};
+use std::sync::{Arc, RwLock};
+
+use std::thread::{spawn, JoinHandle};
+use thiserror::Error as ThisError;
+use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
+extern crate queues;
+use queues::*;
+extern crate socketcan;
+use crate::virtio_can::{
+ VirtioCanConfig, VirtioCanFrame, CAN_CS_STARTED, CAN_CS_STOPPED, CAN_EFF_FLAG,
+ CAN_FRMF_TYPE_FD, VIRTIO_CAN_RX,
+};
+use socketcan::{
+ CanAnyFrame, CanDataFrame, CanFdFrame, CanFdSocket, EmbeddedFrame, ExtendedId, Frame, Id,
+ Socket, StandardId,
+};
+
+type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
+/// Errors related to low level can helpers
+pub(crate) enum Error {
+ #[error("Can open socket operation failed")]
+ SocketOpen,
+ #[error("Can write socket operation failed")]
+ SocketWrite,
+ #[error("Can read socket operation failed")]
+ SocketRead,
+ #[error("Pop can element operation failed")]
+ PopFailed,
+ #[error("Queue is empty")]
+ QueueEmpty,
+ #[error("Creating Eventfd for CAN events failed")]
+ EventFdFailed,
+ #[error("Push can element operation failed")]
+ PushFailed,
+ #[error("No output interface available")]
+ NoOutputInterface,
+}
+
+#[derive(Debug)]
+pub(crate) struct CanController {
+ pub config: VirtioCanConfig,
+ pub can_name: String,
+ pub can_socket: Option<CanFdSocket>,
+ pub rx_event_fd: EventFd,
+ rx_fifo: Queue<VirtioCanFrame>,
+ pub status: bool,
+ pub ctrl_state: u8,
+}
+
+impl CanController {
+ // Creates a new controller corresponding to `device`.
+ pub(crate) fn new(can_name: String) -> Result<CanController> {
+ let can_name = can_name.to_owned();
+ info!("can_name: {:?}", can_name);
+
+ let rx_fifo = Queue::new();
+ let rx_efd = EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?;
+
+ Ok(CanController {
+ config: VirtioCanConfig { status: 0x0.into() },
+ can_name,
+ can_socket: None,
+ rx_event_fd: rx_efd,
+ rx_fifo,
+ status: true,
+ ctrl_state: CAN_CS_STOPPED,
+ })
+ }
+
+ pub fn print_can_frame(canframe: VirtioCanFrame) {
+ trace!("canframe.msg_type 0x{:x}", canframe.msg_type.to_native());
+ trace!("canframe.can_id 0x{:x}", canframe.can_id.to_native());
+ trace!("canframe.length {}", canframe.length.to_native());
+ trace!("canframe.flags 0x{:x}", canframe.flags.to_native());
+ if canframe.length.to_native() == 0 {
+ trace!("[]");
+ return;
+ }
+ trace!("[");
+ let last_elem = canframe.length.to_native() as usize - 1;
+ for (index, sdu) in canframe.sdu.iter().enumerate() {
+ if index == last_elem {
+ trace!("0x{:x}", sdu);
+ break;
+ }
+ trace!("0x{:x}, ", sdu);
+ }
+ trace!("]");
+ }
+
+ pub fn start_read_thread(controller: Arc<RwLock<CanController>>) -> JoinHandle<Result<()>> {
+ spawn(move || CanController::read_can_socket(controller))
+ }
+
+ pub fn push(&mut self, rx_elem: VirtioCanFrame) -> Result<()> {
+ match self.rx_fifo.add(rx_elem) {
+ Ok(_) => Ok(()),
+ _ => Err(Error::PushFailed),
+ }
+ }
+
+ pub fn rx_is_empty(&mut self) -> bool {
+ self.rx_fifo.size() == 0
+ }
+
+ pub fn pop(&mut self) -> Result<VirtioCanFrame> {
+ if self.rx_fifo.size() < 1 {
+ return Err(Error::QueueEmpty);
+ }
+
+ match self.rx_fifo.remove() {
+ Ok(item) => Ok(item),
+ _ => Err(Error::PopFailed),
+ }
+ }
+
+ pub fn open_can_socket(&mut self) -> Result<()> {
+ self.can_socket = match CanFdSocket::open(&self.can_name) {
+ Ok(socket) => Some(socket),
+ Err(_) => {
+ warn!("Error opening CAN socket");
+ return Err(Error::SocketOpen);
+ }
+ };
+ Ok(())
+ }
+
+ // Helper function to process frame
+ fn process_frame<F: Frame>(frame: F, is_fd: bool) -> VirtioCanFrame {
+ VirtioCanFrame {
+ msg_type: VIRTIO_CAN_RX.into(),
+ can_id: frame.id_word().into(),
+ length: (frame.data().len() as u16).into(),
+ reserved: 0.into(),
+ flags: if is_fd {
+ CAN_FRMF_TYPE_FD.into()
+ } else {
+ 0.into()
+ },
+ sdu: {
+ let mut sdu_data: [u8; 64] = [0; 64];
+ sdu_data[..frame.data().len()].copy_from_slice(frame.data());
+ sdu_data
+ },
+ }
+ }
+
+ pub fn read_can_socket(controller: Arc<RwLock<CanController>>) -> Result<()> {
+ let can_name = &controller.read().unwrap().can_name.clone();
+ dbg!("Start reading from {} socket!", &can_name);
+ let socket = match CanFdSocket::open(can_name) {
+ Ok(socket) => socket,
+ Err(_) => {
+ warn!("Error opening CAN socket");
+ return Err(Error::SocketOpen);
+ }
+ };
+
+ // Set non-blocking otherwise the device will not restart immediatelly
+ // when the VM closes, and a new canfd message needs to be received for
+ // restart to happen.
+ // This caused by the fact that the thread is stacked in read function
+ // and does not go to the next loop to check the status condition.
+ socket
+ .set_nonblocking(true)
+ .expect("Cannot set nonblocking");
+
+ // Receive CAN messages
+ loop {
+ // If the status variable is false then break and exit.
+ if !controller.read().unwrap().status {
+ dbg!("exit read can thread");
+ return Ok(());
+ }
+
+ if let Ok(frame) = socket.read_frame() {
+ // If ctrl_state is stopped, consume the received CAN/FD frame
+ // and loop till the ctrl_state changes to started or the thread
+ // to exit.
+ if controller.read().unwrap().ctrl_state != CAN_CS_STARTED {
+ trace!("CAN/FD frame is received but not saved!");
+ continue;
+ }
+
+ // Match and process frame variants
+ let read_can_frame = match frame {
+ CanAnyFrame::Normal(frame) => {
+ trace!("Received CAN frame: {:?}", frame);
+ Self::process_frame(frame, false)
+ }
+ CanAnyFrame::Fd(frame) => {
+ trace!("Received CAN FD frame: {:?}", frame);
+ Self::process_frame(frame, true)
+ }
+ CanAnyFrame::Remote(frame) => {
+ trace!("Received Remote CAN frame: {:?}", frame);
+ Self::process_frame(frame, false)
+ }
+ CanAnyFrame::Error(frame) => {
+ trace!("Received Error frame: {:?}", frame);
+ Self::process_frame(frame, false)
+ }
+ };
+
+ match controller.write().unwrap().push(read_can_frame) {
+ Ok(_) => warn!("New Can frame was received"),
+ Err(_) => {
+ warn!("Error read/push CAN frame");
+ return Err(Error::SocketRead);
+ }
+ };
+
+ controller
+ .write()
+ .unwrap()
+ .rx_event_fd
+ .write(1)
+ .expect("Fail to write on rx_event_fd");
+ }
+ }
+ }
+
+ pub(crate) fn exit_read_thread(&mut self) {
+ trace!("Exit can read thread\n");
+ self.status = false;
+ }
+
+ pub(crate) fn config(&mut self) -> &VirtioCanConfig {
+ &self.config
+ }
+
+ pub(crate) fn can_out(&self, tx_request: VirtioCanFrame) -> Result<()> {
+ // Create a CAN frame with a specific CAN-ID and the data buffer
+ let can_id: Id = if (tx_request.can_id.to_native() & CAN_EFF_FLAG) != 0 {
+ // SAFETY: Use new_unchecked cause checks have been taken place
+ // to prior stage. Also flags have beem already added on can_id
+ // so tnew will fail (can_id + can_flags) > 29 bits
+ unsafe { Id::Extended(ExtendedId::new_unchecked(tx_request.can_id.into())) }
+ } else {
+ // SAFETY: Use new_unchecked cause checks have been taken place
+ // to prior stage. Also flags have beem already added on can_id
+ // so tnew will fail (can_id + can_flags) > 11 bits
+ unsafe {
+ Id::Standard(StandardId::new_unchecked(
+ tx_request.can_id.to_native() as u16
+ ))
+ }
+ };
+
+ // Grab the data to be tranfered
+ let data_len = tx_request.length.to_native() as usize;
+ let data: Vec<u8> = tx_request.sdu.iter().cloned().take(data_len).collect();
+
+ // Format CAN/FD frame
+ let frame: CanAnyFrame = if (tx_request.flags.to_native() & CAN_FRMF_TYPE_FD) != 0 {
+ CanAnyFrame::Fd(CanFdFrame::new(can_id, &data).expect("Fail to create CanFdFrame"))
+ } else {
+ CanAnyFrame::Normal(CanDataFrame::new(can_id, &data).expect("Fail to create CanFrame"))
+ };
+
+ // Send the CAN/FD frame
+ let socket = self.can_socket.as_ref().ok_or("No available device");
+
+ match socket {
+ Ok(socket) => match socket.write_frame(&frame) {
+ Ok(_) => Ok(()),
+ Err(_) => {
+ warn!("Error write CAN socket");
+ Err(Error::SocketWrite)
+ }
+ },
+ Err(_) => Err(Error::NoOutputInterface),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::vhu_can::VhostUserCanBackend;
+ use std::sync::{Arc, RwLock};
+
+ #[test]
+ fn test_can_controller_creation() {
+ let can_name = "can".to_string();
+
+ let controller = CanController::new(can_name.clone()).unwrap();
+ assert_eq!(controller.can_name, can_name);
+ }
+
+ #[test]
+ fn test_can_controller_push_and_pop() {
+ let can_name = "can".to_string();
+ let mut controller = CanController::new(can_name.clone()).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_RX.into(),
+ can_id: 123.into(),
+ length: 64.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ // Test push
+ controller.push(frame).unwrap();
+
+ // Test pop
+ let pop_result = controller.pop().unwrap();
+ assert_eq!(pop_result, frame);
+ }
+
+ #[test]
+ fn test_can_controller_config() {
+ let can_name = "can".to_string();
+ let mut controller = CanController::new(can_name.clone()).unwrap();
+
+ // Test config
+ let config = controller.config();
+ assert_eq!(config.status.to_native(), 0);
+ }
+
+ #[test]
+ fn test_can_controller_operation() {
+ let can_name = "can".to_string();
+ let mut controller = CanController::new(can_name.clone()).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_RX.into(),
+ can_id: 123.into(),
+ length: 64.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ match controller.open_can_socket() {
+ Ok(_) => {
+ // Test operation
+ let operation_result = controller.can_out(frame);
+ assert!(operation_result.is_ok());
+ }
+ Err(_) => warn!("There is no CAN interface with {} name", can_name),
+ }
+ }
+
+ #[test]
+ fn test_can_controller_start_read_thread() {
+ let can_name = "can".to_string();
+ let controller = CanController::new(can_name.clone()).unwrap();
+ let arc_controller = Arc::new(RwLock::new(controller));
+
+ // Test start_read_thread
+ let thread_handle = CanController::start_read_thread(arc_controller.clone());
+ assert!(thread_handle.join().is_ok());
+ }
+
+ #[test]
+ fn test_can_open_socket_fail() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ assert_eq!(
+ controller.write().unwrap().open_can_socket(),
+ Err(Error::SocketOpen)
+ );
+ }
+
+ #[test]
+ fn test_can_read_socket_fail() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ assert_eq!(
+ CanController::read_can_socket(controller),
+ Err(Error::SocketOpen)
+ );
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs
new file mode 100755
index 00000000..97386884
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs
@@ -0,0 +1,96 @@
+// VIRTIO CAN Emulation via vhost-user
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+mod backend;
+mod can;
+mod vhu_can;
+mod virtio_can;
+
+use clap::Parser;
+use log::{error, info};
+use socketcan::{CanSocket, Socket};
+use std::convert::TryFrom;
+use std::path::PathBuf;
+use std::process::exit;
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+use crate::backend::{start_backend, Error, VuCanConfig};
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct CanArgs {
+ /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
+ #[clap(short, long, value_name = "SOCKET")]
+ socket_path: PathBuf,
+
+ /// A can device name to be used for reading (ex. vcan, can0, can1, ... etc.)
+ #[clap(short = 'd', long)]
+ can_devices: String,
+
+ /// Number of guests (sockets) to connect to.
+ #[clap(short = 'c', long, default_value_t = 1)]
+ socket_count: u32,
+}
+
+fn check_can_devices(can_devices: &[String]) -> Result<()> {
+ for can_dev in can_devices {
+ if CanSocket::open(can_dev).is_err() {
+ info!("There is no interface with the following name {}", can_dev);
+ return Err(Error::CouldNotFindCANDevs);
+ }
+ }
+ Ok(())
+}
+
+fn parse_can_devices(input: &CanArgs) -> Result<Vec<String>> {
+ let can_devices_vec: Vec<&str> = input.can_devices.split_whitespace().collect();
+ let can_devices: Vec<_> = can_devices_vec.iter().map(|x| x.to_string()).collect();
+
+ if (can_devices.len() as u32) != input.socket_count {
+ info!(
+ "Number of CAN/FD devices ({}) not equal with socket count {}",
+ input.can_devices, input.socket_count
+ );
+ return Err(Error::SocketCountInvalid(
+ input.socket_count.try_into().unwrap(),
+ ));
+ }
+
+ match check_can_devices(&can_devices) {
+ Ok(_) => Ok(can_devices),
+ Err(_) => Err(Error::CouldNotFindCANDevs),
+ }
+}
+
+impl TryFrom<CanArgs> for VuCanConfig {
+ type Error = Error;
+
+ fn try_from(args: CanArgs) -> Result<Self> {
+ if args.socket_count == 0 {
+ return Err(Self::Error::SocketCountInvalid(0));
+ }
+
+ let can_devices = match parse_can_devices(&args) {
+ Ok(can_devs) => can_devs,
+ Err(e) => return Err(e),
+ };
+
+ Ok(VuCanConfig {
+ socket_path: args.socket_path,
+ socket_count: args.socket_count,
+ can_devices,
+ })
+ }
+}
+
+fn main() {
+ env_logger::init();
+ if let Err(e) = VuCanConfig::try_from(CanArgs::parse()).and_then(start_backend) {
+ error!("{e}");
+ exit(1);
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs
new file mode 100755
index 00000000..b53f57d1
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs
@@ -0,0 +1,2145 @@
+// vhost device can
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use crate::can::CanController;
+use crate::can::Error::QueueEmpty;
+use crate::virtio_can::{
+ VirtioCanCtrlRequest, VirtioCanFrame, VirtioCanHeader, CANFD_VALID_LENGTHS, CAN_CS_STARTED,
+ CAN_CS_STOPPED, CAN_EFF_FLAG, CAN_EFF_MASK, CAN_ERR_BUSOFF, CAN_ERR_FLAG, CAN_FRMF_TYPE_FD,
+ CAN_RTR_FLAG, CAN_SFF_MASK, VIRTIO_CAN_FLAGS_EXTENDED, VIRTIO_CAN_FLAGS_FD,
+ VIRTIO_CAN_FLAGS_RTR, VIRTIO_CAN_FLAGS_VALID_MASK, VIRTIO_CAN_F_CAN_CLASSIC,
+ VIRTIO_CAN_F_CAN_FD, VIRTIO_CAN_F_RTR_FRAMES, VIRTIO_CAN_RESULT_NOT_OK, VIRTIO_CAN_RESULT_OK,
+ VIRTIO_CAN_RX, VIRTIO_CAN_SET_CTRL_MODE_START, VIRTIO_CAN_SET_CTRL_MODE_STOP,
+ VIRTIO_CAN_S_CTRL_BUSOFF, VIRTIO_CAN_TX,
+};
+use log::{error, trace, warn};
+use std::os::fd::AsRawFd;
+use std::slice::from_raw_parts;
+use std::sync::{Arc, RwLock};
+use std::{
+ convert,
+ io::{self, Result as IoResult},
+};
+use thiserror::Error as ThisError;
+use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
+use vhost_user_backend::VringEpollHandler;
+use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT};
+use virtio_bindings::bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1};
+use virtio_bindings::bindings::virtio_ring::{
+ VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC,
+};
+use virtio_queue::{DescriptorChain, QueueOwnedT};
+use vm_memory::{
+ ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap,
+};
+use vmm_sys_util::epoll::EventSet;
+use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
+
+/// Virtio configuration
+const QUEUE_SIZE: usize = 64;
+const NUM_QUEUES: usize = 3;
+
+/// Queues
+const TX_QUEUE: u16 = 0;
+const RX_QUEUE: u16 = 1;
+const CTRL_QUEUE: u16 = 2;
+const BACKEND_EFD: u16 = (NUM_QUEUES + 1) as u16;
+
+type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
+/// Errors related to vhost-device-can-daemon.
+pub(crate) enum Error {
+ #[error("Failed to handle event, didn't match EPOLLIN")]
+ HandleEventNotEpollIn,
+ #[error("Failed to handle unknown event")]
+ HandleEventUnknown,
+ #[error("Descriptor not found")]
+ DescriptorNotFound,
+ #[error("Descriptor read failed")]
+ DescriptorReadFailed,
+ #[error("Descriptor write failed")]
+ DescriptorWriteFailed,
+ #[error("Failed to send notification")]
+ NotificationFailed,
+ #[error("Failed to create new EventFd")]
+ EventFdFailed,
+ #[error("Unknown can message type: {0}")]
+ UnexpectedCanMsgType(u16),
+ #[error("RTR frames not negotiated")]
+ UnexpectedRtrFlag,
+ #[error("Can FD frames not negotiated")]
+ UnexpectedFdFlag,
+ #[error("Unexpected CAN ID bigger than: {0} bits")]
+ UnexpectedCanId(u16),
+ #[error("Invalid CAN flags: {0}")]
+ InvalidCanFlags(u32),
+ #[error("Invalid CAN[FD] length: {0}")]
+ InvalidCanLength(u32),
+ #[error("Classic CAN frames not negotiated")]
+ UnexpectedClassicFlag,
+ #[error("Bus off error received")]
+ BusoffRxFrame,
+ #[error("Rx CAN frame has unknown error")]
+ RxFrameUnknownFail,
+}
+
+impl convert::From<Error> for io::Error {
+ fn from(e: Error) -> Self {
+ io::Error::new(io::ErrorKind::Other, e)
+ }
+}
+
+pub(crate) struct VhostUserCanBackend {
+ controller: Arc<RwLock<CanController>>,
+ acked_features: u64,
+ event_idx: bool,
+ pub(crate) exit_event: EventFd,
+ mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
+}
+
+type CanDescriptorChain = DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMmap<()>>>;
+
+impl VhostUserCanBackend {
+ pub(crate) fn new(controller: Arc<RwLock<CanController>>) -> Result<Self> {
+ Ok(VhostUserCanBackend {
+ controller,
+ event_idx: false,
+ acked_features: 0x0,
+ exit_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
+ mem: None,
+ })
+ }
+
+ fn check_features(&self, features: u16) -> bool {
+ (self.acked_features & (1 << features)) != 0
+ }
+
+ fn check_tx_frame(&self, request: VirtioCanFrame) -> Result<VirtioCanFrame> {
+ let msg_type = request.msg_type.to_native();
+ let mut can_id = request.can_id.to_native();
+ let length = request.length.to_native();
+ let mut flags = 0;
+
+ if msg_type != VIRTIO_CAN_TX {
+ warn!("TX: Message type 0x{:x} unknown\n", msg_type);
+ return Err(Error::UnexpectedCanMsgType(msg_type));
+ }
+
+ // Check for undefined bits in frame's flags
+ let invalid_can_flags: u32 = request.flags.to_native() & !VIRTIO_CAN_FLAGS_VALID_MASK;
+ if invalid_can_flags != 0 {
+ return Err(Error::InvalidCanFlags(invalid_can_flags));
+ }
+
+ // If VIRTIO_CAN_FLAGS_EXTENDED has negotiated then use extended CAN ID
+ if (request.flags.to_native() & VIRTIO_CAN_FLAGS_EXTENDED) != 0 {
+ // If we have a extended frame there is no way to check if
+ // can_id > 29 bits. The reason is that bit 29, 30, 31 are
+ // dedicated to CAN specific flags (EFF, RTR, ERR).
+ if can_id > CAN_EFF_MASK {
+ return Err(Error::UnexpectedCanId(29_u16));
+ }
+ can_id |= CAN_EFF_FLAG;
+ } else {
+ // If we have a standard frame and can_id > 11 bits then fail
+ if can_id > CAN_SFF_MASK {
+ return Err(Error::UnexpectedCanId(11_u16));
+ }
+ }
+
+ // Remote transfer request is used only with classic CAN
+ if (request.flags.to_native() & VIRTIO_CAN_FLAGS_RTR) != 0 {
+ if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC)
+ || !self.check_features(VIRTIO_CAN_F_RTR_FRAMES)
+ {
+ warn!("TX: RTR frames not negotiated");
+ return Err(Error::UnexpectedRtrFlag);
+ }
+ can_id |= CAN_RTR_FLAG;
+ }
+
+ // One of VIRTIO_CAN_F_CAN_CLASSIC and VIRTIO_CAN_F_CAN_FD must be negotiated
+ // Check if VIRTIO_CAN_F_CAN_FD is negotiated when the frame is CANFD
+ if (request.flags.to_native() & VIRTIO_CAN_FLAGS_FD) != 0 {
+ if !self.check_features(VIRTIO_CAN_F_CAN_FD) {
+ warn!("TX: FD frames not negotiated\n");
+ return Err(Error::UnexpectedFdFlag);
+ }
+ flags = CAN_FRMF_TYPE_FD;
+ } else {
+ // Check if VIRTIO_CAN_F_CAN_CLASSIC is negotiated when the frame is CAN
+ if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) {
+ warn!("TX: Classic frames not negotiated\n");
+ return Err(Error::UnexpectedClassicFlag);
+ }
+ }
+
+ // Check if CAN/FD length is out-of-range (based on negotiated features)
+ if (request.flags.to_native() & VIRTIO_CAN_FLAGS_FD) != 0 {
+ if length > 8 && !CANFD_VALID_LENGTHS.contains(&length.into()) {
+ return Err(Error::InvalidCanLength(length.into()));
+ }
+ } else if length > 8 {
+ return Err(Error::InvalidCanLength(length.into()));
+ }
+
+ Ok(VirtioCanFrame {
+ msg_type: msg_type.into(),
+ can_id: can_id.into(),
+ length: length.into(),
+ reserved: 0.into(),
+ flags: flags.into(),
+ sdu: request.sdu[0..64].try_into().unwrap(),
+ })
+ }
+
+ fn check_rx_frame(&self, response: VirtioCanFrame) -> Result<VirtioCanFrame> {
+ CanController::print_can_frame(response);
+
+ let mut can_rx = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_RX.into(),
+ can_id: response.can_id,
+ length: response.length,
+ reserved: 0.into(),
+ flags: response.flags,
+ sdu: [0; 64],
+ };
+ let res_len = response.length.to_native();
+ let res_can_id = response.can_id.to_native();
+ let mut res_flags = response.flags.to_native();
+
+ // If we receive an error message check if that's a busoff.
+ // If no just drop the message, otherwise update config and return.
+ if (res_can_id & CAN_ERR_FLAG) != 0 {
+ if (res_can_id & CAN_ERR_BUSOFF) != 0 {
+ // TODO: Trigger a config_change notification
+ self.controller.write().unwrap().config.status = VIRTIO_CAN_S_CTRL_BUSOFF.into();
+ self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED;
+ warn!("Got BusOff error frame, device does a local bus off\n");
+ return Err(Error::BusoffRxFrame);
+ } else {
+ trace!("Dropping error frame 0x{:x}\n", response.can_id.to_native());
+ return Err(Error::RxFrameUnknownFail);
+ }
+ }
+
+ // One of VIRTIO_CAN_F_CAN_CLASSIC and VIRTIO_CAN_F_CAN_FD must be negotiated
+ if (res_flags & CAN_FRMF_TYPE_FD) != 0 {
+ if !self.check_features(VIRTIO_CAN_F_CAN_FD) {
+ warn!("Drop non-supported CAN FD frame");
+ return Err(Error::UnexpectedFdFlag);
+ }
+ } else if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) {
+ warn!("Drop non-supported CAN classic frame");
+ return Err(Error::UnexpectedClassicFlag);
+ }
+
+ // Add VIRTIO_CAN_FLAGS_EXTENDED in flag if the received frame
+ // had an extended CAN ID.
+ // Based on virtio-spec v1.4, the 3 MSB of can_id should be set to 0.
+ if (res_can_id & CAN_EFF_FLAG) != 0 {
+ can_rx.flags = VIRTIO_CAN_FLAGS_EXTENDED.into();
+ can_rx.can_id = (res_can_id & CAN_EFF_MASK).into();
+ } else {
+ can_rx.flags = 0.into();
+ can_rx.can_id = (res_can_id & CAN_SFF_MASK).into();
+ }
+
+ // Remote transfer request is used only with classic CAN
+ if (res_can_id & CAN_RTR_FLAG) != 0 {
+ if !self.check_features(VIRTIO_CAN_F_RTR_FRAMES)
+ || !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC)
+ {
+ warn!("Drop non-supported RTR frame");
+ return Err(Error::UnexpectedRtrFlag);
+ }
+ // If remote transfer request is enabled add the according flag
+ can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_RTR).into();
+ }
+
+ // Treat Vcan interface as CANFD if MTU is set to 64 bytes.
+ //
+ // Vcan can not be configured as CANFD interface, but it is
+ // possible to configure its MTU to 64 bytes. So if a messages
+ // bigger than 8 bytes is being received we consider it as
+ // CANFD message.
+ let can_name = self.controller.read().unwrap().can_name.clone();
+ if self.check_features(VIRTIO_CAN_F_CAN_FD) && res_len > 8 && can_name == "vcan0" {
+ res_flags |= CAN_FRMF_TYPE_FD;
+ warn!("\n\n\nCANFD VCAN0\n\n");
+ }
+
+ // Check if CAN/FD length is out-of-range (based on negotiated features)
+ if (res_flags & CAN_FRMF_TYPE_FD) != 0 {
+ if res_len > 8 && !CANFD_VALID_LENGTHS.contains(&res_len.into()) {
+ return Err(Error::InvalidCanLength(res_len.into()));
+ }
+ can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_FD).into();
+ } else if res_len > 8 {
+ return Err(Error::InvalidCanLength(res_len.into()));
+ }
+
+ can_rx.sdu.copy_from_slice(&response.sdu[0..64]);
+ CanController::print_can_frame(can_rx);
+
+ Ok(can_rx)
+ }
+
+ fn process_ctrl_requests(
+ &self,
+ requests: Vec<CanDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
+ if requests.is_empty() {
+ return Ok(true);
+ }
+
+ for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+
+ let mut reader = desc_chain
+ .clone()
+ .reader(&atomic_mem)
+ .map_err(|_| Error::DescriptorReadFailed)?;
+
+ let mut writer = desc_chain
+ .clone()
+ .writer(&atomic_mem)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ let request = reader
+ .read_obj::<VirtioCanCtrlRequest>()
+ .map_err(|_| Error::DescriptorReadFailed)?;
+
+ // This implementation requires the CAN devices to be already in UP state
+ // before starting. This code does not trigger state changes [UP/DOWN] of
+ // the host CAN devices.
+ let response = match request.msg_type.into() {
+ VIRTIO_CAN_SET_CTRL_MODE_START => {
+ if self.controller.write().unwrap().ctrl_state == CAN_CS_STARTED {
+ VIRTIO_CAN_RESULT_NOT_OK
+ } else {
+ // TODO: Trigger a config_change notification (optional)
+ self.controller.write().unwrap().config.status = 0.into();
+ self.controller.write().unwrap().ctrl_state = CAN_CS_STARTED;
+ VIRTIO_CAN_RESULT_OK
+ }
+ }
+ VIRTIO_CAN_SET_CTRL_MODE_STOP => {
+ if self.controller.write().unwrap().ctrl_state == CAN_CS_STOPPED {
+ VIRTIO_CAN_RESULT_NOT_OK
+ } else {
+ // TODO: Trigger a config_change notification (optional)
+ self.controller.write().unwrap().config.status = 0.into();
+ self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED;
+ VIRTIO_CAN_RESULT_OK
+ }
+ }
+ _ => {
+ trace!("Ctrl queue: msg type 0x{:?} unknown", request.msg_type);
+ VIRTIO_CAN_RESULT_NOT_OK
+ }
+ };
+
+ writer
+ .write_obj(response)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ if vring
+ .add_used(desc_chain.head_index(), writer.bytes_written() as u32)
+ .is_err()
+ {
+ warn!("Couldn't return used descriptors to the ring");
+ }
+ }
+
+ Ok(true)
+ }
+
+ fn process_tx_requests(
+ &self,
+ requests: Vec<CanDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
+ if requests.is_empty() {
+ return Ok(true);
+ }
+
+ for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+
+ let mut reader = desc_chain
+ .clone()
+ .reader(&atomic_mem)
+ .map_err(|_| Error::DescriptorReadFailed)?;
+
+ let mut writer = desc_chain
+ .clone()
+ .writer(&atomic_mem)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ let request_header = reader
+ .read_obj::<VirtioCanHeader>()
+ .map_err(|_| Error::DescriptorReadFailed)?;
+
+ let data_len = request_header.length.to_native() as usize;
+ let mut request_data: Vec<u8> = Vec::new();
+ for _i in 0..data_len {
+ let data_byte = reader
+ .read_obj::<u8>()
+ .map_err(|_| Error::DescriptorReadFailed)?;
+ request_data.push(data_byte);
+ }
+
+ let request = VirtioCanFrame {
+ msg_type: request_header.msg_type,
+ can_id: request_header.can_id,
+ length: request_header.length,
+ reserved: request_header.reserved,
+ flags: request_header.flags,
+ sdu: {
+ let mut sdu_data: [u8; 64] = [0; 64];
+ sdu_data[..request_header.length.to_native() as usize]
+ .copy_from_slice(request_data.as_slice());
+ sdu_data
+ },
+ };
+ CanController::print_can_frame(request);
+
+ let response = if self.controller.read().unwrap().ctrl_state == CAN_CS_STOPPED {
+ trace!("Device is stopped!");
+ VIRTIO_CAN_RESULT_NOT_OK
+ } else {
+ let _response = match self.check_tx_frame(request) {
+ Ok(frame) => {
+ CanController::print_can_frame(frame);
+
+ // If the VIRTIO_CAN_F_CAN_LATE_TX_ACK is negotiated sent the
+ // frame and wait for it to be sent.
+ // TODO: Otherwise send it asynchronously.
+ match self.controller.write().unwrap().can_out(frame) {
+ Ok(_) => VIRTIO_CAN_RESULT_OK,
+ Err(_) => {
+ warn!("we got an error from controller send func");
+ VIRTIO_CAN_RESULT_NOT_OK
+ }
+ }
+ }
+ Err(e) => {
+ warn!("The tx frame had the following error: {}", e);
+ VIRTIO_CAN_RESULT_NOT_OK
+ }
+ };
+
+ // If the device cannot send the frame either because socket does not
+ // exist or the writing the frame fails for another unknown reason
+ // then behave as receiving a busoff error.
+ if _response == VIRTIO_CAN_RESULT_NOT_OK {
+ trace!("Change controller status to STOPPED and busoff to true");
+ // TODO: Trigger a config_change notification
+ self.controller.write().unwrap().config.status =
+ VIRTIO_CAN_S_CTRL_BUSOFF.into();
+ self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED;
+ }
+
+ _response
+ };
+
+ writer
+ .write_obj(response)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ if vring
+ .add_used(desc_chain.head_index(), writer.bytes_written() as u32)
+ .is_err()
+ {
+ warn!("Couldn't return used descriptors to the ring");
+ }
+ }
+
+ Ok(true)
+ }
+
+ pub fn process_rx_requests(
+ &mut self,
+ requests: Vec<CanDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
+ if requests.is_empty() {
+ return Ok(true);
+ }
+
+ // This flag, if true, requests a new tx buffer from the front-end
+ let mut req_rx_buf = false;
+
+ for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+ let mut writer = desc_chain
+ .clone()
+ .writer(&atomic_mem)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ let response = match self.controller.write().unwrap().pop() {
+ Ok(item) => {
+ // If one or more frames have been received then request
+ // a new rx buffer.
+ req_rx_buf = true;
+ item
+ }
+ Err(QueueEmpty) => {
+ return Ok(req_rx_buf);
+ }
+ Err(_) => {
+ return Err(Error::HandleEventUnknown);
+ }
+ };
+
+ let can_rx = match self.check_rx_frame(response) {
+ Ok(frame) => frame,
+ Err(e) => {
+ warn!("The tx frame had the following error: {}", e);
+ return Err(e);
+ }
+ };
+
+ writer
+ .write_obj(can_rx)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ if vring
+ .add_used(desc_chain.head_index(), writer.bytes_written() as u32)
+ .is_err()
+ {
+ warn!("Couldn't return used descriptors to the ring");
+ }
+ }
+
+ Ok(true)
+ }
+
+ /// Process the messages in the vring and dispatch replies
+ fn process_ctrl_queue(&mut self, vring: &VringRwLock) -> Result<()> {
+ let requests: Vec<CanDescriptorChain> = vring
+ .get_mut()
+ .get_queue_mut()
+ .iter(self.mem.as_ref().unwrap().memory())
+ .map_err(|_| Error::DescriptorNotFound)?
+ .collect();
+
+ if self.process_ctrl_requests(requests, vring)? {
+ // Send notification once all the requests are processed
+ vring
+ .signal_used_queue()
+ .map_err(|_| Error::NotificationFailed)?;
+ }
+ Ok(())
+ }
+
+ /// Process the messages in the vring and dispatch replies
+ fn process_tx_queue(&self, vring: &VringRwLock) -> Result<()> {
+ let requests: Vec<CanDescriptorChain> = vring
+ .get_mut()
+ .get_queue_mut()
+ .iter(self.mem.as_ref().unwrap().memory())
+ .map_err(|_| Error::DescriptorNotFound)?
+ .collect();
+
+ if self.process_tx_requests(requests, vring)? {
+ // Send notification once all the requests are processed
+ vring
+ .signal_used_queue()
+ .map_err(|_| Error::NotificationFailed)?;
+ }
+
+ Ok(())
+ }
+
+ /// Process the messages in the vring and dispatch replies
+ fn process_rx_queue(&mut self, vring: &VringRwLock) -> Result<()> {
+ let requests: Vec<CanDescriptorChain> = vring
+ .get_mut()
+ .get_queue_mut()
+ .iter(self.mem.as_ref().unwrap().memory())
+ .map_err(|_| Error::DescriptorNotFound)?
+ .collect();
+
+ if self.process_rx_requests(requests, vring)? {
+ // Send notification once all the requests are processed
+ vring
+ .signal_used_queue()
+ .map_err(|_| Error::NotificationFailed)?;
+ }
+ Ok(())
+ }
+
+ /// Set self's VringWorker.
+ pub(crate) fn set_vring_worker(
+ &self,
+ vring_worker: &Arc<VringEpollHandler<Arc<RwLock<VhostUserCanBackend>>>>,
+ ) {
+ let rx_event_fd = self.controller.read().unwrap().rx_event_fd.as_raw_fd();
+ vring_worker
+ .register_listener(rx_event_fd, EventSet::IN, u64::from(BACKEND_EFD))
+ .expect("Fail to register new handler");
+ }
+}
+
+/// VhostUserBackendMut trait methods
+impl VhostUserBackendMut for VhostUserCanBackend {
+ type Vring = VringRwLock;
+ type Bitmap = ();
+
+ fn num_queues(&self) -> usize {
+ NUM_QUEUES
+ }
+
+ fn max_queue_size(&self) -> usize {
+ QUEUE_SIZE
+ }
+
+ fn features(&self) -> u64 {
+ 1 << VIRTIO_F_VERSION_1
+ | 1 << VIRTIO_F_NOTIFY_ON_EMPTY
+ | 1 << VIRTIO_RING_F_EVENT_IDX
+ | 1 << VIRTIO_CAN_F_CAN_CLASSIC
+ | 1 << VIRTIO_CAN_F_CAN_FD
+ | 1 << VIRTIO_RING_F_INDIRECT_DESC
+ | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
+ }
+
+ fn acked_features(&mut self, _features: u64) {
+ self.acked_features = _features;
+ }
+
+ fn protocol_features(&self) -> VhostUserProtocolFeatures {
+ VhostUserProtocolFeatures::MQ
+ | VhostUserProtocolFeatures::CONFIG
+ | VhostUserProtocolFeatures::REPLY_ACK
+ }
+
+ fn get_config(&self, offset: u32, size: u32) -> Vec<u8> {
+ // SAFETY: The layout of the structure is fixed and can be initialized by
+ // reading its content from byte array.
+ unsafe {
+ from_raw_parts(
+ self.controller
+ .write()
+ .unwrap()
+ .config()
+ .as_slice()
+ .as_ptr()
+ .offset(offset as isize) as *const _ as *const _,
+ size as usize,
+ )
+ .to_vec()
+ }
+ }
+
+ fn set_event_idx(&mut self, enabled: bool) {
+ self.event_idx = enabled;
+ }
+
+ fn update_memory(&mut self, mem: GuestMemoryAtomic<GuestMemoryMmap>) -> IoResult<()> {
+ self.mem = Some(mem);
+ Ok(())
+ }
+
+ fn handle_event(
+ &mut self,
+ device_event: u16,
+ evset: EventSet,
+ vrings: &[VringRwLock],
+ _thread_id: usize,
+ ) -> IoResult<()> {
+ if evset != EventSet::IN {
+ return Err(Error::HandleEventNotEpollIn.into());
+ }
+
+ // If the device is in STOPPED state only TX and CTRL messages can be handled
+ if (self.controller.read().unwrap().ctrl_state == CAN_CS_STOPPED)
+ && ((device_event == RX_QUEUE) || (device_event == BACKEND_EFD))
+ {
+ trace!("Device is stopped!");
+ if device_event == BACKEND_EFD {
+ let _ = self.controller.write().unwrap().rx_event_fd.read();
+ }
+ return Ok(());
+ }
+
+ if device_event == RX_QUEUE && self.controller.write().unwrap().rx_is_empty() {
+ return Ok(());
+ };
+
+ let vring = if device_event != BACKEND_EFD {
+ &vrings[device_event as usize]
+ } else {
+ let _ = self.controller.write().unwrap().rx_event_fd.read();
+ &vrings[RX_QUEUE as usize]
+ };
+
+ if self.event_idx {
+ // vm-virtio's Queue implementation only checks avail_index
+ // once, so to properly support EVENT_IDX we need to keep
+ // calling process_request_queue() until it stops finding
+ // new requests on the queue.
+ loop {
+ vring.disable_notification().unwrap();
+ match device_event {
+ CTRL_QUEUE => self.process_ctrl_queue(vring),
+ TX_QUEUE => self.process_tx_queue(vring),
+ RX_QUEUE => self.process_rx_queue(vring),
+ BACKEND_EFD => self.process_rx_queue(vring),
+ _ => Err(Error::HandleEventUnknown),
+ }?;
+ if !vring.enable_notification().unwrap() {
+ break;
+ }
+ }
+ } else {
+ // Without EVENT_IDX, a single call is enough.
+ match device_event {
+ CTRL_QUEUE => self.process_ctrl_queue(vring),
+ TX_QUEUE => self.process_tx_queue(vring),
+ RX_QUEUE => self.process_rx_queue(vring),
+ BACKEND_EFD => self.process_rx_queue(vring),
+ _ => Err(Error::HandleEventUnknown),
+ }?;
+ }
+ Ok(())
+ }
+
+ fn exit_event(&self, _thread_index: usize) -> Option<EventFd> {
+ self.exit_event.try_clone().ok()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::virtio_can::{VirtioCanCtrlResponse, VirtioCanTxResponse};
+ use std::mem::size_of;
+ use virtio_bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE};
+ use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue};
+ use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, Le16, Le32};
+
+ #[test]
+ fn test_virtio_can_tx_response_default() {
+ let response = VirtioCanTxResponse::default();
+ assert_eq!(response.result, 0);
+ }
+
+ #[test]
+ fn test_virtio_can_ctrl_request_default() {
+ let request = VirtioCanCtrlRequest::default();
+ assert_eq!(request.msg_type, Le16::default());
+ }
+
+ #[test]
+ fn test_virtio_can_ctrl_response_default() {
+ let response = VirtioCanCtrlResponse::default();
+ assert_eq!(response.result, 0);
+ }
+
+ #[test]
+ fn test_virtio_can_frame_default() {
+ let frame = VirtioCanFrame::default();
+ assert_eq!(frame.msg_type, Le16::default());
+ assert_eq!(frame.length, Le16::default());
+ assert_eq!(frame.reserved, Le32::default());
+ assert_eq!(frame.flags, Le32::default());
+ assert_eq!(frame.can_id, Le32::default());
+ assert_eq!(frame.sdu, [0; 64]);
+ }
+
+ #[test]
+ fn test_virtio_can_empty_requests() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryAtomic::new(
+ GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
+ );
+ let vring = VringRwLock::new(mem.clone(), 0x1000).unwrap();
+
+ // Empty descriptor chain should be ignored
+ assert!(vu_can_backend
+ .process_rx_requests(Vec::<CanDescriptorChain>::new(), &vring)
+ .expect("Fail to examin empty rx vring"));
+ assert!(vu_can_backend
+ .process_tx_requests(Vec::<CanDescriptorChain>::new(), &vring)
+ .expect("Fail to examin empty tx vring"));
+ assert!(vu_can_backend
+ .process_ctrl_requests(Vec::<CanDescriptorChain>::new(), &vring)
+ .expect("Fail to examin empty ctrl vring"));
+ }
+
+ #[test]
+ fn test_virtio_can_empty_handle_request() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryAtomic::new(
+ GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
+ );
+ vu_can_backend.update_memory(mem.clone()).unwrap();
+
+ let vring = VringRwLock::new(mem, 0x1000).unwrap();
+ vring.set_queue_info(0x100, 0x200, 0x300).unwrap();
+ vring.set_queue_ready(true);
+ let list_vrings = [vring.clone(), vring.clone(), vring.clone(), vring.clone()];
+
+ vu_can_backend
+ .handle_event(RX_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_can_backend
+ .handle_event(TX_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_can_backend
+ .handle_event(CTRL_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_can_backend
+ .handle_event(BACKEND_EFD, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+ }
+
+ fn build_desc_chain_mem(
+ mem: &GuestMemoryMmap,
+ count: u16,
+ flags: Vec<u16>,
+ len: u32,
+ ) -> CanDescriptorChain {
+ let vq = MockSplitQueue::new(mem, 16);
+ let mut desc_vec = Vec::new();
+
+ //Create a descriptor chain with count descriptors.
+ for i in 0..count {
+ let desc_flags = if i < count - 1 {
+ flags[i as usize] | VRING_DESC_F_NEXT as u16
+ } else {
+ flags[i as usize] & !VRING_DESC_F_NEXT as u16
+ };
+
+ let desc = Descriptor::new((0x100 * (i + 1)) as u64, len, desc_flags, i + 1);
+ desc_vec.push(desc);
+ }
+
+ vq.add_desc_chains(&desc_vec, 0).unwrap();
+
+ // Create descriptor chain from pre-filled memory
+ vq.create_queue::<Queue>()
+ .unwrap()
+ .iter(GuestMemoryAtomic::new(mem.clone()).memory())
+ .unwrap()
+ .next()
+ .unwrap()
+ }
+
+ #[test]
+ fn test_virtio_can_ctrl_request() {
+ // Initialize can device and vhost-device-can backend
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Test 1: Since we provide only one write only, an error will be trigger
+ // the reader will have 0 data.
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 1,
+ vec![VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_ctrl_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 2: Two write only descriptors are available, so the reader
+ // will complain for having 0 data
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![VRING_DESC_F_WRITE as u16, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_ctrl_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 3: The reader descriptor has two bytes of data
+ // but are initialized to 0 -> Unknown control message
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert!(vu_can_backend
+ .process_ctrl_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res);
+
+ // Test 4: Successfull test for VIRTIO_CAN_SET_CTRL_MODE_START
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ // Write a value on the writer which does not represent known messages
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_ctrl_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res);
+
+ // Test 5: Successfull test for VIRTIO_CAN_SET_CTRL_MODE_STOP
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_STOP;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ // Write a value on the writer which does not represent known messages
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_ctrl_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res);
+
+ // Test 6: Since we provide only one read only, an error will be trigger
+ // about the writer having 0 data
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], can_mes_len.try_into().unwrap());
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_ctrl_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 7: Two read only descriptors are available, so the writer
+ // will complain for having 0 data
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, 0], can_mes_len.try_into().unwrap());
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_STOP;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_ctrl_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 8: If we interchange the write read descriptors then
+ // it will fail!
+
+ let can_mes_len = size_of::<VirtioCanCtrlRequest>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![VRING_DESC_F_WRITE as u16, 0],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START;
+ desc_chain
+ .memory()
+ .write_obj(ctrl_msg, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ // Write a value on the writer which does not represent known messages
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_ctrl_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x100_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res);
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_unknown_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedCanMsgType(0)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_can_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Test 1: UnexpectedClassicFlag
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 4.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 2: Return the same length
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap().length,
+ frame.length
+ );
+
+ // Test 3: Return Error::InvalidCanLength(frame_length) when length is out-of-range (>8)
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 40.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(40)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_canfd_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller).expect("Could not build vhucan device");
+
+ // Enable VIRTIO_CAN_F_CAN_FD feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_FD);
+
+ // Test 1: If no VIRTIO_CAN_FLAGS_FD in flag return UnexpectedClassicFlag
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 12.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 2: If VIRTIO_CAN_FLAGS_FD is in flag check if return message has
+ // CAN_FRMF_TYPE_FD in flags.
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 12.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_tx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & CAN_FRMF_TYPE_FD,
+ CAN_FRMF_TYPE_FD
+ );
+
+ // Test 3: If 1 is not a valid CAN flag return InvalidCanFlags(1).
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 1.into(),
+ length: 12.into(),
+ reserved: 0.into(),
+ flags: 0x1.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanFlags(1)
+ );
+
+ // Test 4: receive frame with the same length if length is < 64 but not
+ // one of [12, 16, 20, 24, 32, 48, 64] -> InvalidCanLength(40)]
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 40.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(40)
+ );
+
+ // Test 5: receive frame with length is > 64
+ // then -> InvalidCanLength(frame_length).
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 80.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(80)
+ );
+
+ // Test 6: receive frame with length in [12, 16, 20, 24, 32, 48, 64]
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 48.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(vu_can_backend.check_tx_frame(frame).unwrap().length, 48);
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_rtr_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Test 1: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC and
+ // VIRTIO_CAN_F_RTR_FRAMES are not negotiated.
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: VIRTIO_CAN_FLAGS_RTR.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+
+ // Test 2: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated.
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_RTR_FRAMES);
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+
+ // Test 3: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should succeed because VIRTIO_CAN_F_CAN_CLASSIC is negotiated.
+ vu_can_backend
+ .acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_CLASSIC));
+
+ assert_eq!(
+ vu_can_backend
+ .check_tx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ & CAN_RTR_FLAG,
+ CAN_RTR_FLAG
+ );
+
+ // Test 4: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated,
+ // and RTR does not work with VIRTIO_CAN_F_CAN_FD.
+ vu_can_backend.acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_FD));
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_tx_eff_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // This test is valid for both CAN & CANFD messages, so for simplicity
+ // we will check only CAN case.
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ // Test 1: Received message should not have CAN_EFF_FLAG in can_id
+ let mut frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_tx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ & CAN_EFF_FLAG,
+ 0
+ );
+
+ // Test 2: If VIRTIO_CAN_FLAGS_EXTENDED is NOT enabled, CAN ID should
+ // be smaller than 11 bits
+ frame.can_id = (CAN_SFF_MASK + 1).into(); // CAN_SFF_MASK = 0x7FFU
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedCanId(11)
+ );
+
+ // Test 3: Received message should have CAN_EFF_MASK in can_id
+ frame.flags = VIRTIO_CAN_FLAGS_EXTENDED.into();
+ assert_eq!(
+ vu_can_backend
+ .check_tx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ & CAN_EFF_FLAG,
+ CAN_EFF_FLAG
+ );
+
+ // Test 4: If VIRTIO_CAN_FLAGS_EXTENDED is enabled, CAN ID should be
+ // smaller than 29 bits
+ frame.can_id = (CAN_EFF_MASK + 1).into(); // CAN_EFF_MASK = 0x1FFFFFFFU
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::UnexpectedCanId(29)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_tx_general_tests() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Any number of descriptor different than 2 will generate an error
+ let count = 1;
+
+ // Test 1: Provide only write only descriptor -> Fail
+ let desc_chain = build_desc_chain_mem(&mem, count, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 2: Provide only read only descriptor -> Fail
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 3: Provide only write only descriptors -> Fail
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![VRING_DESC_F_WRITE as u16, VRING_DESC_F_WRITE as u16],
+ 0x200,
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 4: Provide only read only descriptors -> Fail
+ let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, 0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 5: Provide correct descriptors but 0 size -> Fail
+ let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, VRING_DESC_F_WRITE as u16], 0x0);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 6: Provide correct descriptors and size
+ let can_mes_len = size_of::<VirtioCanFrame>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert!(vu_can_backend
+ .process_tx_requests(vec![desc_chain], &vring)
+ .unwrap());
+ }
+
+ #[test]
+ fn test_virtio_can_tx_device_stopped_test() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Enable VIRTIO_CAN_F_CAN_CLASSIC feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ let can_mes_len = size_of::<VirtioCanFrame>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ desc_chain
+ .memory()
+ .write_obj(frame, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res);
+ }
+
+ #[test]
+ fn test_virtio_can_tx_device_started_test_send_fail() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Enable VIRTIO_CAN_F_CAN_CLASSIC feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ // Start the device
+ controller.write().unwrap().ctrl_state = CAN_CS_STARTED;
+
+ let can_mes_len = size_of::<VirtioCanFrame>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ desc_chain
+ .memory()
+ .write_obj(frame, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res);
+ }
+
+ #[test]
+ fn test_virtio_can_tx_device_started_check_frame_fail() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Start the device
+ controller.write().unwrap().ctrl_state = CAN_CS_STARTED;
+
+ let can_mes_len = size_of::<VirtioCanFrame>();
+ let desc_chain = build_desc_chain_mem(
+ &mem,
+ 2,
+ vec![0, VRING_DESC_F_WRITE as u16],
+ can_mes_len.try_into().unwrap(),
+ );
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ desc_chain
+ .memory()
+ .write_obj(frame, vm_memory::GuestAddress(0x100_u64))
+ .unwrap();
+
+ desc_chain
+ .memory()
+ .write_obj(5, vm_memory::GuestAddress(0x200_u64))
+ .unwrap();
+
+ assert!(vu_can_backend
+ .process_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ let can_frame_res = desc_chain
+ .memory()
+ .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res);
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_err_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: CAN_ERR_FLAG.into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::RxFrameUnknownFail
+ );
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: (CAN_ERR_FLAG | CAN_ERR_BUSOFF).into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::BusoffRxFrame
+ );
+
+ assert_eq!(
+ controller.read().unwrap().config.status.to_native(),
+ VIRTIO_CAN_S_CTRL_BUSOFF
+ );
+ assert_eq!(controller.read().unwrap().ctrl_state, CAN_CS_STOPPED);
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_features_not_negotiated() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedFdFlag
+ );
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 0.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_eff_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // This test is valid for both CAN & CANFD messages, so for simplicity
+ // we will check only CAN case.
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ // Test 1: Received message should not have CAN_EFF_FLAG in can_id
+ let mut frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ & CAN_EFF_FLAG,
+ 0
+ );
+
+ // Test 2: Received message's can_id should be smaller than CAN_SFF_MASK
+ frame.can_id = (CAN_SFF_MASK + 1).into(); // CAN_SFF_MASK = 0x7FFU
+ assert!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ < CAN_SFF_MASK,
+ );
+
+ // Test 3: Received message should have CAN_EFF_MASK in can_id
+ frame.can_id = CAN_EFF_FLAG.into();
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & VIRTIO_CAN_FLAGS_EXTENDED,
+ VIRTIO_CAN_FLAGS_EXTENDED
+ );
+
+ // Test 4: Received message's can_id should be equal to CAN_EFF_MASK,
+ // since the 3 MSB have been zeroed.
+ frame.can_id = (frame.can_id.to_native() | CAN_EFF_MASK).into(); // CAN_EFF_MASK = 0x1FFFFFFFU
+ assert!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .can_id
+ .to_native()
+ == CAN_EFF_MASK
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_rtr_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: CAN_RTR_FLAG.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ // Test 1: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated.
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+
+ // Test 2: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should succeed because VIRTIO_CAN_F_CAN_CLASSIC is negotiated.
+ vu_can_backend
+ .acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_CLASSIC));
+
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & VIRTIO_CAN_FLAGS_RTR,
+ VIRTIO_CAN_FLAGS_RTR
+ );
+
+ // Test 3: Take a valid CAN / CANFD message and try to enable RTR in flags.
+ // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated,
+ // and RTR does not work with VIRTIO_CAN_F_CAN_FD.
+
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: CAN_RTR_FLAG.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(), // Mark it as CAN FD frame
+ sdu: [0; 64],
+ };
+
+ vu_can_backend.acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_FD));
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedRtrFlag
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_can_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Test 1: UnexpectedClassicFlag
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 4.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 2: Return the same length
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap().length,
+ frame.length
+ );
+
+ // Test 3: Return the length equal to 8
+ let frame = VirtioCanFrame {
+ msg_type: VIRTIO_CAN_TX.into(),
+ can_id: 0.into(),
+ length: 40.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_tx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(40)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_canfd_frame() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Enable VIRTIO_CAN_F_CAN_FD feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_FD);
+
+ // Test 1: If no CAN_FRMF_TYPE_FD in flags return UnexpectedClassicFlag
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 40.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 2: If VIRTIO_CAN_FLAGS_FD is in flag check if return message has
+ // VIRTIO_CAN_FLAGS_FD in flags.
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 48.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & VIRTIO_CAN_FLAGS_FD,
+ VIRTIO_CAN_FLAGS_FD
+ );
+
+ // Test 3: receive frame with the same length if length is < 64
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap().length,
+ frame.length
+ );
+
+ // Test 4: receive frame with length out-of-range: length is > 64
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 80.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(80)
+ );
+
+ // Test 5: receive frame with length out-of-range: length no in
+ // list: [12, 16, 20, 24, 32, 48, 64]
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 62.into(),
+ reserved: 0.into(),
+ flags: CAN_FRMF_TYPE_FD.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap_err(),
+ Error::InvalidCanLength(62)
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_check_rx_canfd_vcan0() {
+ let controller =
+ CanController::new("vcan0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+
+ // Enable VIRTIO_CAN_F_CAN_FD feature
+ vu_can_backend.acked_features((1 << VIRTIO_CAN_F_CAN_FD) | (1 << VIRTIO_CAN_F_CAN_CLASSIC));
+
+ // If VIRTIO_CAN_F_CAN_FD and VIRTIO_CAN_F_CAN_CLASSIC are negotiated
+ // and interface is "vcan0" check if return message has
+ // VIRTIO_CAN_FLAGS_FD in flags and has been treated as CANFD frame.
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 0.into(),
+ length: 48.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ assert_eq!(
+ vu_can_backend
+ .check_rx_frame(frame)
+ .unwrap()
+ .flags
+ .to_native()
+ & VIRTIO_CAN_FLAGS_FD,
+ VIRTIO_CAN_FLAGS_FD
+ );
+
+ assert_eq!(
+ vu_can_backend.check_rx_frame(frame).unwrap().length,
+ frame.length
+ );
+ }
+
+ #[test]
+ fn test_virtio_can_rx_request() {
+ let controller =
+ CanController::new("can0".to_string()).expect("Could not build controller");
+ let controller = Arc::new(RwLock::new(controller));
+ let mut vu_can_backend =
+ VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device");
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Note: The following test are focusing only to simple CAN messages.
+
+ // Test 1: This should succeed because there is no element inserted in the
+ // CAN/FD frames' queue
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert!(!vu_can_backend
+ .process_rx_requests(vec![desc_chain], &vring)
+ .unwrap(),);
+
+ // Test 2: This should fail because there is a simple CAN frame
+ // inserted in the CAN/FD frames' queue, but this does not
+ // pass the checks.
+
+ // Push a new can message into the can.rs queue
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 64.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ controller.write().unwrap().push(frame).unwrap();
+
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert_eq!(
+ vu_can_backend
+ .process_rx_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::UnexpectedClassicFlag
+ );
+
+ // Test 3: This should succeed because there is a simple CAN frame
+ // inserted in the CAN/FD frames' queue, and this does
+ // pass the checks.
+
+ // Enable VIRTIO_CAN_F_CAN_CLASSIC feature
+ vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC);
+
+ // Push a new can message into the can.rs queue
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+
+ controller.write().unwrap().push(frame).unwrap();
+
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+ assert!(vu_can_backend
+ .process_rx_requests(vec![desc_chain], &vring)
+ .unwrap());
+
+ // Test 4: This should fail because the descriptor is read-only
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ // Push a new can message into the can.rs queue
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+ controller.write().unwrap().push(frame).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_rx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 5: This should fail because the descriptor length is less
+ // than VirtioCanFrame size.
+ let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x10);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_can_backend.update_memory(mem1).unwrap();
+
+ // Push a new can message into the can.rs queue
+ let frame = VirtioCanFrame {
+ msg_type: 0.into(),
+ can_id: 123.into(),
+ length: 8.into(),
+ reserved: 0.into(),
+ flags: 0.into(),
+ sdu: [0; 64],
+ };
+ controller.write().unwrap().push(frame).unwrap();
+
+ assert_eq!(
+ vu_can_backend
+ .process_rx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs
new file mode 100755
index 00000000..768dc12a
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs
@@ -0,0 +1,144 @@
+// CAN virtio bindings
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use vm_memory::{ByteValued, Le16, Le32};
+
+/// CAN FRAME Flags and Masks
+pub(crate) const CAN_EFF_FLAG: u32 = 0x80000000; // EFF/SFF is set in the MSB
+pub(crate) const CAN_RTR_FLAG: u32 = 0x40000000; // remote transmission request
+pub(crate) const CAN_ERR_FLAG: u32 = 0x20000000; // error message frame
+pub(crate) const CAN_SFF_MASK: u32 = 0x000007FF; // standard frame format (SFF)
+pub(crate) const CAN_EFF_MASK: u32 = 0x1FFFFFFF; // extended frame format (EFF)
+#[allow(dead_code)]
+pub(crate) const CAN_FRMF_BRS: u32 = 0x01; // bit rate switch (2nd bitrate for data)
+#[allow(dead_code)]
+pub(crate) const CAN_FRMF_ESI: u32 = 0x02; // error state ind. of transmitting node
+pub(crate) const CAN_FRMF_TYPE_FD: u32 = 0x10; // internal bit ind. of CAN FD frame
+pub(crate) const CAN_ERR_BUSOFF: u32 = 0x00000040; // bus off
+
+/// CANFD frame's valid data lengths
+pub(crate) const CANFD_VALID_LENGTHS: [u32; 7] = [12, 16, 20, 24, 32, 48, 64];
+
+/// CAN controller states
+pub(crate) const CAN_CS_STARTED: u8 = 0x01;
+pub(crate) const CAN_CS_STOPPED: u8 = 0x02;
+
+/// CAN flags to determine type of CAN FRAME Id
+pub(crate) const VIRTIO_CAN_FLAGS_EXTENDED: u32 = 0x8000;
+pub(crate) const VIRTIO_CAN_FLAGS_FD: u32 = 0x4000;
+pub(crate) const VIRTIO_CAN_FLAGS_RTR: u32 = 0x2000;
+
+pub(crate) const VIRTIO_CAN_FLAGS_VALID_MASK: u32 =
+ VIRTIO_CAN_FLAGS_EXTENDED | VIRTIO_CAN_FLAGS_FD | VIRTIO_CAN_FLAGS_RTR;
+
+pub(crate) const VIRTIO_CAN_TX: u16 = 0x0001;
+pub(crate) const VIRTIO_CAN_RX: u16 = 0x0101;
+
+/// Feature bit numbers
+pub const VIRTIO_CAN_F_CAN_CLASSIC: u16 = 0;
+pub const VIRTIO_CAN_F_CAN_FD: u16 = 1;
+pub const VIRTIO_CAN_S_CTRL_BUSOFF: u16 = 2; /* Controller BusOff */
+#[allow(dead_code)]
+pub const VIRTIO_CAN_F_LATE_TX_ACK: u16 = 2;
+pub const VIRTIO_CAN_F_RTR_FRAMES: u16 = 3;
+
+/// Possible values of the status field
+pub const VIRTIO_CAN_RESULT_OK: u8 = 0x0;
+pub const VIRTIO_CAN_RESULT_NOT_OK: u8 = 0x1;
+
+/// CAN Control messages
+pub const VIRTIO_CAN_SET_CTRL_MODE_START: u16 = 0x0201;
+pub const VIRTIO_CAN_SET_CTRL_MODE_STOP: u16 = 0x0202;
+
+/// Virtio Can Configuration
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+#[repr(C)]
+pub(crate) struct VirtioCanConfig {
+ /// CAN controller status
+ pub(crate) status: Le16,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanConfig {}
+
+/// Virtio CAN Request / Response messages
+///
+/// The response message is a stream of bytes, where first byte represents the
+/// status, and rest is message specific data.
+
+#[derive(Copy, Clone, Default)]
+#[repr(C)]
+pub struct VirtioCanTxResponse {
+ pub result: i8,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanTxResponse {}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[repr(C)]
+pub struct VirtioCanFrame {
+ pub msg_type: Le16,
+ pub length: Le16, /* 0..8 CC, 0..64 CAN­FD, 0..2048 CAN­XL, 12 bits */
+ pub reserved: Le32, /* May be needed in part for CAN XL priority */
+ pub flags: Le32,
+ pub can_id: Le32,
+ pub sdu: [u8; 64],
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanFrame {}
+
+impl Default for VirtioCanFrame {
+ fn default() -> Self {
+ VirtioCanFrame {
+ msg_type: Le16::default(),
+ length: Le16::default(),
+ reserved: Le32::default(),
+ flags: Le32::default(),
+ can_id: Le32::default(),
+ sdu: [0; 64], // Initialize "sdu" with default value (0 in this case)
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Default)]
+#[repr(C)]
+pub struct VirtioCanHeader {
+ pub msg_type: Le16,
+ pub length: Le16, /* 0..8 CC, 0..64 CAN­FD, 0..2048 CAN­XL, 12 bits */
+ pub reserved: Le32, /* May be needed in part for CAN XL priority */
+ pub flags: Le32,
+ pub can_id: Le32,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanHeader {}
+
+#[derive(Copy, Clone, Default)]
+#[repr(C)]
+pub struct VirtioCanCtrlRequest {
+ pub msg_type: Le16,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanCtrlRequest {}
+
+#[derive(Copy, Clone, Default)]
+#[repr(C)]
+pub struct VirtioCanCtrlResponse {
+ pub result: i8,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioCanCtrlResponse {}