Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions examples/dump_eeprom_page.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT

use futures::stream::TryStreamExt;

fn main() {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_io()
.build()
.unwrap();
let iface_name = std::env::args().nth(1);
rt.block_on(get_eeprom(iface_name.as_deref()));
}

async fn get_eeprom(iface_name: Option<&str>) {
let (connection, mut handle, _) = ethtool::new_connection().unwrap();
tokio::spawn(connection);

let mut eeprom_handle = handle
.eeprom()
.get(iface_name, 0, 1, 0, 0, 0x50)
.execute()
.await;

let mut msgs = Vec::new();
while let Some(msg) = eeprom_handle.try_next().await.unwrap() {
msgs.push(msg);
}
assert!(!msgs.is_empty());
for msg in msgs {
println!("{:?}", msg);
}
}
114 changes: 114 additions & 0 deletions src/eeprom/attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: MIT

use anyhow::Context;
use byteorder::{ByteOrder, NativeEndian};
use netlink_packet_utils::{
nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED},
DecodeError, Emitable, Parseable,
};

use crate::{EthtoolAttr, EthtoolHeader};


const ETHTOOL_A_MODULE_EEPROM_HEADER: u16 = 1;
const ETHTOOL_A_MODULE_EEPROM_OFFSET: u16 = 2;
const ETHTOOL_A_MODULE_EEPROM_LENGTH: u16 = 3;
const ETHTOOL_A_MODULE_EEPROM_PAGE: u16 = 4;
const ETHTOOL_A_MODULE_EEPROM_BANK: u16 = 5;
const ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS: u16 = 6;
const ETHTOOL_A_MODULE_EEPROM_DATA: u16 = 7;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum EthtoolModuleEEPROMAttr {
Header(Vec<EthtoolHeader>),
Offset(u32),
Length(u32),
Page(u8),
Bank(u8),
I2CAddress(u8),
Data(Vec<u8>),
Other(DefaultNla),
}

impl Nla for EthtoolModuleEEPROMAttr {
fn value_len(&self) -> usize {
match self {
Self::Header(hdrs) => hdrs.as_slice().buffer_len(),
Self::Data(data) => data.len(),
Self::Page(_)
| Self::Bank(_)
| Self::I2CAddress(_) => 1,
Self::Offset(_)
| Self::Length(_) => 4,
Self::Other(attr) => attr.value_len(),
}
}

fn kind(&self) -> u16 {
match self {
Self::Header(_) => ETHTOOL_A_MODULE_EEPROM_HEADER | NLA_F_NESTED,
Self::Offset(_) => ETHTOOL_A_MODULE_EEPROM_OFFSET,
Self::Length(_) => ETHTOOL_A_MODULE_EEPROM_LENGTH,
Self::Page(_) => ETHTOOL_A_MODULE_EEPROM_PAGE,
Self::Bank(_) => ETHTOOL_A_MODULE_EEPROM_BANK,
Self::I2CAddress(_) => ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS,
Self::Data(_) => ETHTOOL_A_MODULE_EEPROM_DATA,
Self::Other(attr) => attr.kind(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
Self::Header(ref nlas) => nlas.as_slice().emit(buffer),
Self::Data(d) => buffer.copy_from_slice(d.as_slice()),
Self::Page(d)
| Self::Bank(d)
| Self::I2CAddress(d) => buffer[0] = *d,
Self::Offset(d)
| Self::Length(d) => NativeEndian::write_u32(buffer, *d),
Self::Other(ref attr) => attr.emit(buffer),
}
}
}

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
for EthtoolModuleEEPROMAttr
{
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
ETHTOOL_A_MODULE_EEPROM_HEADER => {
let mut nlas = Vec::new();
let error_msg = "failed to parse module eeprom header attributes";
for nla in NlasIterator::new(payload) {
let nla = &nla.context(error_msg)?;
let parsed =
EthtoolHeader::parse(nla).context(error_msg)?;
nlas.push(parsed);
}
Self::Header(nlas)
}
ETHTOOL_A_MODULE_EEPROM_DATA => Self::Data(
Vec::from(payload),
),
kind => Self::Other(
DefaultNla::parse(buf)
.context(format!("invalid ethtool module eeprom NLA kind {kind}"))?,
),
})
}
}

pub(crate) fn parse_module_eeprom_nlas(
buffer: &[u8],
) -> Result<Vec<EthtoolAttr>, DecodeError> {
let mut nlas = Vec::new();
for nla in NlasIterator::new(buffer) {
let error_msg =
format!("Failed to parse ethtool module eeprom message attribute {nla:?}");
let nla = &nla.context(error_msg.clone())?;
let parsed = EthtoolModuleEEPROMAttr::parse(nla).context(error_msg)?;
nlas.push(EthtoolAttr::ModuleEEPROM(parsed));
}
Ok(nlas)
}
54 changes: 54 additions & 0 deletions src/eeprom/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT

use futures::TryStream;
use netlink_packet_generic::GenlMessage;

use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage};

pub struct EthtoolModuleEEPROMGetRequest {
handle: EthtoolHandle,
iface_name: Option<String>,
offset: u32,
length: u32,
page: u8,
bank: u8,
i2c_address: u8,
}

impl EthtoolModuleEEPROMGetRequest {
pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>,
offset: u32,
length: u32,
page: u8,
bank: u8,
i2c_address: u8
) -> Self {
EthtoolModuleEEPROMGetRequest {
handle,
iface_name: iface_name.map(|i| i.to_string()),
offset,
length,
page,
bank,
i2c_address
}
}

pub async fn execute(
self,
) -> impl TryStream<Ok = GenlMessage<EthtoolMessage>, Error = EthtoolError>
{
let EthtoolModuleEEPROMGetRequest {
mut handle,
iface_name,
offset,
length,
page,
bank,
i2c_address
} = self;

let ethtool_msg = EthtoolMessage::new_module_eeprom_get(iface_name.as_deref(), offset, length, page, bank, i2c_address);
ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await
}
}
23 changes: 23 additions & 0 deletions src/eeprom/handle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT

use crate::{EthtoolHandle, EthtoolModuleEEPROMGetRequest};

pub struct EthtoolModuleEEPROMHandle(EthtoolHandle);

impl EthtoolModuleEEPROMHandle {
pub fn new(handle: EthtoolHandle) -> Self {
EthtoolModuleEEPROMHandle(handle)
}

/// Retrieve the module eeprom data pages of a interface (used by `ethtool -m
/// eth1`)
pub fn get(&mut self, iface_name: Option<&str>,
offset: u32,
length: u32,
page: u8,
bank: u8,
i2c_address: u8
) -> EthtoolModuleEEPROMGetRequest {
EthtoolModuleEEPROMGetRequest::new(self.0.clone(), iface_name, offset, length, page, bank, i2c_address)
}
}
11 changes: 11 additions & 0 deletions src/eeprom/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT

mod attr;
mod get;
mod handle;

pub(crate) use attr::parse_module_eeprom_nlas;

pub use attr::EthtoolModuleEEPROMAttr;
pub use get::EthtoolModuleEEPROMGetRequest;
pub use handle::EthtoolModuleEEPROMHandle;
10 changes: 7 additions & 3 deletions src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use netlink_packet_generic::GenlMessage;
use netlink_packet_utils::DecodeError;

use crate::{
try_ethtool, EthtoolChannelHandle, EthtoolCoalesceHandle, EthtoolError,
EthtoolFeatureHandle, EthtoolFecHandle, EthtoolLinkModeHandle,
EthtoolMessage, EthtoolPauseHandle, EthtoolRingHandle, EthtoolTsInfoHandle,
try_ethtool, EthtoolChannelHandle, EthtoolCoalesceHandle, EthtoolError, EthtoolFeatureHandle,
EthtoolFecHandle, EthtoolLinkModeHandle, EthtoolMessage, EthtoolPauseHandle, EthtoolRingHandle,
EthtoolTsInfoHandle, EthtoolModuleEEPROMHandle
};

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -56,6 +56,10 @@ impl EthtoolHandle {
EthtoolChannelHandle::new(self.clone())
}

pub fn eeprom(&mut self) -> EthtoolModuleEEPROMHandle {
EthtoolModuleEEPROMHandle::new(self.clone())
}

pub async fn request(
&mut self,
message: NetlinkMessage<GenlMessage<EthtoolMessage>>,
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod bitset_util;
mod channel;
mod coalesce;
mod connection;
mod eeprom;
mod error;
mod feature;
mod fec;
Expand All @@ -30,6 +31,7 @@ pub use coalesce::{
#[cfg(feature = "tokio_socket")]
pub use connection::new_connection;
pub use connection::new_connection_with_socket;
pub use eeprom::{EthtoolModuleEEPROMAttr, EthtoolModuleEEPROMGetRequest, EthtoolModuleEEPROMHandle};
pub use error::EthtoolError;
pub use feature::{
EthtoolFeatureAttr, EthtoolFeatureBit, EthtoolFeatureGetRequest,
Expand Down
45 changes: 45 additions & 0 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
coalesce::{parse_coalesce_nlas, EthtoolCoalesceAttr},
feature::{parse_feature_nlas, EthtoolFeatureAttr},
fec::{parse_fec_nlas, EthtoolFecAttr},
eeprom::{parse_module_eeprom_nlas, EthtoolModuleEEPROMAttr},
link_mode::{parse_link_mode_nlas, EthtoolLinkModeAttr},
pause::{parse_pause_nlas, EthtoolPauseAttr},
ring::{parse_ring_nlas, EthtoolRingAttr},
Expand All @@ -34,6 +35,8 @@ const ETHTOOL_MSG_FEC_GET_REPLY: u8 = 30;
const ETHTOOL_MSG_CHANNELS_GET: u8 = 17;
const ETHTOOL_MSG_CHANNELS_GET_REPLY: u8 = 18;
const ETHTOOL_MSG_CHANNELS_SET: u8 = 18;
const ETHTOOL_MSG_MODULE_EEPROM_GET: u8 = 31;
const ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY: u8 = 32;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum EthtoolCmd {
Expand All @@ -54,6 +57,8 @@ pub enum EthtoolCmd {
ChannelGet,
ChannelGetReply,
ChannelSet,
ModuleEEPROMGet,
ModuleEEPROMGetReply,
}

impl From<EthtoolCmd> for u8 {
Expand All @@ -76,6 +81,8 @@ impl From<EthtoolCmd> for u8 {
EthtoolCmd::ChannelGet => ETHTOOL_MSG_CHANNELS_GET,
EthtoolCmd::ChannelGetReply => ETHTOOL_MSG_CHANNELS_GET_REPLY,
EthtoolCmd::ChannelSet => ETHTOOL_MSG_CHANNELS_SET,
EthtoolCmd::ModuleEEPROMGet => ETHTOOL_MSG_MODULE_EEPROM_GET,
EthtoolCmd::ModuleEEPROMGetReply => ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY,
}
}
}
Expand All @@ -90,6 +97,7 @@ pub enum EthtoolAttr {
TsInfo(EthtoolTsInfoAttr),
Fec(EthtoolFecAttr),
Channel(EthtoolChannelAttr),
ModuleEEPROM(EthtoolModuleEEPROMAttr),
}

impl Nla for EthtoolAttr {
Expand All @@ -103,6 +111,7 @@ impl Nla for EthtoolAttr {
Self::TsInfo(attr) => attr.value_len(),
Self::Fec(attr) => attr.value_len(),
Self::Channel(attr) => attr.value_len(),
Self::ModuleEEPROM(attr) => attr.value_len(),
}
}

Expand All @@ -116,6 +125,7 @@ impl Nla for EthtoolAttr {
Self::TsInfo(attr) => attr.kind(),
Self::Fec(attr) => attr.kind(),
Self::Channel(attr) => attr.kind(),
Self::ModuleEEPROM(attr) => attr.kind(),
}
}

Expand All @@ -129,6 +139,7 @@ impl Nla for EthtoolAttr {
Self::TsInfo(attr) => attr.emit_value(buffer),
Self::Fec(attr) => attr.emit_value(buffer),
Self::Channel(attr) => attr.emit_value(buffer),
Self::ModuleEEPROM(attr) => attr.emit_value(buffer),
}
}
}
Expand Down Expand Up @@ -289,11 +300,41 @@ impl EthtoolMessage {
vec![EthtoolAttr::Channel(EthtoolChannelAttr::Header(vec![
EthtoolHeader::DevName(iface_name.to_string()),
]))];

EthtoolMessage {
cmd: EthtoolCmd::ChannelSet,
nlas,
}
}


pub fn new_module_eeprom_get(
iface_name: Option<&str>,
offset: u32,
length: u32,
page:u8,
bank:u8,
i2c_address:u8) -> Self {
let mut nlas = match iface_name {
Some(s) => {
vec![EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Header(vec![
EthtoolHeader::DevName(s.to_string()),
]))]
}
None => {
vec![EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Header(vec![]))]
}
};
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Offset(offset)));
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Length(length)));
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Page(page)));
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Bank(bank)));
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::I2CAddress(i2c_address)));
EthtoolMessage {
cmd: EthtoolCmd::ModuleEEPROMGet,
nlas,
}
}
}

impl Emitable for EthtoolMessage {
Expand Down Expand Up @@ -344,6 +385,10 @@ impl ParseableParametrized<[u8], GenlHeader> for EthtoolMessage {
cmd: EthtoolCmd::ChannelGetReply,
nlas: parse_channel_nlas(buffer)?,
},
ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY => Self {
cmd: EthtoolCmd::ModuleEEPROMGetReply,
nlas: parse_module_eeprom_nlas(buffer)?,
},
cmd => {
return Err(DecodeError::from(format!(
"Unsupported ethtool reply command: {cmd}"
Expand Down