ixgbe_driver/lib.rs
1//! # ixgbe-driver
2//!
3//! A `no_std` driver implementation for Intel 82599+ 10 Gigabit Ethernet NICs.
4//!
5//! This crate provides a safe, low-level driver for the Intel 82599 (ixgbe) family of
6//! network interface cards. It is designed to be used in embedded and bare-metal environments
7//! where the standard library is not available.
8//!
9//! ## Features
10//!
11//! - `no_std` compatible - works without the standard library
12//! - Multi-queue support for both RX and TX
13//! - MSI/MSI-X interrupt support (with `irq` feature)
14//! - Memory pool management for efficient packet buffer allocation
15//! - Zero-copy packet handling
16//!
17//! ## Basic Usage
18//!
19//! ```rust,ignore
20//! use ixgbe_driver::{IxgbeDevice, IxgbeHal, MemPool, NicDevice};
21//!
22//! // First, implement the IxgbeHal trait for your platform
23//! struct MyHal;
24//!
25//! unsafe impl IxgbeHal for MyHal {
26//! // Implement required methods...
27//! }
28//!
29//! // Initialize the device
30//! let pool = MemPool::allocate::<MyHal>(4096, 2048)?;
31//! let device = IxgbeDevice::<MyHal, 512>::init(
32//! pci_bar_addr,
33//! pci_bar_size,
34//! num_rx_queues,
35//! num_tx_queues,
36//! &pool,
37//! )?;
38//!
39//! // Send and receive packets
40//! let tx_buf = IxgbeNetBuf::alloc(&pool, packet_size)?;
41//! device.send(queue_id, tx_buf)?;
42//!
43//! device.receive_packets(queue_id, num_packets, |rx_buf| {
44//! // Handle received packet
45//! })?;
46//! ```
47//!
48//! ## Hardware Abstraction Layer (HAL)
49//!
50//! This driver requires the user to implement the [`IxgbeHal`] trait, which provides
51//! platform-specific operations such as:
52//! - DMA memory allocation/deallocation
53//! - MMIO address translation
54//! - Timing/waiting operations
55//!
56//! ## Platform Support
57//!
58//! The ixgbe driver supports Intel 82599 and compatible 10GbE controllers including:
59//! - Intel 82599ES
60//! - Intel X540
61//! - Intel X550/X552
62//!
63//! ## Interrupt Modes
64//!
65//! The driver supports three operation modes:
66//! - **Polling mode** (default): No interrupts, the driver continuously polls for packets
67//! - **MSI mode**: Message Signaled Interrupts (with `irq` feature)
68//! - **MSI-X mode**: Extended MSI with multiple vectors (with `irq` feature)
69
70#![no_std]
71#![deny(warnings)]
72#![deny(missing_docs)]
73#![allow(dead_code)]
74
75mod constants;
76pub mod descriptor;
77mod hal;
78mod interrupts;
79mod ixgbe;
80mod memory;
81
82extern crate alloc;
83#[macro_use]
84extern crate log;
85
86pub use hal::IxgbeHal;
87pub use ixgbe::{IxgbeDevice, IxgbeNetBuf};
88
89pub use memory::{alloc_pkt, MemPool, PhysAddr, PACKET_HEADROOM};
90
91/// Vendor ID for Intel.
92pub const INTEL_VEND: u16 = 0x8086;
93
94/// Device ID for the 82599ES, used to identify the device from the PCI space.
95pub const INTEL_82599: u16 = 0x10FB;
96
97/// Error type for ixgbe driver operations.
98///
99/// This enum represents the various error conditions that can occur when
100/// interacting with the ixgbe device.
101#[derive(Debug)]
102pub enum IxgbeError {
103 /// The queue size is not aligned (not a power of 2).
104 ///
105 /// Hardware descriptor rings require sizes that are powers of 2.
106 QueueNotAligned,
107 /// There are not enough descriptors available in the queue.
108 ///
109 /// The transmit or receive queue is full. The caller should retry later
110 /// after processing pending packets.
111 QueueFull,
112 /// No memory available.
113 ///
114 /// The memory pool is exhausted or DMA allocation failed.
115 NoMemory,
116 /// The allocated page is not properly aligned.
117 ///
118 /// DMA memory must be aligned to page boundaries.
119 PageNotAligned,
120 /// The device is not ready for the requested operation.
121 ///
122 /// This typically means a packet receive operation was attempted when
123 /// no packets are available.
124 NotReady,
125 /// Invalid queue ID.
126 ///
127 /// The specified `queue_id` does not exist on this device.
128 InvalidQueue,
129}
130
131/// Result type for ixgbe driver functions.
132///
133/// A type alias for `Result` with [`IxgbeError`] as the error type.
134pub type IxgbeResult<T = ()> = Result<T, IxgbeError>;
135
136/// Generic network device interface.
137///
138/// This trait provides a common interface for network device drivers, allowing
139/// protocol stacks to be implemented independently of the specific NIC hardware.
140/// It is inspired by the ixy driver approach and provides methods for:
141///
142/// - Device identification and configuration
143/// - Packet transmission and reception
144/// - Queue management
145/// - Statistics tracking
146///
147/// # Example
148///
149/// ```rust,ignore
150/// use ixgbe_driver::{IxgbeDevice, NicDevice};
151///
152/// let mut device = IxgbeDevice::<MyHal, 512>::init(...)?;
153///
154/// // Check device info
155/// println!("Driver: {}", device.get_driver_name());
156/// println!("MAC: {:02x?}", device.get_mac_addr());
157/// println!("Speed: {} Mbit/s", device.get_link_speed());
158///
159/// // Send and receive
160/// while device.can_receive(0)? {
161/// device.receive_packets(0, 32, |packet| {
162/// // Process packet
163/// })?;
164/// }
165/// ```
166pub trait NicDevice<H: IxgbeHal> {
167 /// Returns the driver's name.
168 ///
169 /// This is a simple string identifier such as "ixgbe" or "virtio".
170 fn get_driver_name(&self) -> &str;
171
172 /// Returns the MAC (Ethernet) address of this device.
173 ///
174 /// Returns a 6-byte array representing the layer 2 address.
175 fn get_mac_addr(&self) -> [u8; 6];
176
177 /// Resets the network card's statistics registers.
178 ///
179 /// Clears all packet and byte counters maintained by the hardware.
180 /// This affects the values returned by subsequent statistics reads.
181 fn reset_stats(&mut self);
182
183 /// Returns the link speed of the network card.
184 ///
185 /// Returns the current link speed in Mbit/s (e.g., 10000 for 10GbE).
186 /// Returns 0 if the link is down.
187 fn get_link_speed(&self) -> u16;
188
189 /// Polls the transmit queue for completed packets and frees their buffers.
190 ///
191 /// This method reclaims transmit descriptors that have been completed by
192 /// the hardware and returns their packet buffers to the memory pool.
193 ///
194 /// # Arguments
195 ///
196 /// * `queue_id` - The transmit queue ID to clean
197 ///
198 /// # Errors
199 ///
200 /// Returns [`IxgbeError::InvalidQueue`] if `queue_id` is out of range.
201 fn recycle_tx_buffers(&mut self, queue_id: u16) -> IxgbeResult;
202
203 /// Receives up to `packet_nums` packets from the network.
204 ///
205 /// This method receives packets from the specified queue and invokes the
206 /// closure `f` for each received packet. Using a closure avoids dynamic
207 /// memory allocation and allows the caller to handle packets efficiently.
208 ///
209 /// # Arguments
210 ///
211 /// * `queue_id` - The receive queue ID to read from
212 /// * `packet_nums` - Maximum number of packets to receive
213 /// * `f` - Closure called for each received packet with the [`IxgbeNetBuf`]
214 ///
215 /// # Returns
216 ///
217 /// Returns the number of packets actually received.
218 ///
219 /// # Errors
220 ///
221 /// - [`IxgbeError::NotReady`] - No packets are currently available
222 /// - [`IxgbeError::InvalidQueue`] - `queue_id` is out of range
223 ///
224 /// # Example
225 ///
226 /// ```rust,ignore
227 /// let count = device.receive_packets(0, 32, |packet| {
228 /// let data = packet.packet();
229 /// // Handle packet data
230 /// })?;
231 /// println!("Received {} packets", count);
232 /// ```
233 fn receive_packets<F>(&mut self, queue_id: u16, packet_nums: usize, f: F) -> IxgbeResult<usize>
234 where
235 F: FnMut(IxgbeNetBuf);
236
237 /// Sends a packet to the network.
238 ///
239 /// Queues the packet for transmission on the specified queue. The actual
240 /// transmission happens asynchronously in the hardware.
241 ///
242 /// # Arguments
243 ///
244 /// * `queue_id` - The transmit queue ID to send on
245 /// * `tx_buf` - The packet buffer to send
246 ///
247 /// # Errors
248 ///
249 /// - [`IxgbeError::QueueFull`] - The transmit queue has no available descriptors
250 /// - [`IxgbeError::InvalidQueue`] - `queue_id` is out of range
251 fn send(&mut self, queue_id: u16, tx_buf: IxgbeNetBuf) -> IxgbeResult;
252
253 /// Checks whether a packet can be received from the specified queue.
254 ///
255 /// Returns `true` if at least one packet is available in the receive queue.
256 ///
257 /// # Errors
258 ///
259 /// Returns [`IxgbeError::InvalidQueue`] if `queue_id` is out of range.
260 fn can_receive(&self, queue_id: u16) -> IxgbeResult<bool>;
261
262 /// Checks whether a packet can be sent on the specified queue.
263 ///
264 /// Returns `true` if the transmit queue has at least one free descriptor.
265 ///
266 /// # Errors
267 ///
268 /// Returns [`IxgbeError::InvalidQueue`] if `queue_id` is out of range.
269 fn can_send(&self, queue_id: u16) -> IxgbeResult<bool>;
270}
271
272/// Network device statistics.
273///
274/// Holds counters for sent and received packets and bytes.
275/// These values can be read from the hardware's statistic registers.
276#[derive(Default, Copy, Clone)]
277pub struct DeviceStats {
278 /// Number of received packets.
279 pub rx_pkts: u64,
280 /// Number of transmitted packets.
281 pub tx_pkts: u64,
282 /// Number of received bytes.
283 pub rx_bytes: u64,
284 /// Number of transmitted bytes.
285 pub tx_bytes: u64,
286}
287
288impl core::fmt::Display for DeviceStats {
289 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
290 write!(
291 f,
292 "rx_pkts: {}, tx_pkts: {}, rx_bytes: {}, tx_bytes: {}",
293 self.rx_pkts, self.tx_pkts, self.rx_bytes, self.tx_bytes
294 )
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 #[test]
303 fn test_vendor_device_constants() {
304 assert_eq!(INTEL_VEND, 0x8086);
305 assert_eq!(INTEL_82599, 0x10FB);
306 }
307
308 #[test]
309 fn test_device_stats_default() {
310 let stats = DeviceStats::default();
311 assert_eq!(stats.rx_pkts, 0);
312 assert_eq!(stats.tx_pkts, 0);
313 assert_eq!(stats.rx_bytes, 0);
314 assert_eq!(stats.tx_bytes, 0);
315 }
316
317 #[test]
318 fn test_device_stats_copy_clone() {
319 let mut stats = DeviceStats::default();
320 stats.rx_pkts = 100;
321 stats.tx_pkts = 50;
322 stats.rx_bytes = 1500;
323 stats.tx_bytes = 750;
324
325 let copy = stats;
326 assert_eq!(copy.rx_pkts, 100);
327 assert_eq!(copy.tx_pkts, 50);
328 assert_eq!(copy.rx_bytes, 1500);
329 assert_eq!(copy.tx_bytes, 750);
330
331 let cloned = stats.clone();
332 assert_eq!(cloned.rx_pkts, 100);
333 assert_eq!(cloned.tx_pkts, 50);
334 }
335}