• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::VirtQueue;
2 use crate::{transport::Transport, Error, Hal, Result};
3 use alloc::boxed::Box;
4 use core::convert::TryInto;
5 use core::ptr::{null_mut, NonNull};
6 use zerocopy::FromZeros;
7 
8 /// A wrapper around [`Queue`] that owns all the buffers that are passed to the queue.
9 #[derive(Debug)]
10 pub struct OwningQueue<H: Hal, const SIZE: usize, const BUFFER_SIZE: usize> {
11     queue: VirtQueue<H, SIZE>,
12     buffers: [NonNull<[u8; BUFFER_SIZE]>; SIZE],
13 }
14 
15 impl<H: Hal, const SIZE: usize, const BUFFER_SIZE: usize> OwningQueue<H, SIZE, BUFFER_SIZE> {
16     /// Constructs a new `OwningQueue` wrapping around the given `VirtQueue`.
17     ///
18     /// This will allocate `SIZE` buffers of `BUFFER_SIZE` bytes each and add them to the queue.
19     ///
20     /// The caller is responsible for notifying the device if `should_notify` returns true.
new(mut queue: VirtQueue<H, SIZE>) -> Result<Self>21     pub fn new(mut queue: VirtQueue<H, SIZE>) -> Result<Self> {
22         let mut buffers = [null_mut(); SIZE];
23         for (i, queue_buffer) in buffers.iter_mut().enumerate() {
24             let mut buffer: Box<[u8; BUFFER_SIZE]> = FromZeros::new_box_zeroed().unwrap();
25             // SAFETY: The buffer lives as long as the queue, as specified in the function safety
26             // requirement, and we don't access it until it is popped.
27             let token = unsafe { queue.add(&[], &mut [buffer.as_mut_slice()]) }?;
28             assert_eq!(i, token.into());
29             *queue_buffer = Box::into_raw(buffer);
30         }
31         let buffers = buffers.map(|ptr| NonNull::new(ptr).unwrap());
32 
33         Ok(Self { queue, buffers })
34     }
35 
36     /// Returns whether the driver should notify the device after adding a new buffer to the
37     /// virtqueue.
38     ///
39     /// This will be false if the device has supressed notifications.
should_notify(&self) -> bool40     pub fn should_notify(&self) -> bool {
41         self.queue.should_notify()
42     }
43 
44     /// Tells the device whether to send used buffer notifications.
set_dev_notify(&mut self, enable: bool)45     pub fn set_dev_notify(&mut self, enable: bool) {
46         self.queue.set_dev_notify(enable);
47     }
48 
49     /// Adds the buffer at the given index in `buffers` back to the queue.
50     ///
51     /// Automatically notifies the device if required.
52     ///
53     /// # Safety
54     ///
55     /// The buffer must not currently be in the RX queue, and no other references to it must exist
56     /// between when this method is called and when it is popped from the queue.
add_buffer_to_queue(&mut self, index: u16, transport: &mut impl Transport) -> Result57     unsafe fn add_buffer_to_queue(&mut self, index: u16, transport: &mut impl Transport) -> Result {
58         // SAFETY: The buffer lives as long as the queue, and the caller guarantees that it's not
59         // currently in the queue or referred to anywhere else until it is popped.
60         unsafe {
61             let buffer = self
62                 .buffers
63                 .get_mut(usize::from(index))
64                 .ok_or(Error::WrongToken)?
65                 .as_mut();
66             let new_token = self.queue.add(&[], &mut [buffer])?;
67             // If the RX buffer somehow gets assigned a different token, then our safety assumptions
68             // are broken and we can't safely continue to do anything with the device.
69             assert_eq!(new_token, index);
70         }
71 
72         if self.queue.should_notify() {
73             transport.notify(self.queue.queue_idx);
74         }
75 
76         Ok(())
77     }
78 
pop(&mut self) -> Result<Option<(&[u8], u16)>>79     fn pop(&mut self) -> Result<Option<(&[u8], u16)>> {
80         let Some(token) = self.queue.peek_used() else {
81             return Ok(None);
82         };
83 
84         // SAFETY: The device has told us it has finished using the buffer, and there are no other
85         // references to it.
86         let buffer = unsafe { self.buffers[usize::from(token)].as_mut() };
87         // SAFETY: We maintain a consistent mapping of tokens to buffers, so we pass the same buffer
88         // to `pop_used` as we previously passed to `add` for the token. Once we add the buffer back
89         // to the RX queue then we don't access it again until next time it is popped.
90         let len = unsafe { self.queue.pop_used(token, &[], &mut [buffer])? }
91             .try_into()
92             .unwrap();
93 
94         Ok(Some((&buffer[0..len], token)))
95     }
96 
97     /// Checks whether there are any buffers which the device has marked as used so the driver
98     /// should now process. If so, passes the first one to the `handle` function and then adds it
99     /// back to the queue.
100     ///
101     /// Returns an error if there is an error accessing the queue or `handler` returns an error.
102     /// Returns `Ok(None)` if there are no pending buffers to handle, or if `handler` returns
103     /// `Ok(None)`.
104     ///
105     /// If `handler` panics then the buffer will not be added back to the queue, so this should be
106     /// avoided.
poll<T>( &mut self, transport: &mut impl Transport, handler: impl FnOnce(&[u8]) -> Result<Option<T>>, ) -> Result<Option<T>>107     pub fn poll<T>(
108         &mut self,
109         transport: &mut impl Transport,
110         handler: impl FnOnce(&[u8]) -> Result<Option<T>>,
111     ) -> Result<Option<T>> {
112         let Some((buffer, token)) = self.pop()? else {
113             return Ok(None);
114         };
115 
116         let result = handler(buffer);
117 
118         // SAFETY: The buffer was just popped from the queue so it's not in it, and there won't be
119         // any other references until next time it's popped.
120         unsafe {
121             self.add_buffer_to_queue(token, transport)?;
122         }
123 
124         result
125     }
126 }
127 
128 // SAFETY: The `buffers` can be accessed from any thread.
129 unsafe impl<H: Hal, const SIZE: usize, const BUFFER_SIZE: usize> Send
130     for OwningQueue<H, SIZE, BUFFER_SIZE>
131 where
132     VirtQueue<H, SIZE>: Send,
133 {
134 }
135 
136 // SAFETY: An `&OwningQueue` only allows calling `should_notify`.
137 unsafe impl<H: Hal, const SIZE: usize, const BUFFER_SIZE: usize> Sync
138     for OwningQueue<H, SIZE, BUFFER_SIZE>
139 where
140     VirtQueue<H, SIZE>: Sync,
141 {
142 }
143 
144 impl<H: Hal, const SIZE: usize, const BUFFER_SIZE: usize> Drop
145     for OwningQueue<H, SIZE, BUFFER_SIZE>
146 {
drop(&mut self)147     fn drop(&mut self) {
148         for buffer in self.buffers {
149             // Safe because we obtained the buffer pointer from Box::into_raw, and it won't be used
150             // anywhere else after the queue is destroyed.
151             unsafe { drop(Box::from_raw(buffer.as_ptr())) };
152         }
153     }
154 }
155