mirror of
https://github.com/rust-vmm/vhost-device.git
synced 2025-12-28 08:01:04 +00:00
Merge pull request #28 from rust-vmm/refactor_for_testing
[i2c] Refactoring for improved testing & separation of concerns
This commit is contained in:
commit
a2e55a91e9
371
Cargo.lock
generated
Normal file
371
Cargo.lock
generated
Normal file
@ -0,0 +1,371 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6df5aef5c5830360ce5218cecb8f018af3438af5686ae945094affc86fdec63"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.0-beta.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.0.0-beta.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "epoll"
|
||||
version = "4.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a6b90237e10f1a61b35fba73885c3567e1a5a8c40d44daae335f7710210a7dc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vhost-device-i2c"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"epoll",
|
||||
"libc",
|
||||
"log",
|
||||
"vhost",
|
||||
"vhost-user-backend",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vhost-user-backend"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vhost-user-backend?rev=70f668a699d865f13ba40498897ad181a409bd41#70f668a699d865f13ba40498897ad181a409bd41"
|
||||
dependencies = [
|
||||
"epoll",
|
||||
"libc",
|
||||
"log",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtio-bindings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff512178285488516ed85f15b5d0113a7cdb89e9e8a760b269ae4f02b84bd6b"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-virtio?rev=6013dd9#6013dd91b2e6eb77ea10c6bdeda8f5eb18de6dda"
|
||||
dependencies = [
|
||||
"log",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a8ebcb86ca457f9d6e14cf97009f679952eba42f0113de5db596e514cd0e43b"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cf11afbc4ebc0d5c7a7748a77d19e2042677fc15faa2f4ccccb27c18a60605"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"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-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[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 = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"coverage_score": 40.6,
|
||||
"coverage_score": 49.4,
|
||||
"exclude_path": "",
|
||||
"crate_features": ""
|
||||
}
|
||||
|
||||
@ -20,4 +20,4 @@ vhost = { version = "0.1", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost-user-backend", rev = "70f668a699d865f13ba40498897ad181a409bd41" }
|
||||
virtio-bindings = ">=0.1"
|
||||
vm-memory = "0.6"
|
||||
vmm-sys-util = ">=0.8.0"
|
||||
vmm-sys-util = "=0.8.0"
|
||||
|
||||
@ -5,11 +5,14 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use libc::{c_ulong, ioctl, EADDRINUSE, EADDRNOTAVAIL, EINVAL};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use libc::{c_ulong, ioctl, EINVAL};
|
||||
use vmm_sys_util::errno::{errno_result, Error, Result};
|
||||
|
||||
use super::AdapterConfig;
|
||||
|
||||
// The type of the `req` parameter is different for the `musl` library. This will enable
|
||||
// successful build for other non-musl libraries.
|
||||
#[cfg(target_env = "musl")]
|
||||
@ -230,25 +233,99 @@ pub struct I2cReq {
|
||||
pub buf: Vec<u8>,
|
||||
}
|
||||
|
||||
/// I2C adapter and helpers
|
||||
pub trait I2cAdapterTrait: Send + Sync + 'static {
|
||||
fn new(bus: &str) -> Result<Self>
|
||||
/// Trait that represents an I2C Device.
|
||||
///
|
||||
/// This trait is introduced for development purposes only, and should not
|
||||
/// be used outside of this crate. The purpose of this trait is to provide a
|
||||
/// mock implementation for the I2C driver so that we can test the I2C
|
||||
/// functionality without the need of a physical device.
|
||||
pub trait I2cDevice {
|
||||
// Open the device specified by path.
|
||||
fn open(device_path: String) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn bus(&self) -> u32;
|
||||
fn is_smbus(&self) -> bool;
|
||||
// Corresponds to the I2C_FUNCS ioctl call.
|
||||
fn funcs(&mut self, func: u64) -> i32;
|
||||
|
||||
/// Sets device's address for an I2C adapter.
|
||||
fn set_device_addr(&self, addr: usize) -> Result<()>;
|
||||
// Corresponds to the I2C_RDWR ioctl call.
|
||||
fn rdwr(&self, data: &I2cRdwrIoctlData) -> i32;
|
||||
|
||||
/// Gets adapter's functionality
|
||||
fn get_func(&mut self) -> Result<()>;
|
||||
// Corresponds to the I2C_SMBUS ioctl call.
|
||||
fn smbus(&self, data: &I2cSmbusIoctlData) -> i32;
|
||||
|
||||
/// Transfer data
|
||||
fn do_i2c_transfer(&self, data: &I2cRdwrIoctlData, addr: u16) -> Result<()>;
|
||||
// Corresponds to the I2C_SLAVE ioctl call.
|
||||
fn slave(&self, addr: u64) -> i32;
|
||||
}
|
||||
|
||||
fn do_smbus_transfer(&self, data: &I2cSmbusIoctlData, addr: u16) -> Result<()>;
|
||||
/// A physical I2C device. This structure can only be initialized on hosts
|
||||
/// where `/dev/i2c-XX` is available.
|
||||
pub struct PhysDevice {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl I2cDevice for PhysDevice {
|
||||
fn open(device_path: String) -> Result<Self> {
|
||||
Ok(PhysDevice {
|
||||
file: OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(device_path)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn funcs(&mut self, func: u64) -> i32 {
|
||||
unsafe { ioctl(self.file.as_raw_fd(), I2C_FUNCS, &func) }
|
||||
}
|
||||
|
||||
fn rdwr(&self, data: &I2cRdwrIoctlData) -> i32 {
|
||||
unsafe { ioctl(self.file.as_raw_fd(), I2C_RDWR, data) }
|
||||
}
|
||||
|
||||
fn smbus(&self, data: &I2cSmbusIoctlData) -> i32 {
|
||||
unsafe { ioctl(self.file.as_raw_fd(), I2C_SMBUS, data) }
|
||||
}
|
||||
|
||||
fn slave(&self, addr: u64) -> i32 {
|
||||
unsafe { ioctl(self.file.as_raw_fd(), I2C_SLAVE, addr as c_ulong) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct I2cAdapter<D: I2cDevice> {
|
||||
device: D,
|
||||
adapter_no: u32,
|
||||
smbus: bool,
|
||||
}
|
||||
|
||||
impl<D: I2cDevice> I2cAdapter<D> {
|
||||
// Creates a new adapter corresponding to the specified number.
|
||||
fn new(adapter_no: u32) -> Result<I2cAdapter<D>> {
|
||||
let i2cdev = format!("/dev/i2c-{}", adapter_no);
|
||||
let func: u64 = I2C_FUNC_SMBUS_ALL;
|
||||
let mut device = D::open(i2cdev)?;
|
||||
let smbus;
|
||||
|
||||
let ret = device.funcs(func);
|
||||
if ret == -1 {
|
||||
println!("Failed to get I2C function");
|
||||
return errno_result();
|
||||
}
|
||||
|
||||
if (func & I2C_FUNC_I2C) != 0 {
|
||||
smbus = false;
|
||||
} else if (func & I2C_FUNC_SMBUS_ALL) != 0 {
|
||||
smbus = true;
|
||||
} else {
|
||||
println!("Invalid functionality {:x}", func);
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
Ok(I2cAdapter {
|
||||
device,
|
||||
adapter_no,
|
||||
smbus,
|
||||
})
|
||||
}
|
||||
|
||||
/// Perform I2C_RDWR transfer
|
||||
fn i2c_transfer(&self, reqs: &mut [I2cReq]) -> Result<()> {
|
||||
@ -270,14 +347,27 @@ pub trait I2cAdapterTrait: Send + Sync + 'static {
|
||||
nmsgs: len as u32,
|
||||
};
|
||||
|
||||
self.do_i2c_transfer(&data, addr)
|
||||
let ret = self.device.rdwr(&data);
|
||||
if ret == -1 {
|
||||
println!("Failed to transfer i2c data to device addr to {:x}", addr);
|
||||
errno_result()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform I2C_SMBUS transfer
|
||||
fn smbus_transfer(&self, reqs: &mut [I2cReq]) -> Result<()> {
|
||||
let smbus_data = I2cSmbusIoctlData::new(reqs)?;
|
||||
|
||||
self.do_smbus_transfer(&smbus_data, reqs[0].addr)?;
|
||||
let ret = self.device.smbus(&smbus_data);
|
||||
if ret == -1 {
|
||||
println!(
|
||||
"Failed to transfer smbus data to device addr to {:x}",
|
||||
reqs[0].addr
|
||||
);
|
||||
return errno_result();
|
||||
}
|
||||
|
||||
if smbus_data.read_write == I2C_SMBUS_READ {
|
||||
unsafe {
|
||||
@ -298,27 +388,9 @@ pub trait I2cAdapterTrait: Send + Sync + 'static {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct I2cAdapter {
|
||||
fd: File,
|
||||
bus: u32,
|
||||
smbus: bool,
|
||||
}
|
||||
|
||||
impl I2cAdapterTrait for I2cAdapter {
|
||||
fn new(bus: &str) -> Result<I2cAdapter> {
|
||||
let i2cdev = String::from("/dev/i2c-") + bus;
|
||||
|
||||
Ok(I2cAdapter {
|
||||
bus: bus.parse::<u32>().map_err(|_| Error::new(EINVAL))?,
|
||||
smbus: false,
|
||||
fd: OpenOptions::new().read(true).write(true).open(i2cdev)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn bus(&self) -> u32 {
|
||||
self.bus
|
||||
fn adapter_no(&self) -> u32 {
|
||||
self.adapter_no
|
||||
}
|
||||
|
||||
fn is_smbus(&self) -> bool {
|
||||
@ -327,7 +399,7 @@ impl I2cAdapterTrait for I2cAdapter {
|
||||
|
||||
/// Sets device's address for an I2C adapter.
|
||||
fn set_device_addr(&self, addr: usize) -> Result<()> {
|
||||
let ret = unsafe { ioctl(self.fd.as_raw_fd(), I2C_SLAVE, addr as c_ulong) };
|
||||
let ret = self.device.slave(addr as u64);
|
||||
|
||||
if ret == -1 {
|
||||
println!("Failed to set device addr to {:x}", addr);
|
||||
@ -337,103 +409,44 @@ impl I2cAdapterTrait for I2cAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets adapter's functionality
|
||||
fn get_func(&mut self) -> Result<()> {
|
||||
let func: u64 = I2C_FUNC_SMBUS_ALL;
|
||||
|
||||
let ret = unsafe { ioctl(self.fd.as_raw_fd(), I2C_FUNCS, &func) };
|
||||
|
||||
if ret == -1 {
|
||||
println!("Failed to get I2C function");
|
||||
return errno_result();
|
||||
}
|
||||
|
||||
if (func & I2C_FUNC_I2C) != 0 {
|
||||
self.smbus = false;
|
||||
} else if (func & I2C_FUNC_SMBUS_ALL) != 0 {
|
||||
self.smbus = true;
|
||||
fn transfer(&self, reqs: &mut [I2cReq]) -> Result<()> {
|
||||
if self.is_smbus() {
|
||||
self.smbus_transfer(reqs)
|
||||
} else {
|
||||
println!("Invalid functionality {:x}", func);
|
||||
return Err(Error::new(EINVAL));
|
||||
self.i2c_transfer(reqs)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Transfer data
|
||||
fn do_i2c_transfer(&self, data: &I2cRdwrIoctlData, addr: u16) -> Result<()> {
|
||||
let ret = unsafe { ioctl(self.fd.as_raw_fd(), I2C_RDWR, data) };
|
||||
|
||||
if ret == -1 {
|
||||
println!("Failed to transfer i2c data to device addr to {:x}", addr);
|
||||
errno_result()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn do_smbus_transfer(&self, data: &I2cSmbusIoctlData, addr: u16) -> Result<()> {
|
||||
let ret = unsafe { ioctl(self.fd.as_raw_fd(), I2C_SMBUS, data) };
|
||||
|
||||
if ret == -1 {
|
||||
println!("Failed to transfer smbus data to device addr to {:x}", addr);
|
||||
return errno_result();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// I2C map and helpers
|
||||
const MAX_I2C_VDEV: usize = 1 << 7;
|
||||
pub(crate) const MAX_I2C_VDEV: usize = 1 << 7;
|
||||
const I2C_INVALID_ADAPTER: u32 = 0xFFFFFFFF;
|
||||
|
||||
pub struct I2cMap<A: I2cAdapterTrait> {
|
||||
adapters: Vec<A>,
|
||||
pub struct I2cMap<D: I2cDevice> {
|
||||
adapters: Vec<I2cAdapter<D>>,
|
||||
device_map: [u32; MAX_I2C_VDEV],
|
||||
}
|
||||
|
||||
impl<A: I2cAdapterTrait> I2cMap<A> {
|
||||
pub fn new(list: &str) -> Result<Self>
|
||||
impl<D: I2cDevice> I2cMap<D> {
|
||||
pub(crate) fn new(device_config: &AdapterConfig) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut device_map: [u32; MAX_I2C_VDEV] = [I2C_INVALID_ADAPTER; MAX_I2C_VDEV];
|
||||
let mut adapters: Vec<A> = Vec::new();
|
||||
let busses: Vec<&str> = list.split(',').collect();
|
||||
let mut adapters: Vec<I2cAdapter<D>> = Vec::new();
|
||||
|
||||
for (i, businfo) in busses.iter().enumerate() {
|
||||
let list: Vec<&str> = businfo.split(':').collect();
|
||||
let mut adapter = A::new(list[0])?;
|
||||
let devices = &list[1..];
|
||||
for (i, device_cfg) in device_config.inner.iter().enumerate() {
|
||||
let adapter = I2cAdapter::new(device_cfg.adapter_no)?;
|
||||
|
||||
adapter.get_func()?;
|
||||
|
||||
for device in devices {
|
||||
let device = device.parse::<usize>().map_err(|_| Error::new(EINVAL))?;
|
||||
|
||||
if device > MAX_I2C_VDEV {
|
||||
println!("Invalid device address {}", device);
|
||||
return Err(Error::new(EADDRNOTAVAIL));
|
||||
}
|
||||
|
||||
if device_map[device] != I2C_INVALID_ADAPTER {
|
||||
println!(
|
||||
"Client address {} is already used by {}",
|
||||
device,
|
||||
adapters[device_map[device] as usize].bus()
|
||||
);
|
||||
return Err(Error::new(EADDRINUSE));
|
||||
}
|
||||
|
||||
adapter.set_device_addr(device)?;
|
||||
device_map[device] = i as u32;
|
||||
// Check that all addresses corresponding to the adapter are valid.
|
||||
for addr in &device_cfg.addr {
|
||||
adapter.set_device_addr(*addr as usize)?;
|
||||
device_map[*addr as usize] = i as u32;
|
||||
}
|
||||
|
||||
println!(
|
||||
"Added I2C master with bus id: {:x} for devices: {:?}",
|
||||
adapter.bus(),
|
||||
devices
|
||||
"Added I2C master with bus id: {:x} for devices",
|
||||
adapter.adapter_no(),
|
||||
);
|
||||
|
||||
adapters.push(adapter);
|
||||
@ -447,6 +460,8 @@ impl<A: I2cAdapterTrait> I2cMap<A> {
|
||||
|
||||
pub fn transfer(&self, reqs: &mut [I2cReq]) -> Result<()> {
|
||||
let device = reqs[0].addr as usize;
|
||||
|
||||
// identify the device in the device_map
|
||||
let index = self.device_map[device];
|
||||
|
||||
// This can happen a lot while scanning the bus, don't print any errors.
|
||||
@ -454,92 +469,72 @@ impl<A: I2cAdapterTrait> I2cMap<A> {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
// get the corresponding adapter based on the device config.
|
||||
let adapter = &self.adapters[index as usize];
|
||||
|
||||
// Set device's address
|
||||
adapter.set_device_addr(device)?;
|
||||
|
||||
if adapter.is_smbus() {
|
||||
adapter.smbus_transfer(reqs)
|
||||
} else {
|
||||
adapter.i2c_transfer(reqs)
|
||||
}
|
||||
adapter.transfer(reqs)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub struct I2cMockAdapter {
|
||||
bus: u32,
|
||||
smbus: bool,
|
||||
result: Result<()>,
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DummyDevice {
|
||||
funcs_result: i32,
|
||||
rdwr_result: i32,
|
||||
smbus_result: i32,
|
||||
slave_result: i32,
|
||||
}
|
||||
|
||||
impl I2cAdapterTrait for I2cMockAdapter {
|
||||
fn new(bus: &str) -> Result<I2cMockAdapter> {
|
||||
Ok(I2cMockAdapter {
|
||||
bus: bus.parse::<u32>().map_err(|_| Error::new(EINVAL))?,
|
||||
smbus: false,
|
||||
result: Ok(()),
|
||||
})
|
||||
impl I2cDevice for DummyDevice {
|
||||
fn open(_device_path: String) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok(DummyDevice::default())
|
||||
}
|
||||
|
||||
fn bus(&self) -> u32 {
|
||||
self.bus
|
||||
fn funcs(&mut self, _func: u64) -> i32 {
|
||||
self.funcs_result
|
||||
}
|
||||
|
||||
fn is_smbus(&self) -> bool {
|
||||
self.smbus
|
||||
fn rdwr(&self, _data: &I2cRdwrIoctlData) -> i32 {
|
||||
self.rdwr_result
|
||||
}
|
||||
|
||||
fn set_device_addr(&self, _addr: usize) -> Result<()> {
|
||||
Ok(())
|
||||
fn smbus(&self, _data: &I2cSmbusIoctlData) -> i32 {
|
||||
self.smbus_result
|
||||
}
|
||||
|
||||
fn get_func(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
fn slave(&self, _addr: u64) -> i32 {
|
||||
self.slave_result
|
||||
}
|
||||
|
||||
fn do_i2c_transfer(&self, _data: &I2cRdwrIoctlData, _addr: u16) -> Result<()> {
|
||||
println!("In i2c-transfer");
|
||||
self.result
|
||||
}
|
||||
|
||||
fn do_smbus_transfer(&self, _data: &I2cSmbusIoctlData, _addr: u16) -> Result<()> {
|
||||
println!("In smbus-transfer");
|
||||
self.result
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_results(
|
||||
i2c_map: &mut I2cMap<I2cMockAdapter>,
|
||||
reqs: &mut Vec<I2cReq>,
|
||||
before: bool,
|
||||
after: bool,
|
||||
) {
|
||||
i2c_map.adapters[0].result = Ok(());
|
||||
assert_eq!(i2c_map.transfer(reqs).is_err(), before);
|
||||
i2c_map.adapters[0].result = Err(Error::new(EINVAL));
|
||||
assert_eq!(i2c_map.transfer(reqs).is_err(), after);
|
||||
|
||||
reqs.clear();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i2c_map_duplicate_device4() {
|
||||
assert!(I2cMap::<I2cMockAdapter>::new("1:4,2:32:21,5:4:23").is_err());
|
||||
assert!(AdapterConfig::try_from("1:4,2:32:21,5:4:23").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duplicated_adapter_no() {
|
||||
assert!(AdapterConfig::try_from("1:4,1:32:21,5:10:23").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i2c_map() {
|
||||
let i2c_map: I2cMap<I2cMockAdapter> = I2cMap::new("1:4,2:32:21,5:10:23").unwrap();
|
||||
let adapter_config = AdapterConfig::try_from("1:4,2:32:21,5:10:23").unwrap();
|
||||
let i2c_map: I2cMap<DummyDevice> = I2cMap::new(&adapter_config).unwrap();
|
||||
|
||||
assert_eq!(i2c_map.adapters.len(), 3);
|
||||
assert_eq!(i2c_map.adapters[0].bus, 1);
|
||||
assert_eq!(i2c_map.adapters[1].bus, 2);
|
||||
assert_eq!(i2c_map.adapters[2].bus, 5);
|
||||
assert_eq!(i2c_map.adapters[0].adapter_no, 1);
|
||||
assert_eq!(i2c_map.adapters[1].adapter_no, 2);
|
||||
assert_eq!(i2c_map.adapters[2].adapter_no, 5);
|
||||
|
||||
assert_eq!(i2c_map.device_map[4], 0);
|
||||
assert_eq!(i2c_map.device_map[32], 1);
|
||||
@ -550,22 +545,26 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_i2c_transfer() {
|
||||
let mut i2c_map: I2cMap<I2cMockAdapter> = I2cMap::new("1:3").unwrap();
|
||||
let adapter_config = AdapterConfig::try_from("1:3").unwrap();
|
||||
let mut i2c_map: I2cMap<DummyDevice> = I2cMap::new(&adapter_config).unwrap();
|
||||
|
||||
i2c_map.adapters[0].smbus = false;
|
||||
|
||||
let mut reqs: Vec<I2cReq> = vec![I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
len: 2,
|
||||
buf: [7, 4].to_vec(),
|
||||
buf: vec![7, 4],
|
||||
}];
|
||||
|
||||
assert_results(&mut i2c_map, &mut reqs, false, true);
|
||||
i2c_map.transfer(&mut *reqs).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_smbus_transfer() {
|
||||
let mut i2c_map: I2cMap<I2cMockAdapter> = I2cMap::new("1:3").unwrap();
|
||||
let adapter_config = AdapterConfig::try_from("1:3").unwrap();
|
||||
let mut i2c_map: I2cMap<DummyDevice> = I2cMap::new(&adapter_config).unwrap();
|
||||
|
||||
i2c_map.adapters[0].smbus = true;
|
||||
|
||||
let mut reqs: Vec<I2cReq> = vec![I2cReq {
|
||||
@ -576,28 +575,30 @@ pub mod tests {
|
||||
}];
|
||||
|
||||
// I2C_SMBUS_WRITE (I2C_SMBUS_BYTE_DATA) operation
|
||||
assert_results(&mut i2c_map, &mut reqs, false, true);
|
||||
i2c_map.transfer(&mut reqs).unwrap();
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) operation
|
||||
reqs.push(I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
len: 1,
|
||||
buf: [34].to_vec(),
|
||||
});
|
||||
reqs.push(I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 1,
|
||||
len: 2,
|
||||
buf: [3, 4].to_vec(),
|
||||
});
|
||||
|
||||
assert_results(&mut i2c_map, &mut reqs, false, true);
|
||||
let mut reqs = vec![
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
len: 1,
|
||||
buf: [34].to_vec(),
|
||||
},
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 1,
|
||||
len: 2,
|
||||
buf: [3, 4].to_vec(),
|
||||
},
|
||||
];
|
||||
i2c_map.transfer(&mut reqs).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_smbus_transfer_failure() {
|
||||
let mut i2c_map: I2cMap<I2cMockAdapter> = I2cMap::new("1:3").unwrap();
|
||||
let adapter_config = AdapterConfig::try_from("1:3").unwrap();
|
||||
let mut i2c_map: I2cMap<DummyDevice> = I2cMap::new(&adapter_config).unwrap();
|
||||
i2c_map.adapters[0].smbus = true;
|
||||
|
||||
let mut reqs: Vec<I2cReq> = vec![
|
||||
@ -617,57 +618,63 @@ pub mod tests {
|
||||
];
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
assert_results(&mut i2c_map, &mut reqs, true, true);
|
||||
// TODO: check the actual error once we have an error type defined.
|
||||
// TODO-continued: otherwise this test is unreliable because it might
|
||||
// fail for another reason than the expected one.
|
||||
assert!(i2c_map.transfer(&mut reqs).is_err());
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
reqs.push(I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
len: 1,
|
||||
buf: [34].to_vec(),
|
||||
});
|
||||
reqs.push(I2cReq {
|
||||
addr: 0x3,
|
||||
// Will cause failure
|
||||
flags: 0,
|
||||
len: 2,
|
||||
buf: [3, 4].to_vec(),
|
||||
});
|
||||
|
||||
assert_results(&mut i2c_map, &mut reqs, true, true);
|
||||
let mut reqs = vec![
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
len: 1,
|
||||
buf: [34].to_vec(),
|
||||
},
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
// Will cause failure
|
||||
flags: 0,
|
||||
len: 2,
|
||||
buf: [3, 4].to_vec(),
|
||||
},
|
||||
];
|
||||
assert!(i2c_map.transfer(&mut reqs).is_err());
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
reqs.push(I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
// Will cause failure
|
||||
len: 2,
|
||||
buf: [3, 4].to_vec(),
|
||||
});
|
||||
reqs.push(I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 1,
|
||||
len: 2,
|
||||
buf: [3, 4].to_vec(),
|
||||
});
|
||||
|
||||
assert_results(&mut i2c_map, &mut reqs, true, true);
|
||||
let mut reqs = vec![
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
// Will cause failure
|
||||
len: 2,
|
||||
buf: [3, 4].to_vec(),
|
||||
},
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 1,
|
||||
len: 2,
|
||||
buf: [3, 4].to_vec(),
|
||||
},
|
||||
];
|
||||
assert!(i2c_map.transfer(&mut reqs).is_err());
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
reqs.push(I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
len: 1,
|
||||
buf: [34].to_vec(),
|
||||
});
|
||||
reqs.push(I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 1,
|
||||
// Will cause failure
|
||||
len: 3,
|
||||
buf: [3, 4, 5].to_vec(),
|
||||
});
|
||||
|
||||
assert_results(&mut i2c_map, &mut reqs, true, true);
|
||||
let mut reqs = vec![
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 0,
|
||||
len: 1,
|
||||
buf: [34].to_vec(),
|
||||
},
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
flags: 1,
|
||||
// Will cause failure
|
||||
len: 3,
|
||||
buf: [3, 4, 5].to_vec(),
|
||||
},
|
||||
];
|
||||
assert!(i2c_map.transfer(&mut reqs).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,75 +8,150 @@
|
||||
mod i2c;
|
||||
mod vhu_i2c;
|
||||
|
||||
use clap::{load_yaml, App, ArgMatches};
|
||||
use i2c::{I2cAdapter, I2cAdapterTrait, I2cMap};
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread::spawn;
|
||||
|
||||
use clap::{load_yaml, App, ArgMatches};
|
||||
use vhost::{vhost_user, vhost_user::Listener};
|
||||
use vhost_user_backend::VhostUserDaemon;
|
||||
use vhu_i2c::VhostUserI2cBackend;
|
||||
use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
|
||||
|
||||
fn start_daemon<T: I2cAdapterTrait>(
|
||||
backend: Arc<RwLock<VhostUserI2cBackend<T>>>,
|
||||
listener: Listener,
|
||||
) -> bool {
|
||||
let mut daemon = VhostUserDaemon::new(
|
||||
String::from("vhost-device-i2c-backend"),
|
||||
backend.clone(),
|
||||
GuestMemoryAtomic::new(GuestMemoryMmap::new()),
|
||||
)
|
||||
.unwrap();
|
||||
use i2c::{I2cDevice, I2cMap, PhysDevice, MAX_I2C_VDEV};
|
||||
use vhu_i2c::VhostUserI2cBackend;
|
||||
|
||||
daemon.start(listener).unwrap();
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct DeviceConfig {
|
||||
adapter_no: u32,
|
||||
addr: Vec<u16>,
|
||||
}
|
||||
|
||||
match daemon.wait() {
|
||||
Ok(()) => {
|
||||
println!("Stopping cleanly.");
|
||||
}
|
||||
Err(vhost_user_backend::Error::HandleRequest(vhost_user::Error::PartialMessage)) => {
|
||||
println!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug.");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error running daemon: {:?}", e);
|
||||
impl DeviceConfig {
|
||||
fn new(adapter_no: u32) -> Self {
|
||||
DeviceConfig {
|
||||
adapter_no,
|
||||
addr: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// No matter the result, we need to shut down the worker thread.
|
||||
backend
|
||||
.read()
|
||||
.unwrap()
|
||||
.exit_event
|
||||
.write(1)
|
||||
.expect("Shutting down worker thread");
|
||||
fn push(&mut self, addr: u16) -> std::result::Result<(), String> {
|
||||
if addr as usize > MAX_I2C_VDEV {
|
||||
return Err(format!("Invalid address: {} (> maximum allowed)", addr));
|
||||
}
|
||||
|
||||
false
|
||||
if self.addr.contains(&addr) {
|
||||
return Err(format!("Address already in use: {}", addr));
|
||||
}
|
||||
|
||||
self.addr.push(addr);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn start_backend<T: I2cAdapterTrait>(
|
||||
cmd_args: ArgMatches,
|
||||
start_daemon: fn(Arc<RwLock<VhostUserI2cBackend<T>>>, Listener) -> bool,
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct AdapterConfig {
|
||||
inner: Vec<DeviceConfig>,
|
||||
}
|
||||
|
||||
impl AdapterConfig {
|
||||
fn new() -> Self {
|
||||
Self { inner: Vec::new() }
|
||||
}
|
||||
|
||||
fn contains_adapter_no(&self, adapter_no: u32) -> bool {
|
||||
self.inner.iter().any(|elem| elem.adapter_no == adapter_no)
|
||||
}
|
||||
|
||||
fn contains_addr(&self, addr: u16) -> bool {
|
||||
self.inner.iter().any(|elem| elem.addr.contains(&addr))
|
||||
}
|
||||
|
||||
fn push(&mut self, device: DeviceConfig) -> std::result::Result<(), String> {
|
||||
if self.contains_adapter_no(device.adapter_no) {
|
||||
return Err("Duplicated adapter number".to_string());
|
||||
}
|
||||
|
||||
for addr in device.addr.iter() {
|
||||
if self.contains_addr(*addr) {
|
||||
return Err(format!("Address already in use: {}", addr));
|
||||
}
|
||||
}
|
||||
|
||||
self.inner.push(device);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for AdapterConfig {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(list: &str) -> Result<Self, Self::Error> {
|
||||
let busses: Vec<&str> = list.split(',').collect();
|
||||
let mut devices = AdapterConfig::new();
|
||||
|
||||
for businfo in busses.iter() {
|
||||
let list: Vec<&str> = businfo.split(':').collect();
|
||||
let bus_addr = list[0].parse::<u32>().map_err(|_| "Invalid bus address")?;
|
||||
let mut adapter = DeviceConfig::new(bus_addr);
|
||||
|
||||
for device_str in list[1..].iter() {
|
||||
let addr = device_str
|
||||
.parse::<u16>()
|
||||
.map_err(|_| "Invalid device addr: {}")?;
|
||||
adapter.push(addr)?;
|
||||
}
|
||||
|
||||
devices.push(adapter)?;
|
||||
}
|
||||
Ok(devices)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct I2cConfiguration {
|
||||
socket_path: String,
|
||||
socket_count: usize,
|
||||
devices: AdapterConfig,
|
||||
}
|
||||
|
||||
impl TryFrom<ArgMatches> for I2cConfiguration {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(cmd_args: ArgMatches) -> Result<Self, Self::Error> {
|
||||
let socket_path = cmd_args
|
||||
.value_of("socket_path")
|
||||
.ok_or("Invalid socket path")?
|
||||
.to_string();
|
||||
|
||||
let socket_count = cmd_args
|
||||
.value_of("socket_count")
|
||||
.unwrap_or("1")
|
||||
.parse::<usize>()
|
||||
.map_err(|_| "Invalid socket_count")?;
|
||||
|
||||
let list = cmd_args.value_of("devices").ok_or("Invalid devices list")?;
|
||||
let devices = AdapterConfig::try_from(list)?;
|
||||
Ok(I2cConfiguration {
|
||||
socket_path,
|
||||
socket_count,
|
||||
devices,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn start_backend<D: 'static + I2cDevice + Send + Sync>(
|
||||
config: I2cConfiguration,
|
||||
) -> Result<(), String> {
|
||||
// The same i2c_map structure instance is shared between all the guests
|
||||
let i2c_map = Arc::new(
|
||||
I2cMap::<D>::new(&config.devices)
|
||||
.map_err(|e| format!("Failed to create i2c_map ({})", e))?,
|
||||
);
|
||||
|
||||
let mut handles = Vec::new();
|
||||
|
||||
let path = cmd_args
|
||||
.value_of("socket_path")
|
||||
.ok_or("Invalid socket path")?;
|
||||
|
||||
let count = cmd_args
|
||||
.value_of("socket_count")
|
||||
.unwrap_or("1")
|
||||
.parse::<u32>()
|
||||
.map_err(|_| "Invalid socket_count")?;
|
||||
|
||||
let list = cmd_args.value_of("devices").ok_or("Invalid devices list")?;
|
||||
|
||||
// The same i2c_map structure instance is shared between all the guests
|
||||
let i2c_map =
|
||||
Arc::new(I2cMap::<T>::new(list).map_err(|e| format!("Failed to create i2c_map ({})", e))?);
|
||||
|
||||
for i in 0..count {
|
||||
let socket = path.to_owned() + &i.to_string();
|
||||
for i in 0..config.socket_count {
|
||||
let socket = config.socket_path.to_owned() + &i.to_string();
|
||||
let i2c_map = i2c_map.clone();
|
||||
|
||||
let handle = spawn(move || loop {
|
||||
@ -85,9 +160,36 @@ fn start_backend<T: I2cAdapterTrait>(
|
||||
));
|
||||
let listener = Listener::new(socket.clone(), true).unwrap();
|
||||
|
||||
if start_daemon(backend, listener) {
|
||||
break;
|
||||
let mut daemon = VhostUserDaemon::new(
|
||||
String::from("vhost-device-i2c-backend"),
|
||||
backend.clone(),
|
||||
GuestMemoryAtomic::new(GuestMemoryMmap::new()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
daemon.start(listener).unwrap();
|
||||
|
||||
match daemon.wait() {
|
||||
Ok(()) => {
|
||||
println!("Stopping cleanly.");
|
||||
}
|
||||
Err(vhost_user_backend::Error::HandleRequest(
|
||||
vhost_user::Error::PartialMessage,
|
||||
)) => {
|
||||
println!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug.");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error running daemon: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// No matter the result, we need to shut down the worker thread.
|
||||
backend
|
||||
.read()
|
||||
.unwrap()
|
||||
.exit_event
|
||||
.write(1)
|
||||
.expect("Shutting down worker thread");
|
||||
});
|
||||
|
||||
handles.push(handle);
|
||||
@ -104,13 +206,25 @@ fn main() -> Result<(), String> {
|
||||
let yaml = load_yaml!("cli.yaml");
|
||||
let cmd_args = App::from(yaml).get_matches();
|
||||
|
||||
start_backend::<I2cAdapter>(cmd_args, start_daemon)
|
||||
let config = I2cConfiguration::try_from(cmd_args).unwrap();
|
||||
start_backend::<PhysDevice>(config)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use i2c::tests::I2cMockAdapter;
|
||||
|
||||
impl DeviceConfig {
|
||||
pub fn new_with(adapter_no: u32, addr: Vec<u16>) -> Self {
|
||||
DeviceConfig { adapter_no, addr }
|
||||
}
|
||||
}
|
||||
|
||||
impl AdapterConfig {
|
||||
pub fn new_with(devices: Vec<DeviceConfig>) -> Self {
|
||||
AdapterConfig { inner: devices }
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cmd_args(name: &str, devices: &str, count: u32) -> ArgMatches {
|
||||
let yaml = load_yaml!("cli.yaml");
|
||||
@ -133,34 +247,34 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn mock_start_daemon<T: I2cAdapterTrait>(
|
||||
_backend: Arc<RwLock<VhostUserI2cBackend<T>>>,
|
||||
_listener: Listener,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backend_single() {
|
||||
let cmd_args = get_cmd_args("vi2c.sock_single", "1:4,2:32:21,5:5:23", 0);
|
||||
assert!(start_backend::<I2cMockAdapter>(cmd_args, mock_start_daemon).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backend_multiple() {
|
||||
let cmd_args = get_cmd_args("vi2c.sock", "1:4,2:32:21,5:5:23", 5);
|
||||
assert!(start_backend::<I2cMockAdapter>(cmd_args, mock_start_daemon).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backend_failure() {
|
||||
fn test_parse_failure() {
|
||||
let cmd_args = get_cmd_args("vi2c.sock_failure", "1:4d", 5);
|
||||
assert!(start_backend::<I2cMockAdapter>(cmd_args, mock_start_daemon).is_err());
|
||||
// TODO: Check against the actual error instead of `is_err`.
|
||||
assert!(I2cConfiguration::try_from(cmd_args).is_err());
|
||||
|
||||
let cmd_args = get_cmd_args("vi2c.sock_duplicate", "1:4,2:32:21,5:4:23", 5);
|
||||
// TODO: Check against the actual error instead of `is_err`.
|
||||
assert!(I2cConfiguration::try_from(cmd_args).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backend_failure_duplicate_device4() {
|
||||
let cmd_args = get_cmd_args("vi2c.sock_duplicate", "1:4,2:32:21,5:4:23", 5);
|
||||
assert!(start_backend::<I2cMockAdapter>(cmd_args, mock_start_daemon).is_err());
|
||||
fn test_parse_successful() {
|
||||
let cmd_args = get_cmd_args("vi2c.sock_single", "1:4,2:32:21,5:5:23", 5);
|
||||
let config = I2cConfiguration::try_from(cmd_args).unwrap();
|
||||
|
||||
let expected_devices = AdapterConfig::new_with(vec![
|
||||
DeviceConfig::new_with(1, vec![4]),
|
||||
DeviceConfig::new_with(2, vec![32, 21]),
|
||||
DeviceConfig::new_with(5, vec![5, 23]),
|
||||
]);
|
||||
|
||||
let expected_config = I2cConfiguration {
|
||||
socket_count: 5,
|
||||
socket_path: String::from("vi2c.sock_single"),
|
||||
devices: expected_devices,
|
||||
};
|
||||
|
||||
assert_eq!(config, expected_config);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::i2c::*;
|
||||
use std::mem::size_of;
|
||||
use std::sync::Arc;
|
||||
use std::{convert, error, fmt, io};
|
||||
|
||||
use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
|
||||
use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT};
|
||||
use virtio_bindings::bindings::virtio_net::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1};
|
||||
@ -18,6 +18,8 @@ use virtio_bindings::bindings::virtio_ring::{
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryMmap, Le16, Le32};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
use crate::i2c::*;
|
||||
|
||||
const QUEUE_SIZE: usize = 1024;
|
||||
const NUM_QUEUES: usize = 1;
|
||||
|
||||
@ -84,15 +86,15 @@ struct VirtioI2cInHdr {
|
||||
}
|
||||
unsafe impl ByteValued for VirtioI2cInHdr {}
|
||||
|
||||
pub struct VhostUserI2cBackend<A: I2cAdapterTrait> {
|
||||
i2c_map: Arc<I2cMap<A>>,
|
||||
pub struct VhostUserI2cBackend<D: I2cDevice> {
|
||||
i2c_map: Arc<I2cMap<D>>,
|
||||
event_idx: bool,
|
||||
mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
pub exit_event: EventFd,
|
||||
}
|
||||
|
||||
impl<A: I2cAdapterTrait> VhostUserI2cBackend<A> {
|
||||
pub fn new(i2c_map: Arc<I2cMap<A>>) -> Result<Self> {
|
||||
impl<D: I2cDevice> VhostUserI2cBackend<D> {
|
||||
pub fn new(i2c_map: Arc<I2cMap<D>>) -> Result<Self> {
|
||||
Ok(VhostUserI2cBackend {
|
||||
i2c_map,
|
||||
event_idx: false,
|
||||
@ -220,7 +222,9 @@ impl<A: I2cAdapterTrait> VhostUserI2cBackend<A> {
|
||||
}
|
||||
|
||||
/// VhostUserBackendMut trait methods
|
||||
impl<A: I2cAdapterTrait> VhostUserBackendMut<VringRwLock, ()> for VhostUserI2cBackend<A> {
|
||||
impl<D: 'static + I2cDevice + Sync + Send> VhostUserBackendMut<VringRwLock, ()>
|
||||
for VhostUserI2cBackend<D>
|
||||
{
|
||||
fn num_queues(&self) -> usize {
|
||||
NUM_QUEUES
|
||||
}
|
||||
@ -303,11 +307,14 @@ impl<A: I2cAdapterTrait> VhostUserBackendMut<VringRwLock, ()> for VhostUserI2cBa
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::i2c::tests::I2cMockAdapter;
|
||||
use crate::i2c::tests::DummyDevice;
|
||||
use crate::AdapterConfig;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn verify_backend() {
|
||||
let i2c_map: I2cMap<I2cMockAdapter> = I2cMap::new("1:4,2:32:21,5:10:23").unwrap();
|
||||
let device_config = AdapterConfig::try_from("1:4,2:32:21,5:10:23").unwrap();
|
||||
let i2c_map: I2cMap<DummyDevice> = I2cMap::new(&device_config).unwrap();
|
||||
let mut backend = VhostUserI2cBackend::new(Arc::new(i2c_map)).unwrap();
|
||||
|
||||
assert_eq!(backend.num_queues(), NUM_QUEUES);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user