Skip to content
Merged
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
path: target/debug/framework_tool

- name: Build Linux tool (Release)
run: cargo build -p framework_tool --release
run: cargo build -p framework_tool --release --features nvidia

- name: Upload Linux App
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -124,7 +124,7 @@ jobs:
- name: Build Windows tool
run: |
cargo build -p framework_tool
cargo build -p framework_tool --release
cargo build -p framework_tool --release --features nvidia

- name: Check if Windows tool can start
run: cargo run -- --help --release
Expand Down
123 changes: 121 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions framework_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ readonly = [ ]
rusb = ["dep:rusb"]
hidapi = ["dep:hidapi"]
uefi = [ "lazy_static/spin_no_std" ]
nvidia = ["dep:nvml-wrapper"]

[build-dependencies]
built = { version = "0.5", features = ["chrono", "git2"] }
Expand Down Expand Up @@ -52,6 +53,7 @@ clap-num = { version = "1.2.0" }
clap-verbosity-flag = { version = "2.2.1" }
windows-version = "0.1.4"
winreg = "0.55.0"
nvml-wrapper = { version = "0.11.0", optional = true }

[target.'cfg(unix)'.dependencies]
libc = "0.2.155"
Expand All @@ -62,6 +64,7 @@ env_logger = "0.11"
clap = { version = "4.5", features = ["derive", "cargo"] }
clap-num = { version = "1.2.0" }
clap-verbosity-flag = { version = "2.2.1" }
nvml-wrapper = { version = "0.11.0", optional = true }

[target.'cfg(windows)'.dependencies.windows]
version = "0.59.0"
Expand Down
60 changes: 60 additions & 0 deletions framework_lib/src/chromium_ec/i2c_passthrough.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,66 @@ pub fn i2c_read(
})
}

/// I2C read with 16-bit addressing (for larger EEPROMs like 24C32+)
/// Always sends a 2-byte address, even for addr=0
pub fn i2c_read_16bit_addr(
ec: &CrosEc,
i2c_port: u8,
i2c_addr: u16,
addr: u16,
len: u16,
) -> EcResult<EcI2cPassthruResponse> {
trace!(
"i2c_read_16bit_addr(i2c_port: 0x{:X}, i2c_addr: 0x{:X}, addr: 0x{:X}, len: 0x{:X})",
i2c_port,
i2c_addr,
addr,
len
);
if usize::from(len) > MAX_I2C_CHUNK {
return EcResult::Err(EcError::DeviceError(format!(
"i2c_read too long. Must be <128, is: {}",
len
)));
}
// Always use 16-bit addressing (big-endian for EEPROM)
let addr_bytes = u16::to_be_bytes(addr).to_vec();
let messages = vec![
EcParamsI2cPassthruMsg {
addr_and_flags: i2c_addr,
transfer_len: addr_bytes.len() as u16,
},
EcParamsI2cPassthruMsg {
addr_and_flags: i2c_addr + I2C_READ_FLAG,
transfer_len: len, // How much to read
},
];
let msgs_len = size_of::<EcParamsI2cPassthruMsg>() * messages.len();
let msgs_buffer: &[u8] = unsafe { util::any_vec_as_u8_slice(&messages) };

let params = EcParamsI2cPassthru {
port: i2c_port,
messages: messages.len() as u8,
msg: [], // Messages are copied right after this struct
};
let params_len = size_of::<EcParamsI2cPassthru>();
let params_buffer: &[u8] = unsafe { util::any_as_u8_slice(&params) };

let mut buffer: Vec<u8> = vec![0; params_len + msgs_len + addr_bytes.len()];
buffer[0..params_len].copy_from_slice(params_buffer);
buffer[params_len..params_len + msgs_len].copy_from_slice(msgs_buffer);
buffer[params_len + msgs_len..].copy_from_slice(&addr_bytes);

let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?;
let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) };
let res_data = &data[size_of::<_EcI2cPassthruResponse>()..];
debug_assert!(res.messages as usize == messages.len() || res.messages == 0);
Ok(EcI2cPassthruResponse {
i2c_status: res.i2c_status,
data: res_data.to_vec(),
})
}

/* Write address and bytes in a single I2C transfer */
pub fn i2c_write_block(
ec: &CrosEc,
Expand Down
5 changes: 3 additions & 2 deletions framework_lib/src/chromium_ec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1295,7 +1295,8 @@ impl CrosEc {
let remaining = len - data.len() as u16;
let chunk_len = std::cmp::min(i2c_passthrough::MAX_I2C_CHUNK, remaining.into());
let offset = addr + data.len() as u16;
let i2c_response = i2c_passthrough::i2c_read(
// Use 16-bit addressing for GPU EEPROM (required for larger EEPROMs)
let i2c_response = i2c_passthrough::i2c_read_16bit_addr(
self,
eeprom_port,
eeprom_addr,
Expand All @@ -1311,7 +1312,7 @@ impl CrosEc {
data.extend(i2c_response.data);
}

Ok(data)
Ok(data[..(len.into())].to_vec())
}

pub fn write_ec_gpu_chunk(&self, offset: u16, data: &[u8]) -> EcResult<()> {
Expand Down
5 changes: 5 additions & 0 deletions framework_lib/src/commandline/clap_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ struct ClapCli {
/// File to dump the gpu EEPROM to
#[arg(long)]
dump_gpu_descriptor_file: Option<std::path::PathBuf>,

/// Show NVIDIA GPU information (Framework 16 only)
#[arg(long)]
nvidia: bool,
}

/// Parse a list of commandline arguments and return the struct
Expand Down Expand Up @@ -484,6 +488,7 @@ pub fn parse(args: &[String]) -> Cli {
dump_gpu_descriptor_file: args
.dump_gpu_descriptor_file
.map(|x| x.into_os_string().into_string().unwrap()),
nvidia: args.nvidia,
raw_command: vec![],
}
}
Loading