diff --git a/src/i2c/src/i2c.rs b/src/i2c/src/i2c.rs index e6a4468..fbac740 100644 --- a/src/i2c/src/i2c.rs +++ b/src/i2c/src/i2c.rs @@ -297,7 +297,7 @@ pub struct I2cReq { /// functionality without the need of a physical device. pub trait I2cDevice { // Open the device specified by the adapter number. - fn open(adapter_no: u32) -> Result + fn open(device_path: &str, adapter_no: u32) -> Result where Self: Sized; @@ -319,14 +319,14 @@ pub trait I2cDevice { /// A physical I2C device. This structure can only be initialized on hosts /// where `/dev/i2c-XX` is available. +#[derive(Debug)] pub struct PhysDevice { file: File, adapter_no: u32, } impl I2cDevice for PhysDevice { - fn open(adapter_no: u32) -> Result { - let device_path = format!("/dev/i2c-{}", adapter_no); + fn open(device_path: &str, adapter_no: u32) -> Result { Ok(PhysDevice { file: OpenOptions::new() .read(true) @@ -418,6 +418,7 @@ impl I2cDevice for PhysDevice { } } +#[derive(Debug)] pub struct I2cAdapter { device: D, adapter_no: u32, @@ -512,9 +513,13 @@ impl I2cMap { { let mut device_map = HashMap::new(); let mut adapters: Vec> = Vec::new(); + let prefix = "/dev/i2c-"; for (i, device_cfg) in device_config.inner.iter().enumerate() { - let device = D::open(device_cfg.adapter_no)?; + let device = D::open( + &format!("{}{}", prefix, device_cfg.adapter_no), + device_cfg.adapter_no, + )?; let adapter = I2cAdapter::new(device)?; // Check that all addresses corresponding to the adapter are valid. @@ -561,6 +566,7 @@ impl I2cMap { pub mod tests { use super::*; use std::convert::TryFrom; + use vmm_sys_util::tempfile::TempFile; #[derive(Debug)] pub struct DummyDevice { @@ -584,7 +590,7 @@ pub mod tests { } impl I2cDevice for DummyDevice { - fn open(adapter_no: u32) -> Result + fn open(_path: &str, adapter_no: u32) -> Result where Self: Sized, { @@ -630,6 +636,15 @@ pub mod tests { }; let adapter = I2cAdapter::new(i2c_device).unwrap(); assert_eq!(adapter.smbus, false); + + let i2c_device = DummyDevice { + funcs_result: Ok(0), + ..Default::default() + }; + assert_eq!( + I2cAdapter::new(i2c_device).unwrap_err(), + Error::AdapterFunctionInvalid(0) + ); } #[test] @@ -701,12 +716,59 @@ pub mod tests { i2c_map.transfer(&mut reqs).unwrap(); } + #[test] + fn test_transfer_failure() { + let adapter_config = AdapterConfig::try_from("1:3").unwrap(); + let mut i2c_map: I2cMap = I2cMap::new(&adapter_config).unwrap(); + + i2c_map.adapters[0].smbus = false; + + let mut reqs: Vec = vec![I2cReq { + // Will cause failure + addr: 0x4, + flags: 0, + len: 2, + buf: vec![7, 4], + }]; + + assert_eq!( + i2c_map.transfer(&mut reqs).unwrap_err(), + Error::ClientAddressInvalid + ); + } + #[test] fn test_smbus_transfer_failure() { let adapter_config = AdapterConfig::try_from("1:3").unwrap(); let mut i2c_map: I2cMap = I2cMap::new(&adapter_config).unwrap(); i2c_map.adapters[0].smbus = true; + // I2C_SMBUS_READ (Invalid size) failure operation + let mut reqs: Vec = vec![I2cReq { + addr: 0x3, + flags: I2C_M_RD, + // Will cause failure + len: 2, + buf: [34].to_vec(), + }]; + assert_eq!( + i2c_map.transfer(&mut reqs).unwrap_err(), + Error::MessageLengthInvalid("read", 2) + ); + + // I2C_SMBUS_WRITE (Invalid size) failure operation + let mut reqs: Vec = vec![I2cReq { + addr: 0x3, + flags: 0, + // Will cause failure + len: 4, + buf: [34].to_vec(), + }]; + assert_eq!( + i2c_map.transfer(&mut reqs).unwrap_err(), + Error::MessageLengthInvalid("write", 4) + ); + // I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation let mut reqs: Vec = vec![ I2cReq { @@ -790,5 +852,83 @@ pub mod tests { i2c_map.transfer(&mut reqs).unwrap_err(), Error::SMBusTransferInvalid(2, 1, 3) ); + + // I2C_SMBUS_READ (Invalid request count) failure operation + let mut reqs = vec![ + I2cReq { + addr: 0x3, + flags: 0, + len: 1, + buf: [34].to_vec(), + }, + I2cReq { + addr: 0x3, + flags: I2C_M_RD, + len: 2, + buf: [3, 4].to_vec(), + }, + // Will cause failure + I2cReq { + addr: 0, + flags: 0, + len: 0, + buf: [0].to_vec(), + }, + ]; + assert_eq!( + i2c_map.transfer(&mut reqs).unwrap_err(), + Error::SMBusTransferInvalid(3, 1, 2) + ); + } + + #[test] + fn test_phys_device_failure() { + // Open failure + assert_eq!( + PhysDevice::open("/dev/i2c-invalid-path", 0).unwrap_err(), + Error::DeviceOpenFailed(0) + ); + + let file = TempFile::new().unwrap(); + let mut dev = PhysDevice::open(file.as_path().to_str().unwrap(), 1).unwrap(); + + // Match adapter number + assert_eq!(dev.adapter_no(), 1); + + // funcs failure + assert_eq!( + dev.funcs().unwrap_err(), + Error::IoctlFailure("funcs", IoError::last()) + ); + + // rdwr failure + let mut reqs = [I2cReq { + addr: 0x4, + flags: 0, + len: 2, + buf: vec![7, 4], + }]; + assert_eq!( + dev.rdwr(&mut reqs).unwrap_err(), + Error::IoctlFailure("rdwr", IoError::last()) + ); + + // smbus failure + let mut data = SmbusMsg { + read_write: 0, + command: 0, + size: 0, + data: None, + }; + assert_eq!( + dev.smbus(&mut data).unwrap_err(), + Error::IoctlFailure("smbus", IoError::last()) + ); + + // slave failure + assert_eq!( + dev.slave(0).unwrap_err(), + Error::IoctlFailure("slave", IoError::last()) + ); } } diff --git a/src/i2c/src/main.rs b/src/i2c/src/main.rs index 9f9f2c4..d565158 100644 --- a/src/i2c/src/main.rs +++ b/src/i2c/src/main.rs @@ -256,39 +256,62 @@ mod tests { } } - fn get_cmd_args(name: &str, devices: &str, count: Option) -> ArgMatches { - let mut args = vec!["prog", "-s", name, "-l", devices]; + fn get_cmd_args(name: Option<&str>, devices: &str, count: Option<&str>) -> ArgMatches { + let mut args = vec!["prog", "-l", devices]; let yaml = load_yaml!("cli.yaml"); let app = App::from(yaml); - let socket_count_str; + + if let Some(name) = name { + args.extend_from_slice(&["-s", &name]); + } if let Some(count) = count { - socket_count_str = count.to_string(); - args.extend_from_slice(&["-c", &socket_count_str]); + args.extend_from_slice(&["-c", &count]); } app.try_get_matches_from(args).unwrap() } #[test] fn test_parse_failure() { - let socket_name = "vi2c.sock"; + let socket_name = Some("vi2c.sock"); - // Invalid device list - let cmd_args = get_cmd_args(socket_name, "1:4d", Some(5)); + // Invalid bus_addr + let cmd_args = get_cmd_args(socket_name, "1:4,3d:5", Some("5")); assert_eq!( I2cConfiguration::try_from(cmd_args).unwrap_err(), - Error::ParseFailure("4d".parse::().unwrap_err()) + Error::ParseFailure("3d".parse::().unwrap_err()) + ); + + // Invalid client address + let cmd_args = get_cmd_args(socket_name, "1:4d", Some("5")); + assert_eq!( + I2cConfiguration::try_from(cmd_args).unwrap_err(), + Error::ParseFailure("4d".parse::().unwrap_err()) + ); + + // Invalid socket path + let cmd_args = get_cmd_args(None, "1:4d", Some("5")); + assert_eq!( + I2cConfiguration::try_from(cmd_args).unwrap_err(), + Error::SocketPathInvalid ); // Invalid socket count - let cmd_args = get_cmd_args(socket_name, "1:4", Some(0)); + let cmd_args = get_cmd_args(socket_name, "1:4", Some("1d")); + assert_eq!( + I2cConfiguration::try_from(cmd_args).unwrap_err(), + Error::ParseFailure("1d".parse::().unwrap_err()) + ); + + // Zero socket count + let cmd_args = get_cmd_args(socket_name, "1:4", Some("0")); assert_eq!( I2cConfiguration::try_from(cmd_args).unwrap_err(), Error::SocketCountInvalid(0) ); // Duplicate client address: 4 - let cmd_args = get_cmd_args(socket_name, "1:4,2:32:21,5:4:23", Some(5)); + let cmd_args = get_cmd_args(socket_name, "1:4,2:32:21,5:4:23", Some("5")); assert_eq!( I2cConfiguration::try_from(cmd_args).unwrap_err(), Error::ClientAddressDuplicate(4) @@ -297,14 +320,14 @@ mod tests { #[test] fn test_parse_successful() { - let socket_name = "vi2c.sock"; + let socket_name = Some("vi2c.sock"); // Missing socket count, default (1) should be used. let cmd_args = get_cmd_args(socket_name, "1:4,2:32:21,5:5:23", None); let config = I2cConfiguration::try_from(cmd_args).unwrap(); assert_eq!(config.socket_count, 1); - let cmd_args = get_cmd_args(socket_name, "1:4,2:32:21,5:5:23", Some(5)); + let cmd_args = get_cmd_args(socket_name, "1:4,2:32:21,5:5:23", Some("5")); let config = I2cConfiguration::try_from(cmd_args).unwrap(); let expected_devices = AdapterConfig::new_with(vec![ @@ -315,7 +338,7 @@ mod tests { let expected_config = I2cConfiguration { socket_count: 5, - socket_path: String::from(socket_name), + socket_path: String::from(socket_name.unwrap()), devices: expected_devices, };