igbe: cleanup igbe code
This commit is contained in:
parent
90edc4c8ed
commit
b836cf7fc7
@ -1,24 +1,20 @@
|
||||
#![no_std]
|
||||
|
||||
use core::mem::{self, MaybeUninit};
|
||||
use core::{mem::MaybeUninit, time::Duration};
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
interrupt::{InterruptAffinity, InterruptHandler, IrqVector},
|
||||
};
|
||||
use libk::{
|
||||
dma::{BusAddress, DmaBuffer},
|
||||
error::Error,
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk::{dma::DmaBuffer, error::Error};
|
||||
use libk_util::{
|
||||
sync::{IrqSafeSpinlock, Spinlock},
|
||||
OneTimeInit,
|
||||
};
|
||||
use regs::{Regs, ICR};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use ring::{RxRing, TxRing};
|
||||
use ygg_driver_net_core::{
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
register_interface, RxPacket,
|
||||
@ -26,209 +22,17 @@ use ygg_driver_net_core::{
|
||||
use ygg_driver_pci::{
|
||||
device::{PciDeviceInfo, PreferredInterruptMode},
|
||||
macros::pci_driver,
|
||||
PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
|
||||
PciBaseAddress, PciConfigurationSpace,
|
||||
};
|
||||
use yggdrasil_abi::net::{link::LinkState, MacAddress};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod regs;
|
||||
|
||||
struct RxRing {
|
||||
descriptors: DmaBuffer<[RxDescriptor]>,
|
||||
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
|
||||
buffer_size: usize,
|
||||
tail: u16,
|
||||
}
|
||||
|
||||
struct TxRing {
|
||||
descriptors: DmaBuffer<[TxDescriptor]>,
|
||||
buffers: Vec<Option<DmaBuffer<[u8]>>>,
|
||||
// Consumer end
|
||||
tail: u16,
|
||||
// Producer end
|
||||
head: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct RxDescriptor {
|
||||
address: BusAddress,
|
||||
length: u16,
|
||||
checksum: u16,
|
||||
status: u8,
|
||||
errors: u8,
|
||||
special: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct TxDescriptor {
|
||||
address: BusAddress,
|
||||
length: u16,
|
||||
cso: u8,
|
||||
cmd: u8,
|
||||
sta: u8,
|
||||
_0: u8,
|
||||
css: u8,
|
||||
special: u8,
|
||||
}
|
||||
|
||||
impl RxRing {
|
||||
pub fn with_capacity(
|
||||
dma: &dyn DmaAllocator,
|
||||
capacity: usize,
|
||||
buffer_size: usize,
|
||||
) -> Result<Self, Error> {
|
||||
let buffers = (0..capacity)
|
||||
.map(|_| DmaBuffer::new_uninit_slice(dma, buffer_size))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let descriptors = DmaBuffer::new_slice_with(
|
||||
dma,
|
||||
|i| RxDescriptor::new(buffers[i].bus_address(), buffer_size as u16),
|
||||
capacity,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
descriptors,
|
||||
buffers,
|
||||
tail: 0,
|
||||
buffer_size,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO move to background task/softirq to reduce amount of code run by the irq handler
|
||||
pub fn handle_rx<F: FnMut(DmaBuffer<[u8]>, usize)>(
|
||||
&mut self,
|
||||
dma: &dyn DmaAllocator,
|
||||
head: u16,
|
||||
mut handler: F,
|
||||
) -> u16 {
|
||||
let capacity = self.descriptors.len();
|
||||
while self.tail != head {
|
||||
let index = self.tail as usize;
|
||||
// Replace the buffer
|
||||
|
||||
let new_buffer = DmaBuffer::new_uninit_slice(dma, self.buffer_size).unwrap();
|
||||
let new_buffer_address = new_buffer.bus_address();
|
||||
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
|
||||
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
|
||||
|
||||
let descriptor = &mut self.descriptors[index];
|
||||
|
||||
if descriptor.errors & !1 != 0 {
|
||||
log::warn!("igbe: drop erroneous packet {:#x}", descriptor.errors);
|
||||
} else {
|
||||
let len = descriptor.length as usize;
|
||||
handler(buffer, len);
|
||||
}
|
||||
|
||||
// Replace the descriptor
|
||||
*descriptor = RxDescriptor::new(new_buffer_address, self.buffer_size as u16);
|
||||
|
||||
self.tail = (self.tail + 1) & (capacity as u16 - 1);
|
||||
}
|
||||
|
||||
(self.tail + capacity as u16 - 1) & (capacity as u16 - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl TxRing {
|
||||
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
let buffers = (0..capacity).map(|_| None).collect::<Vec<_>>();
|
||||
let descriptors = DmaBuffer::new_slice_with(dma, |_| TxDescriptor::empty(), capacity)?;
|
||||
Ok(Self {
|
||||
descriptors,
|
||||
buffers,
|
||||
head: 0,
|
||||
tail: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle_tx(&mut self, head: u16) {
|
||||
self.tail = self.head;
|
||||
}
|
||||
|
||||
pub fn tx_now(&mut self, buffer: DmaBuffer<[u8]>) -> Result<u16, DmaBuffer<[u8]>> {
|
||||
// Queue full
|
||||
let capacity = self.descriptors.len();
|
||||
if (self.head + 1) & (capacity as u16 - 1) == self.tail {
|
||||
log::warn!("igbe: tx queue full");
|
||||
return Err(buffer);
|
||||
}
|
||||
let index = self.head as usize;
|
||||
|
||||
let descriptor = &mut self.descriptors[index];
|
||||
// Only generate interrupts for every 1/4th of the buffer
|
||||
let quarter = capacity / 4;
|
||||
descriptor.setup_tx(
|
||||
buffer.bus_address(),
|
||||
buffer.len() as u16 - 4,
|
||||
index % quarter == quarter - 1,
|
||||
);
|
||||
self.descriptors.cache_flush_element(index, true);
|
||||
|
||||
self.buffers[index] = Some(buffer);
|
||||
|
||||
self.head = (self.head + 1) & (capacity as u16 - 1);
|
||||
Ok(self.head)
|
||||
}
|
||||
}
|
||||
|
||||
impl RxDescriptor {
|
||||
pub fn new(address: BusAddress, length: u16) -> Self {
|
||||
Self {
|
||||
address,
|
||||
length,
|
||||
checksum: 0,
|
||||
status: 0,
|
||||
errors: 0,
|
||||
special: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TxDescriptor {
|
||||
// Descriptor done
|
||||
const STA_DD: u8 = 1 << 0;
|
||||
// End of packet
|
||||
const CMD_EOP: u8 = 1 << 0;
|
||||
// Insert MAC FCS
|
||||
const CMD_IFCS: u8 = 1 << 1;
|
||||
// Insert checksum
|
||||
const CMD_IC: u8 = 1 << 2;
|
||||
// Report status
|
||||
const CMD_RS: u8 = 1 << 3;
|
||||
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
address: BusAddress::ZERO,
|
||||
length: 0,
|
||||
cso: 0,
|
||||
cmd: 0,
|
||||
sta: Self::STA_DD,
|
||||
_0: 0,
|
||||
css: 0,
|
||||
special: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_tx(&mut self, address: BusAddress, length: u16, ioc: bool) {
|
||||
let mut cmd = Self::CMD_EOP | Self::CMD_IFCS;
|
||||
if ioc {
|
||||
cmd |= Self::CMD_RS;
|
||||
}
|
||||
self.address = address;
|
||||
self.length = length;
|
||||
self.css = 0;
|
||||
self.cso = 0;
|
||||
self.sta = 0;
|
||||
self.cmd = cmd;
|
||||
self.special = 0;
|
||||
}
|
||||
}
|
||||
mod ring;
|
||||
|
||||
struct Igbe {
|
||||
regs: IrqSafeSpinlock<Regs>,
|
||||
base: PciBaseAddress,
|
||||
dma: Arc<dyn DmaAllocator>,
|
||||
pci: PciDeviceInfo,
|
||||
|
||||
@ -239,15 +43,9 @@ struct Igbe {
|
||||
}
|
||||
|
||||
impl Igbe {
|
||||
pub fn new(
|
||||
dma: Arc<dyn DmaAllocator>,
|
||||
base: PciBaseAddress,
|
||||
regs: Regs,
|
||||
pci: PciDeviceInfo,
|
||||
) -> Self {
|
||||
pub fn new(dma: Arc<dyn DmaAllocator>, regs: Regs, pci: PciDeviceInfo) -> Self {
|
||||
Self {
|
||||
dma,
|
||||
base,
|
||||
pci,
|
||||
mac: OneTimeInit::new(),
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
@ -273,7 +71,7 @@ impl Device for Igbe {
|
||||
regs.disable_interrupts();
|
||||
let mac = regs.read_mac()?;
|
||||
self.mac.init(mac);
|
||||
regs.reset(1000000)?;
|
||||
regs.reset(Duration::from_millis(200))?;
|
||||
// Intel 8257x manuals say an additional interrupt disable is needed after a global reset
|
||||
regs.disable_interrupts();
|
||||
regs.set_link_up()?;
|
||||
@ -390,24 +188,20 @@ pci_driver! {
|
||||
.config_space
|
||||
.bar(0).ok_or(Error::InvalidArgument)?;
|
||||
|
||||
let mut command = info.config_space.command();
|
||||
command |= (PciCommandRegister::BUS_MASTER | PciCommandRegister::DISABLE_INTERRUPTS).bits();
|
||||
match base {
|
||||
let use_mmio = match base {
|
||||
PciBaseAddress::Memory32(_) | PciBaseAddress::Memory64(_) => {
|
||||
command |= PciCommandRegister::ENABLE_MEMORY.bits();
|
||||
command &= !PciCommandRegister::ENABLE_IO.bits();
|
||||
true
|
||||
}
|
||||
PciBaseAddress::Io(_) => {
|
||||
command |= PciCommandRegister::ENABLE_IO.bits();
|
||||
command &= !PciCommandRegister::ENABLE_MEMORY.bits();
|
||||
false
|
||||
}
|
||||
}
|
||||
info.config_space.set_command(command);
|
||||
};
|
||||
|
||||
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||
info.set_command(true, use_mmio, !use_mmio, true);
|
||||
|
||||
let regs = unsafe { Regs::map(base) }?;
|
||||
let device = Igbe::new(dma.clone(), base, regs, info.clone());
|
||||
let device = Igbe::new(dma.clone(), regs, info.clone());
|
||||
Ok(Arc::new(device))
|
||||
}
|
||||
|
||||
|
@ -1,26 +1,35 @@
|
||||
#![allow(non_snake_case)]
|
||||
use core::{cell::UnsafeCell, marker::PhantomData, time};
|
||||
use core::time::Duration;
|
||||
|
||||
use libk::error::Error;
|
||||
use libk::{
|
||||
error::Error,
|
||||
task::runtime::{psleep, pwait},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping};
|
||||
use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy, RegisterLongName};
|
||||
use ygg_driver_net_core::ephy::{MdioBus, PhyAccess, GBESR};
|
||||
use ygg_driver_pci::PciBaseAddress;
|
||||
use yggdrasil_abi::net::{
|
||||
link::{Duplex, EthernetLinkState, EthernetSpeed},
|
||||
MacAddress,
|
||||
};
|
||||
use TXDCTL::LWTHRESH;
|
||||
|
||||
use crate::{RxRing, TxRing};
|
||||
|
||||
enum Inner {
|
||||
Memory(RawDeviceMemoryMapping),
|
||||
// TODO I/O register access not yet implemented
|
||||
#[allow(unused)]
|
||||
Io(u16),
|
||||
}
|
||||
|
||||
enum Eeprom {
|
||||
// TODO not yet handled
|
||||
#[allow(unused)]
|
||||
Present,
|
||||
MappedMemory(RawDeviceMemoryMapping),
|
||||
// TODO I/O register access not yet implemented
|
||||
#[allow(unused)]
|
||||
MappedIo(u16),
|
||||
}
|
||||
|
||||
@ -29,17 +38,6 @@ pub struct Regs {
|
||||
eeprom: Eeprom,
|
||||
}
|
||||
|
||||
// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
// #[repr(u16)]
|
||||
// pub enum Register {
|
||||
// CTRL = 0x0000,
|
||||
// STATUS = 0x0008,
|
||||
// EEPROM = 0x0014,
|
||||
// IMS = 0x00D0,
|
||||
//
|
||||
// RCTL = 0x0100,
|
||||
// }
|
||||
|
||||
pub trait Reg {
|
||||
const OFFSET: u16;
|
||||
}
|
||||
@ -241,95 +239,139 @@ impl Inner {
|
||||
}
|
||||
};
|
||||
|
||||
RawDeviceMemoryMapping::map(memory_base.into_u64(), 0x4000, Default::default())
|
||||
.map(Self::Memory)
|
||||
unsafe {
|
||||
RawDeviceMemoryMapping::map(memory_base.into_u64(), 0x4000, Default::default())
|
||||
.map(Self::Memory)
|
||||
}
|
||||
}
|
||||
|
||||
fn get<R: Reg>(&mut self) -> u32 {
|
||||
// let reg = reg as u16;
|
||||
fn get<R: Reg>(&self) -> u32 {
|
||||
match self {
|
||||
Self::Memory(mapping) => {
|
||||
let ptr: *const u32 =
|
||||
core::ptr::with_exposed_provenance(mapping.address + R::OFFSET as usize);
|
||||
unsafe { ptr.read_volatile() }
|
||||
}
|
||||
Self::Io(io) => todo!(),
|
||||
Self::Io(_io_base) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extract<R: Reg + RegisterLongName>(&mut self) -> LocalRegisterCopy<u32, R> {
|
||||
fn extract<R: Reg + RegisterLongName>(&self) -> LocalRegisterCopy<u32, R> {
|
||||
LocalRegisterCopy::new(self.get::<R>())
|
||||
}
|
||||
|
||||
fn write<R: Reg + RegisterLongName>(&mut self, value: FieldValue<u32, R>) {
|
||||
fn write<R: Reg + RegisterLongName>(&self, value: FieldValue<u32, R>) {
|
||||
self.set::<R>(value.value);
|
||||
}
|
||||
|
||||
fn set<R: Reg>(&mut self, value: u32) {
|
||||
// log::info!("write({:#x}, {:#x})", R::OFFSET, value);
|
||||
fn set<R: Reg>(&self, value: u32) {
|
||||
match self {
|
||||
Self::Memory(mapping) => {
|
||||
let ptr: *mut u32 =
|
||||
core::ptr::with_exposed_provenance_mut(mapping.address + R::OFFSET as usize);
|
||||
unsafe { ptr.write_volatile(value) };
|
||||
}
|
||||
Self::Io(io) => todo!(),
|
||||
Self::Io(_io_base) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn modify<R: Reg + RegisterLongName>(&mut self, modify: FieldValue<u32, R>) {
|
||||
fn modify<R: Reg + RegisterLongName>(&self, modify: FieldValue<u32, R>) {
|
||||
let mut value = self.get::<R>();
|
||||
value &= !modify.mask();
|
||||
value |= modify.value;
|
||||
self.set::<R>(value);
|
||||
}
|
||||
|
||||
fn matches_all<R: Reg + RegisterLongName>(&mut self, pattern: FieldValue<u32, R>) -> bool {
|
||||
fn matches_all<R: Reg + RegisterLongName>(&self, pattern: FieldValue<u32, R>) -> bool {
|
||||
pattern.matches_all(self.get::<R>())
|
||||
}
|
||||
|
||||
// fn detect_eeprom(&mut self, mut timeout_cycles: u64) -> bool {
|
||||
// self.write(Register::EEPROM, 0x01);
|
||||
// while timeout_cycles > 0 && self.read(Register::EEPROM) & 0x10 == 0 {
|
||||
// core::hint::spin_loop();
|
||||
// timeout_cycles -= 1;
|
||||
// }
|
||||
// timeout_cycles > 0
|
||||
// }
|
||||
fn matches_any<R: Reg + RegisterLongName>(&self, pattern: &[FieldValue<u32, R>]) -> bool {
|
||||
let value = self.get::<R>();
|
||||
for pattern in pattern {
|
||||
if pattern.matches_all(value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Eeprom {
|
||||
fn read(&mut self, inner: &mut Inner, addr: u8) -> u16 {
|
||||
fn read(&self, _inner: &mut Inner, addr: u8) -> u16 {
|
||||
match self {
|
||||
Self::Present => todo!(),
|
||||
Self::MappedMemory(mapping) => {
|
||||
// EEPROM registers are read in words, not bytes/halves
|
||||
let shift = (addr as usize & 1) << 4;
|
||||
let reg = (addr as usize & !1) << 1;
|
||||
let ptr: *const u32 = core::ptr::with_exposed_provenance(mapping.address + reg);
|
||||
// This is safe, region is mapped as device memory
|
||||
let word = unsafe { ptr.read_volatile() };
|
||||
(word >> shift) as u16
|
||||
}
|
||||
&mut Self::MappedIo(io_base) => todo!(),
|
||||
&Self::MappedIo(_io_base) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MdioBus for Regs {
|
||||
fn mii_read(&self, phyaddr: u8, regaddr: u8) -> Result<u16, Error> {
|
||||
self.inner.write(
|
||||
MDIC::PHYADD.val(phyaddr as u32) + MDIC::REGADD.val(regaddr as u32) + MDIC::OP::Read,
|
||||
);
|
||||
|
||||
pwait(
|
||||
Duration::from_millis(100),
|
||||
Duration::from_millis(10),
|
||||
|| self.inner.matches_any(&[MDIC::R::SET, MDIC::E::SET]),
|
||||
)?;
|
||||
|
||||
let mdic = self.inner.extract();
|
||||
if mdic.matches_all(MDIC::E::SET) {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
Ok(mdic.read(MDIC::DATA) as u16)
|
||||
}
|
||||
|
||||
fn mii_write(&self, phyaddr: u8, regaddr: u8, value: u16) -> Result<(), Error> {
|
||||
self.inner.write(
|
||||
MDIC::PHYADD.val(phyaddr as u32)
|
||||
+ MDIC::REGADD.val(regaddr as u32)
|
||||
+ MDIC::DATA.val(value as u32)
|
||||
+ MDIC::OP::Write,
|
||||
);
|
||||
|
||||
pwait(
|
||||
Duration::from_millis(100),
|
||||
Duration::from_millis(10),
|
||||
|| self.inner.matches_any(&[MDIC::R::SET, MDIC::E::SET]),
|
||||
)?;
|
||||
|
||||
if self.inner.matches_all(MDIC::E::SET) {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
pub unsafe fn map(bar: PciBaseAddress) -> Result<Self, Error> {
|
||||
let mut inner = Inner::map(bar)?;
|
||||
// let eeprom = if inner.detect_eeprom(10000) {
|
||||
// Eeprom::Present
|
||||
// } else {
|
||||
let inner = unsafe { Inner::map(bar) }?;
|
||||
|
||||
// Map EEPROM registers
|
||||
let eeprom = match &inner {
|
||||
Inner::Memory(mapping) => {
|
||||
let base = mapping.physical_base + ((mapping.address & 0xFFF) + 0x5400) as u64;
|
||||
let mapping = RawDeviceMemoryMapping::map(base, 0x10, Default::default())?;
|
||||
let mapping =
|
||||
unsafe { RawDeviceMemoryMapping::map(base, 0x10, Default::default()) }?;
|
||||
Eeprom::MappedMemory(mapping)
|
||||
}
|
||||
&Inner::Io(io) => todo!(),
|
||||
&Inner::Io(_io_base) => todo!(),
|
||||
};
|
||||
//};
|
||||
|
||||
Ok(Self { inner, eeprom })
|
||||
}
|
||||
|
||||
@ -353,38 +395,25 @@ impl Regs {
|
||||
Ok(MacAddress::from(octets))
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, mut timeout_cycles: u64) -> Result<(), Error> {
|
||||
pub fn reset(&mut self, timeout: Duration) -> Result<(), Error> {
|
||||
self.inner.write(CTRL::RST::SET);
|
||||
|
||||
for _ in 0..10000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
while timeout_cycles > 0 && self.inner.matches_all(CTRL::RST::SET) {
|
||||
timeout_cycles -= 1;
|
||||
}
|
||||
|
||||
if timeout_cycles > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::TimedOut)
|
||||
}
|
||||
psleep(Duration::from_millis(10));
|
||||
pwait(timeout, Duration::from_millis(10), || {
|
||||
self.inner.matches_all(CTRL::RST::CLEAR)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_link_up(&mut self) -> Result<(), Error> {
|
||||
self.inner
|
||||
.modify(CTRL::SLU::SET + CTRL::RFCE::SET + CTRL::TFCE::SET);
|
||||
|
||||
// Reset the PHY
|
||||
self.mdio_write(0x00, 1 << 15, 1000)?;
|
||||
for _ in 0..1000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
// Start auto-negotiation
|
||||
self.mdio_write(0x00, (1 << 12) | (1 << 9), 1000)?;
|
||||
for _ in 0..1000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
let phy = PhyAccess::new(self, 0x01);
|
||||
|
||||
let (id0, id1) = phy.id()?;
|
||||
log::info!("PHY {:04x}:{:04x}", id0, id1);
|
||||
|
||||
phy.reset(Duration::from_millis(200))?;
|
||||
phy.setup_link(true, GBESR::empty())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -431,27 +460,6 @@ impl Regs {
|
||||
self.inner.extract()
|
||||
}
|
||||
|
||||
pub fn mdio_write(&mut self, reg: u8, value: u16, mut timeout: u64) -> Result<(), Error> {
|
||||
self.inner.write(
|
||||
MDIC::DATA.val(value as u32)
|
||||
+ MDIC::PHYADD.val(1)
|
||||
+ MDIC::REGADD.val(reg as u32)
|
||||
+ MDIC::OP::Write,
|
||||
);
|
||||
while timeout > 0 && self.inner.matches_all(MDIC::R::CLEAR + MDIC::E::CLEAR) {
|
||||
timeout -= 1;
|
||||
}
|
||||
if timeout > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::TimedOut)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mdio_read(&mut self, reg: u8, mut timeout: u64) -> Result<u16, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn initialize_receiver(&mut self, rx_ring: &RxRing) {
|
||||
let rx_queue_base = rx_ring.descriptors.bus_address().into_u64();
|
||||
self.inner.set::<RDBAL0::Register>(rx_queue_base as u32);
|
||||
|
200
kernel/driver/net/igbe/src/ring.rs
Normal file
200
kernel/driver/net/igbe/src/ring.rs
Normal file
@ -0,0 +1,200 @@
|
||||
use core::mem::{self, MaybeUninit};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use device_api::dma::DmaAllocator;
|
||||
use libk::{
|
||||
dma::{BusAddress, DmaBuffer},
|
||||
error::Error,
|
||||
};
|
||||
|
||||
pub(crate) struct RxRing {
|
||||
pub(crate) descriptors: DmaBuffer<[RxDescriptor]>,
|
||||
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
|
||||
buffer_size: usize,
|
||||
tail: u16,
|
||||
}
|
||||
|
||||
pub(crate) struct TxRing {
|
||||
pub(crate) descriptors: DmaBuffer<[TxDescriptor]>,
|
||||
buffers: Vec<Option<DmaBuffer<[u8]>>>,
|
||||
// Consumer end
|
||||
tail: u16,
|
||||
// Producer end
|
||||
head: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct RxDescriptor {
|
||||
address: BusAddress,
|
||||
length: u16,
|
||||
checksum: u16,
|
||||
status: u8,
|
||||
errors: u8,
|
||||
special: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct TxDescriptor {
|
||||
address: BusAddress,
|
||||
length: u16,
|
||||
cso: u8,
|
||||
cmd: u8,
|
||||
sta: u8,
|
||||
_0: u8,
|
||||
css: u8,
|
||||
special: u8,
|
||||
}
|
||||
|
||||
impl RxRing {
|
||||
pub fn with_capacity(
|
||||
dma: &dyn DmaAllocator,
|
||||
capacity: usize,
|
||||
buffer_size: usize,
|
||||
) -> Result<Self, Error> {
|
||||
let buffers = (0..capacity)
|
||||
.map(|_| DmaBuffer::new_uninit_slice(dma, buffer_size))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let descriptors = DmaBuffer::new_slice_with(
|
||||
dma,
|
||||
|i| RxDescriptor::new(buffers[i].bus_address(), buffer_size as u16),
|
||||
capacity,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
descriptors,
|
||||
buffers,
|
||||
tail: 0,
|
||||
buffer_size,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO move to background task/softirq to reduce amount of code run by the irq handler
|
||||
pub fn handle_rx<F: FnMut(DmaBuffer<[u8]>, usize)>(
|
||||
&mut self,
|
||||
dma: &dyn DmaAllocator,
|
||||
head: u16,
|
||||
mut handler: F,
|
||||
) -> u16 {
|
||||
let capacity = self.descriptors.len();
|
||||
while self.tail != head {
|
||||
let index = self.tail as usize;
|
||||
// Replace the buffer
|
||||
|
||||
let new_buffer = DmaBuffer::new_uninit_slice(dma, self.buffer_size).unwrap();
|
||||
let new_buffer_address = new_buffer.bus_address();
|
||||
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
|
||||
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
|
||||
|
||||
let descriptor = &mut self.descriptors[index];
|
||||
|
||||
if descriptor.errors & !1 != 0 {
|
||||
log::warn!("igbe: drop erroneous packet {:#x}", descriptor.errors);
|
||||
} else {
|
||||
let len = descriptor.length as usize;
|
||||
handler(buffer, len);
|
||||
}
|
||||
|
||||
// Replace the descriptor
|
||||
*descriptor = RxDescriptor::new(new_buffer_address, self.buffer_size as u16);
|
||||
|
||||
self.tail = (self.tail + 1) & (capacity as u16 - 1);
|
||||
}
|
||||
|
||||
(self.tail + capacity as u16 - 1) & (capacity as u16 - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl TxRing {
|
||||
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
let buffers = (0..capacity).map(|_| None).collect::<Vec<_>>();
|
||||
let descriptors = DmaBuffer::new_slice_with(dma, |_| TxDescriptor::empty(), capacity)?;
|
||||
Ok(Self {
|
||||
descriptors,
|
||||
buffers,
|
||||
head: 0,
|
||||
tail: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle_tx(&mut self, head: u16) {
|
||||
self.tail = head;
|
||||
}
|
||||
|
||||
pub fn tx_now(&mut self, buffer: DmaBuffer<[u8]>) -> Result<u16, DmaBuffer<[u8]>> {
|
||||
// Queue full
|
||||
let capacity = self.descriptors.len();
|
||||
if (self.head + 1) & (capacity as u16 - 1) == self.tail {
|
||||
log::warn!("igbe: tx queue full");
|
||||
return Err(buffer);
|
||||
}
|
||||
let index = self.head as usize;
|
||||
|
||||
let descriptor = &mut self.descriptors[index];
|
||||
// Only generate interrupts for every 1/4th of the buffer
|
||||
let quarter = capacity / 4;
|
||||
descriptor.setup_tx(
|
||||
buffer.bus_address(),
|
||||
buffer.len() as u16 - 4,
|
||||
index % quarter == quarter - 1,
|
||||
);
|
||||
self.descriptors.cache_flush_element(index, true);
|
||||
|
||||
self.buffers[index] = Some(buffer);
|
||||
|
||||
self.head = (self.head + 1) & (capacity as u16 - 1);
|
||||
Ok(self.head)
|
||||
}
|
||||
}
|
||||
|
||||
impl RxDescriptor {
|
||||
pub fn new(address: BusAddress, length: u16) -> Self {
|
||||
Self {
|
||||
address,
|
||||
length,
|
||||
checksum: 0,
|
||||
status: 0,
|
||||
errors: 0,
|
||||
special: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TxDescriptor {
|
||||
// Descriptor done
|
||||
const STA_DD: u8 = 1 << 0;
|
||||
// End of packet
|
||||
const CMD_EOP: u8 = 1 << 0;
|
||||
// Insert MAC FCS
|
||||
const CMD_IFCS: u8 = 1 << 1;
|
||||
// // Insert checksum
|
||||
// const CMD_IC: u8 = 1 << 2;
|
||||
// Report status
|
||||
const CMD_RS: u8 = 1 << 3;
|
||||
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
address: BusAddress::ZERO,
|
||||
length: 0,
|
||||
cso: 0,
|
||||
cmd: 0,
|
||||
sta: Self::STA_DD,
|
||||
_0: 0,
|
||||
css: 0,
|
||||
special: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_tx(&mut self, address: BusAddress, length: u16, ioc: bool) {
|
||||
let mut cmd = Self::CMD_EOP | Self::CMD_IFCS;
|
||||
if ioc {
|
||||
cmd |= Self::CMD_RS;
|
||||
}
|
||||
self.address = address;
|
||||
self.length = length;
|
||||
self.css = 0;
|
||||
self.cso = 0;
|
||||
self.sta = 0;
|
||||
self.cmd = cmd;
|
||||
self.special = 0;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user