1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! Defines the receive and transmit queues that store a ring of DMA descriptors and related information.
//! 
//! Receive and transmit queues are used across all NICs to keep track of incoming and outgoing packets.
//! HW queues used by the NIC only consist of the ring of DMA descriptors.
//! The SW queues defined here hold the ring of DMA descriptors that it shares with the HW,
//! as well as other information such as the buffers received from the queues,
//! the tail register for each queue and the cpu the queue is mapped to.

#![no_std]

#[macro_use] extern crate log;
extern crate alloc;
extern crate memory;
extern crate intel_ethernet;
extern crate nic_buffers;
extern crate cpu;

use alloc::{
    vec::Vec,
    collections::VecDeque
};
use memory::{create_contiguous_mapping, BorrowedSliceMappedPages, Mutable, MMIO_FLAGS};
use intel_ethernet::descriptors::{RxDescriptor, TxDescriptor};
use nic_buffers::{ReceiveBuffer, ReceivedFrame, TransmitBuffer};
use cpu::CpuId;

/// The register trait that gives access to only those registers required for receiving a packet.
/// The Rx queue control registers can only be accessed by the physical NIC.
pub trait RxQueueRegisters {
    fn set_rdbal(&mut self, value: u32);
    fn set_rdbah(&mut self, value: u32);
    fn set_rdlen(&mut self, value: u32);
    fn set_rdh(&mut self, value: u32);
    fn set_rdt(&mut self, value: u32);
}

/// The register trait that gives access to only those registers required for sending a packet.
/// The Tx queue control registers can only be accessed by the physical NIC.
pub trait TxQueueRegisters {
    fn set_tdbal(&mut self, value: u32);
    fn set_tdbah(&mut self, value: u32);
    fn set_tdlen(&mut self, value: u32);
    fn set_tdh(&mut self, value: u32);
    fn set_tdt(&mut self, value: u32);
}

/// A struct that holds all information for one receive queue.
/// There should be one such object per queue.
pub struct RxQueue<S: RxQueueRegisters, T: RxDescriptor> {
    /// The number of the queue, stored here for our convenience.
    pub id: u8,
    /// Registers for this receive queue
    pub regs: S,
    /// Receive descriptors
    pub rx_descs: BorrowedSliceMappedPages<T, Mutable>,
    /// The number of receive descriptors in the descriptor ring
    pub num_rx_descs: u16,
    /// Current receive descriptor index
    pub rx_cur: u16,
    /// The list of rx buffers, in which the index in the vector corresponds to the index in `rx_descs`.
    /// For example, `rx_bufs_in_use[2]` is the receive buffer that will be used when `rx_descs[2]` is the current rx descriptor (rx_cur = 2).
    pub rx_bufs_in_use: Vec<ReceiveBuffer>,
    pub rx_buffer_size_bytes: u16,
    /// The queue of received Ethernet frames, ready for consumption by a higher layer.
    /// Just like a regular FIFO queue, newly-received frames are pushed onto the back
    /// and frames are popped off of the front.
    /// Each frame is represented by a `Vec<ReceiveBuffer>`, because a single frame can span multiple receive buffers.
    /// TODO: improve this? probably not the best cleanest way to expose received frames to higher layers   
    pub received_frames: VecDeque<ReceivedFrame>,
    /// The cpu which this queue is mapped to. 
    /// This in itself doesn't guarantee anything, but we use this value when setting the cpu id for interrupts and DCA.
    pub cpu_id: Option<CpuId>,
    /// Pool where `ReceiveBuffer`s are stored.
    pub rx_buffer_pool: &'static mpmc::Queue<ReceiveBuffer>,
    /// The filter id for the physical NIC filter that is set for this queue
    pub filter_num: Option<u8>
}

impl<S: RxQueueRegisters, T: RxDescriptor> RxQueue<S,T> {
    /// Polls the queue and removes all received packets from it.
    /// The received packets are stored in the receive queue's `received_frames` FIFO queue.
    pub fn poll_queue_and_store_received_packets(&mut self) -> Result<(), &'static str> {
        let mut cur = self.rx_cur as usize;
       
        let mut receive_buffers_in_frame: Vec<ReceiveBuffer> = Vec::new();
        let mut _total_packet_length: u16 = 0;

        while self.rx_descs[cur].descriptor_done() {
            // get information about the current receive buffer
            let length = self.rx_descs[cur].length();
            _total_packet_length += length as u16;
            // error!("poll_queue_and_store_received_packets {}: received descriptor of length {}", self.id, length);
            
            // Now that we are "removing" the current receive buffer from the list of receive buffers that the NIC can use,
            // (because we're saving it for higher layers to use),
            // we need to obtain a new `ReceiveBuffer` and set it up such that the NIC will use it for future receivals.
            let new_receive_buf = match self.rx_buffer_pool.pop() {
                Some(rx_buf) => rx_buf,
                None => {
                    warn!("NIC RX BUF POOL WAS EMPTY.... reallocating! This means that no task is consuming the accumulated received ethernet frames.");
                    // if the pool was empty, then we allocate a new receive buffer
                    let len = self.rx_buffer_size_bytes;
                    let (mp, phys_addr) = create_contiguous_mapping(len as usize, MMIO_FLAGS)?;
                    ReceiveBuffer::new(mp, phys_addr, len, self.rx_buffer_pool)?
                }
            };

            // actually tell the NIC about the new receive buffer, and that it's ready for use now
            self.rx_descs[cur].set_packet_address(new_receive_buf.phys_addr());

            // Swap in the new receive buffer at the index corresponding to this current rx_desc's receive buffer,
            // getting back the receive buffer that is part of the received ethernet frame
            self.rx_bufs_in_use.push(new_receive_buf);
            let mut current_rx_buf = self.rx_bufs_in_use.swap_remove(cur); 
            current_rx_buf.set_length(length as u16)?; // set the ReceiveBuffer's length to the size of the actual packet received
            receive_buffers_in_frame.push(current_rx_buf);

            // move on to the next receive buffer to see if it's ready for us to take
            self.rx_cur = (cur as u16 + 1) % self.num_rx_descs;
            self.regs.set_rdt(cur as u32); 

            if self.rx_descs[cur].end_of_packet() {
                let buffers = core::mem::take(&mut receive_buffers_in_frame);
                self.received_frames.push_back(ReceivedFrame(buffers));
            } else {
                warn!("NIC::poll_queue_and_store_received_packets(): Received multi-rxbuffer frame, this scenario not fully tested!");
            }
            self.rx_descs[cur].reset_status();
            cur = self.rx_cur as usize;
        }

        Ok(())
    }

    /// Returns the earliest received ethernet frame.
    pub fn return_frame(&mut self) -> Option<ReceivedFrame> {
        self.received_frames.pop_front()
    }
}

/// A struct that holds all information for a transmit queue. 
/// There should be one such object per queue.
pub struct TxQueue<S: TxQueueRegisters, T: TxDescriptor> {
    /// The number of the queue, stored here for our convenience.
    pub id: u8,
    /// Registers for this transmit queue
    pub regs: S,
    /// Transmit descriptors 
    pub tx_descs: BorrowedSliceMappedPages<T, Mutable>,
    /// The number of transmit descriptors in the descriptor ring
    pub num_tx_descs: u16,
    /// Current transmit descriptor index
    pub tx_cur: u16,
    /// The cpu which this queue is mapped to. 
    /// This in itself doesn't guarantee anything but we use this value when setting the cpu id for interrupts and DCA.
    pub cpu_id: Option<CpuId>,
}

impl<S: TxQueueRegisters, T: TxDescriptor> TxQueue<S,T> {
    /// Sends a packet on the transmit queue
    /// 
    /// # Arguments:
    /// * `transmit_buffer`: buffer containing the packet to be sent
    pub fn send_on_queue(&mut self, transmit_buffer: TransmitBuffer) {
        self.tx_descs[self.tx_cur as usize].send(transmit_buffer.phys_addr(), transmit_buffer.length());
        // update the tx_cur value to hold the next free descriptor
        let old_cur = self.tx_cur;
        self.tx_cur = (self.tx_cur + 1) % self.num_tx_descs;
        // update the tdt register by 1 so that it knows the previous descriptor has been used
        // and has a packet to be sent
        self.regs.set_tdt(self.tx_cur as u32);
        // Wait for the packet to be sent
        self.tx_descs[old_cur as usize].wait_for_packet_tx();
    }
}