Skip to content

Commit 9caab7e

Browse files
laundreicathay4t
authored andcommitted
Adding support for dumping eeprom pages.
1 parent 64382a5 commit 9caab7e

File tree

8 files changed

+288
-3
lines changed

8 files changed

+288
-3
lines changed

examples/dump_eeprom_page.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use futures::stream::TryStreamExt;
4+
5+
fn main() {
6+
let rt = tokio::runtime::Builder::new_current_thread()
7+
.enable_io()
8+
.build()
9+
.unwrap();
10+
let iface_name = std::env::args().nth(1);
11+
rt.block_on(get_eeprom(iface_name.as_deref()));
12+
}
13+
14+
async fn get_eeprom(iface_name: Option<&str>) {
15+
let (connection, mut handle, _) = ethtool::new_connection().unwrap();
16+
tokio::spawn(connection);
17+
18+
let mut eeprom_handle = handle
19+
.eeprom()
20+
.get(iface_name, 0, 1, 0, 0, 0x50)
21+
.execute()
22+
.await;
23+
24+
let mut msgs = Vec::new();
25+
while let Some(msg) = eeprom_handle.try_next().await.unwrap() {
26+
msgs.push(msg);
27+
}
28+
assert!(!msgs.is_empty());
29+
for msg in msgs {
30+
println!("{:?}", msg);
31+
}
32+
}

src/eeprom/attr.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use anyhow::Context;
4+
use byteorder::{ByteOrder, NativeEndian};
5+
use netlink_packet_utils::{
6+
nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED},
7+
DecodeError, Emitable, Parseable,
8+
};
9+
10+
use crate::{EthtoolAttr, EthtoolHeader};
11+
12+
13+
const ETHTOOL_A_MODULE_EEPROM_HEADER: u16 = 1;
14+
const ETHTOOL_A_MODULE_EEPROM_OFFSET: u16 = 2;
15+
const ETHTOOL_A_MODULE_EEPROM_LENGTH: u16 = 3;
16+
const ETHTOOL_A_MODULE_EEPROM_PAGE: u16 = 4;
17+
const ETHTOOL_A_MODULE_EEPROM_BANK: u16 = 5;
18+
const ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS: u16 = 6;
19+
const ETHTOOL_A_MODULE_EEPROM_DATA: u16 = 7;
20+
21+
#[derive(Debug, PartialEq, Eq, Clone)]
22+
pub enum EthtoolModuleEEPROMAttr {
23+
Header(Vec<EthtoolHeader>),
24+
Offset(u32),
25+
Length(u32),
26+
Page(u8),
27+
Bank(u8),
28+
I2CAddress(u8),
29+
Data(Vec<u8>),
30+
Other(DefaultNla),
31+
}
32+
33+
impl Nla for EthtoolModuleEEPROMAttr {
34+
fn value_len(&self) -> usize {
35+
match self {
36+
Self::Header(hdrs) => hdrs.as_slice().buffer_len(),
37+
Self::Data(data) => data.len(),
38+
Self::Page(_)
39+
| Self::Bank(_)
40+
| Self::I2CAddress(_) => 1,
41+
Self::Offset(_)
42+
| Self::Length(_) => 4,
43+
Self::Other(attr) => attr.value_len(),
44+
}
45+
}
46+
47+
fn kind(&self) -> u16 {
48+
match self {
49+
Self::Header(_) => ETHTOOL_A_MODULE_EEPROM_HEADER | NLA_F_NESTED,
50+
Self::Offset(_) => ETHTOOL_A_MODULE_EEPROM_OFFSET,
51+
Self::Length(_) => ETHTOOL_A_MODULE_EEPROM_LENGTH,
52+
Self::Page(_) => ETHTOOL_A_MODULE_EEPROM_PAGE,
53+
Self::Bank(_) => ETHTOOL_A_MODULE_EEPROM_BANK,
54+
Self::I2CAddress(_) => ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS,
55+
Self::Data(_) => ETHTOOL_A_MODULE_EEPROM_DATA,
56+
Self::Other(attr) => attr.kind(),
57+
}
58+
}
59+
60+
fn emit_value(&self, buffer: &mut [u8]) {
61+
match self {
62+
Self::Header(ref nlas) => nlas.as_slice().emit(buffer),
63+
Self::Data(d) => buffer.copy_from_slice(d.as_slice()),
64+
Self::Page(d)
65+
| Self::Bank(d)
66+
| Self::I2CAddress(d) => buffer[0] = *d,
67+
Self::Offset(d)
68+
| Self::Length(d) => NativeEndian::write_u32(buffer, *d),
69+
Self::Other(ref attr) => attr.emit(buffer),
70+
}
71+
}
72+
}
73+
74+
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
75+
for EthtoolModuleEEPROMAttr
76+
{
77+
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
78+
let payload = buf.value();
79+
Ok(match buf.kind() {
80+
ETHTOOL_A_MODULE_EEPROM_HEADER => {
81+
let mut nlas = Vec::new();
82+
let error_msg = "failed to parse module eeprom header attributes";
83+
for nla in NlasIterator::new(payload) {
84+
let nla = &nla.context(error_msg)?;
85+
let parsed =
86+
EthtoolHeader::parse(nla).context(error_msg)?;
87+
nlas.push(parsed);
88+
}
89+
Self::Header(nlas)
90+
}
91+
ETHTOOL_A_MODULE_EEPROM_DATA => Self::Data(
92+
Vec::from(payload),
93+
),
94+
kind => Self::Other(
95+
DefaultNla::parse(buf)
96+
.context(format!("invalid ethtool module eeprom NLA kind {kind}"))?,
97+
),
98+
})
99+
}
100+
}
101+
102+
pub(crate) fn parse_module_eeprom_nlas(
103+
buffer: &[u8],
104+
) -> Result<Vec<EthtoolAttr>, DecodeError> {
105+
let mut nlas = Vec::new();
106+
for nla in NlasIterator::new(buffer) {
107+
let error_msg =
108+
format!("Failed to parse ethtool module eeprom message attribute {nla:?}");
109+
let nla = &nla.context(error_msg.clone())?;
110+
let parsed = EthtoolModuleEEPROMAttr::parse(nla).context(error_msg)?;
111+
nlas.push(EthtoolAttr::ModuleEEPROM(parsed));
112+
}
113+
Ok(nlas)
114+
}

src/eeprom/get.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use futures::TryStream;
4+
use netlink_packet_generic::GenlMessage;
5+
6+
use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage};
7+
8+
pub struct EthtoolModuleEEPROMGetRequest {
9+
handle: EthtoolHandle,
10+
iface_name: Option<String>,
11+
offset: u32,
12+
length: u32,
13+
page: u8,
14+
bank: u8,
15+
i2c_address: u8,
16+
}
17+
18+
impl EthtoolModuleEEPROMGetRequest {
19+
pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>,
20+
offset: u32,
21+
length: u32,
22+
page: u8,
23+
bank: u8,
24+
i2c_address: u8
25+
) -> Self {
26+
EthtoolModuleEEPROMGetRequest {
27+
handle,
28+
iface_name: iface_name.map(|i| i.to_string()),
29+
offset,
30+
length,
31+
page,
32+
bank,
33+
i2c_address
34+
}
35+
}
36+
37+
pub async fn execute(
38+
self,
39+
) -> impl TryStream<Ok = GenlMessage<EthtoolMessage>, Error = EthtoolError>
40+
{
41+
let EthtoolModuleEEPROMGetRequest {
42+
mut handle,
43+
iface_name,
44+
offset,
45+
length,
46+
page,
47+
bank,
48+
i2c_address
49+
} = self;
50+
51+
let ethtool_msg = EthtoolMessage::new_module_eeprom_get(iface_name.as_deref(), offset, length, page, bank, i2c_address);
52+
ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await
53+
}
54+
}

src/eeprom/handle.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use crate::{EthtoolHandle, EthtoolModuleEEPROMGetRequest};
4+
5+
pub struct EthtoolModuleEEPROMHandle(EthtoolHandle);
6+
7+
impl EthtoolModuleEEPROMHandle {
8+
pub fn new(handle: EthtoolHandle) -> Self {
9+
EthtoolModuleEEPROMHandle(handle)
10+
}
11+
12+
/// Retrieve the module eeprom data pages of a interface (used by `ethtool -m
13+
/// eth1`)
14+
pub fn get(&mut self, iface_name: Option<&str>,
15+
offset: u32,
16+
length: u32,
17+
page: u8,
18+
bank: u8,
19+
i2c_address: u8
20+
) -> EthtoolModuleEEPROMGetRequest {
21+
EthtoolModuleEEPROMGetRequest::new(self.0.clone(), iface_name, offset, length, page, bank, i2c_address)
22+
}
23+
}

src/eeprom/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
mod attr;
4+
mod get;
5+
mod handle;
6+
7+
pub(crate) use attr::parse_module_eeprom_nlas;
8+
9+
pub use attr::EthtoolModuleEEPROMAttr;
10+
pub use get::EthtoolModuleEEPROMGetRequest;
11+
pub use handle::EthtoolModuleEEPROMHandle;

src/handle.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use netlink_packet_core::{
99
use netlink_packet_generic::GenlMessage;
1010

1111
use crate::{
12-
try_ethtool, EthtoolChannelHandle, EthtoolCoalesceHandle, EthtoolError,
13-
EthtoolFeatureHandle, EthtoolFecHandle, EthtoolLinkModeHandle,
14-
EthtoolMessage, EthtoolPauseHandle, EthtoolRingHandle, EthtoolTsInfoHandle,
12+
try_ethtool, EthtoolChannelHandle, EthtoolCoalesceHandle, EthtoolError, EthtoolFeatureHandle,
13+
EthtoolFecHandle, EthtoolLinkModeHandle, EthtoolMessage, EthtoolPauseHandle, EthtoolRingHandle,
14+
EthtoolTsInfoHandle, EthtoolModuleEEPROMHandle
1515
};
1616

1717
#[derive(Clone, Debug)]
@@ -56,6 +56,10 @@ impl EthtoolHandle {
5656
EthtoolChannelHandle::new(self.clone())
5757
}
5858

59+
pub fn eeprom(&mut self) -> EthtoolModuleEEPROMHandle {
60+
EthtoolModuleEEPROMHandle::new(self.clone())
61+
}
62+
5963
pub async fn request(
6064
&mut self,
6165
message: NetlinkMessage<GenlMessage<EthtoolMessage>>,

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod bitset_util;
44
mod channel;
55
mod coalesce;
66
mod connection;
7+
mod eeprom;
78
mod error;
89
mod feature;
910
mod fec;
@@ -30,6 +31,7 @@ pub use coalesce::{
3031
#[cfg(feature = "tokio_socket")]
3132
pub use connection::new_connection;
3233
pub use connection::new_connection_with_socket;
34+
pub use eeprom::{EthtoolModuleEEPROMAttr, EthtoolModuleEEPROMGetRequest, EthtoolModuleEEPROMHandle};
3335
pub use error::EthtoolError;
3436
pub use feature::{
3537
EthtoolFeatureAttr, EthtoolFeatureBit, EthtoolFeatureGetRequest,

src/message.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
coalesce::{parse_coalesce_nlas, EthtoolCoalesceAttr},
99
feature::{parse_feature_nlas, EthtoolFeatureAttr},
1010
fec::{parse_fec_nlas, EthtoolFecAttr},
11+
eeprom::{parse_module_eeprom_nlas, EthtoolModuleEEPROMAttr},
1112
link_mode::{parse_link_mode_nlas, EthtoolLinkModeAttr},
1213
pause::{parse_pause_nlas, EthtoolPauseAttr},
1314
ring::{parse_ring_nlas, EthtoolRingAttr},
@@ -32,6 +33,8 @@ const ETHTOOL_MSG_FEC_GET_REPLY: u8 = 30;
3233
const ETHTOOL_MSG_CHANNELS_GET: u8 = 17;
3334
const ETHTOOL_MSG_CHANNELS_GET_REPLY: u8 = 18;
3435
const ETHTOOL_MSG_CHANNELS_SET: u8 = 18;
36+
const ETHTOOL_MSG_MODULE_EEPROM_GET: u8 = 31;
37+
const ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY: u8 = 32;
3538

3639
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
3740
pub enum EthtoolCmd {
@@ -52,6 +55,8 @@ pub enum EthtoolCmd {
5255
ChannelGet,
5356
ChannelGetReply,
5457
ChannelSet,
58+
ModuleEEPROMGet,
59+
ModuleEEPROMGetReply,
5560
}
5661

5762
impl From<EthtoolCmd> for u8 {
@@ -74,6 +79,8 @@ impl From<EthtoolCmd> for u8 {
7479
EthtoolCmd::ChannelGet => ETHTOOL_MSG_CHANNELS_GET,
7580
EthtoolCmd::ChannelGetReply => ETHTOOL_MSG_CHANNELS_GET_REPLY,
7681
EthtoolCmd::ChannelSet => ETHTOOL_MSG_CHANNELS_SET,
82+
EthtoolCmd::ModuleEEPROMGet => ETHTOOL_MSG_MODULE_EEPROM_GET,
83+
EthtoolCmd::ModuleEEPROMGetReply => ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY,
7784
}
7885
}
7986
}
@@ -88,6 +95,7 @@ pub enum EthtoolAttr {
8895
TsInfo(EthtoolTsInfoAttr),
8996
Fec(EthtoolFecAttr),
9097
Channel(EthtoolChannelAttr),
98+
ModuleEEPROM(EthtoolModuleEEPROMAttr),
9199
}
92100

93101
impl Nla for EthtoolAttr {
@@ -101,6 +109,7 @@ impl Nla for EthtoolAttr {
101109
Self::TsInfo(attr) => attr.value_len(),
102110
Self::Fec(attr) => attr.value_len(),
103111
Self::Channel(attr) => attr.value_len(),
112+
Self::ModuleEEPROM(attr) => attr.value_len(),
104113
}
105114
}
106115

@@ -114,6 +123,7 @@ impl Nla for EthtoolAttr {
114123
Self::TsInfo(attr) => attr.kind(),
115124
Self::Fec(attr) => attr.kind(),
116125
Self::Channel(attr) => attr.kind(),
126+
Self::ModuleEEPROM(attr) => attr.kind(),
117127
}
118128
}
119129

@@ -127,6 +137,7 @@ impl Nla for EthtoolAttr {
127137
Self::TsInfo(attr) => attr.emit_value(buffer),
128138
Self::Fec(attr) => attr.emit_value(buffer),
129139
Self::Channel(attr) => attr.emit_value(buffer),
140+
Self::ModuleEEPROM(attr) => attr.emit_value(buffer),
130141
}
131142
}
132143
}
@@ -287,11 +298,41 @@ impl EthtoolMessage {
287298
vec![EthtoolAttr::Channel(EthtoolChannelAttr::Header(vec![
288299
EthtoolHeader::DevName(iface_name.to_string()),
289300
]))];
301+
290302
EthtoolMessage {
291303
cmd: EthtoolCmd::ChannelSet,
292304
nlas,
293305
}
294306
}
307+
308+
309+
pub fn new_module_eeprom_get(
310+
iface_name: Option<&str>,
311+
offset: u32,
312+
length: u32,
313+
page:u8,
314+
bank:u8,
315+
i2c_address:u8) -> Self {
316+
let mut nlas = match iface_name {
317+
Some(s) => {
318+
vec![EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Header(vec![
319+
EthtoolHeader::DevName(s.to_string()),
320+
]))]
321+
}
322+
None => {
323+
vec![EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Header(vec![]))]
324+
}
325+
};
326+
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Offset(offset)));
327+
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Length(length)));
328+
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Page(page)));
329+
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::Bank(bank)));
330+
nlas.push(EthtoolAttr::ModuleEEPROM(EthtoolModuleEEPROMAttr::I2CAddress(i2c_address)));
331+
EthtoolMessage {
332+
cmd: EthtoolCmd::ModuleEEPROMGet,
333+
nlas,
334+
}
335+
}
295336
}
296337

297338
impl Emitable for EthtoolMessage {
@@ -342,6 +383,10 @@ impl ParseableParametrized<[u8], GenlHeader> for EthtoolMessage {
342383
cmd: EthtoolCmd::ChannelGetReply,
343384
nlas: parse_channel_nlas(buffer)?,
344385
},
386+
ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY => Self {
387+
cmd: EthtoolCmd::ModuleEEPROMGetReply,
388+
nlas: parse_module_eeprom_nlas(buffer)?,
389+
},
345390
cmd => {
346391
return Err(DecodeError::from(format!(
347392
"Unsupported ethtool reply command: {cmd}"

0 commit comments

Comments
 (0)