Skip to main content

bao1x_api/
iox.rs

1#[cfg(feature = "std")]
2use core::sync::atomic::{AtomicU32, Ordering};
3#[cfg(feature = "std")]
4static REFCOUNT: AtomicU32 = AtomicU32::new(0);
5
6#[cfg(feature = "std")]
7use num_traits::ToPrimitive;
8
9#[cfg(feature = "std")]
10use super::*;
11
12#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
13#[derive(Debug, Copy, Clone, num_derive::FromPrimitive, num_derive::ToPrimitive)]
14#[repr(u32)]
15pub enum IoxPort {
16    PA = 0,
17    PB = 1,
18    PC = 2,
19    PD = 3,
20    PE = 4,
21    PF = 5,
22}
23
24#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
25#[derive(Debug)]
26#[repr(u32)]
27pub enum IoxFunction {
28    Gpio = 0b00,
29    AF1 = 0b01,
30    AF2 = 0b10,
31    AF3 = 0b11,
32}
33
34#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
35#[derive(Debug)]
36#[repr(u32)]
37pub enum IoxDriveStrength {
38    Drive2mA = 0b00,
39    Drive4mA = 0b01,
40    Drive8mA = 0b10,
41    Drive12mA = 0b11,
42}
43
44#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
45#[derive(Debug)]
46#[repr(u32)]
47pub enum IoxDir {
48    Input = 0,
49    Output = 1,
50}
51
52#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
53#[derive(Debug)]
54#[repr(u32)]
55pub enum IoxEnable {
56    Disable = 0,
57    Enable = 1,
58}
59
60#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
61#[derive(Debug, Copy, Clone, PartialEq, Eq)]
62#[repr(u32)]
63pub enum IoxValue {
64    Low = 0,
65    High = 1,
66}
67/// The From trait for IoxValue takes any non-zero value and interprets it as "high".
68impl From<u32> for IoxValue {
69    fn from(value: u32) -> Self { if value == 0 { IoxValue::Low } else { IoxValue::High } }
70}
71
72/// Use a trait that will allow us to share code between both `std` and `no-std` implementations
73pub trait IoSetup {
74    fn setup_pin(
75        &self,
76        port: IoxPort,
77        pin: u8,
78        direction: Option<IoxDir>,
79        function: Option<IoxFunction>,
80        schmitt_trigger: Option<IoxEnable>,
81        pullup: Option<IoxEnable>,
82        slow_slew: Option<IoxEnable>,
83        strength: Option<IoxDriveStrength>,
84    );
85    fn set_bio_bit_from_port_and_pin(&self, port: IoxPort, pin: u8) -> Option<u8>;
86    fn set_ports_from_bio_bitmask(&self, enable_bitmask: u32, io_mode: crate::bio::IoConfigMode);
87}
88
89/// Traits for accessing GPIOs after the port has been set up.
90pub trait IoGpio {
91    fn set_gpio_pin_value(&self, port: IoxPort, pin: u8, value: IoxValue);
92    fn get_gpio_pin_value(&self, port: IoxPort, pin: u8) -> IoxValue;
93    fn set_gpio_pin_dir(&self, port: IoxPort, pin: u8, dir: IoxDir);
94}
95
96pub trait IoIrq {
97    /// This hooks a given port/pin to generate a message to the server specified
98    /// with `server` and the opcode number `usize` when an IRQ is detected on the port/pin.
99    /// The active state of the IRQ is defined by `active`; the transition edge from inactive
100    /// to active is when the event is generated.
101    fn set_irq_pin(&self, port: IoxPort, pin: u8, active: IoxValue, server: &str, opcode: usize);
102}
103
104#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
105#[derive(Debug)]
106pub struct IoxConfigMessage {
107    pub port: IoxPort,
108    pub pin: u8,
109    pub direction: Option<IoxDir>,
110    pub function: Option<IoxFunction>,
111    pub schmitt_trigger: Option<IoxEnable>,
112    pub pullup: Option<IoxEnable>,
113    pub slow_slew: Option<IoxEnable>,
114    pub strength: Option<IoxDriveStrength>,
115}
116
117#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
118#[derive(Debug)]
119#[cfg(feature = "std")]
120pub struct IoxIrqRegistration {
121    pub server: String,
122    pub opcode: usize,
123    pub port: IoxPort,
124    pub pin: u8,
125    pub active: IoxValue,
126}
127
128#[cfg(feature = "std")]
129pub struct IoxHal {
130    conn: xous::CID,
131}
132
133#[cfg(feature = "std")]
134impl IoxHal {
135    pub fn new() -> Self {
136        REFCOUNT.fetch_add(1, Ordering::Relaxed);
137        let xns = xous_names::XousNames::new().unwrap();
138        let conn =
139            xns.request_connection(SERVER_NAME_BAO1X_HAL).expect("Couldn't connect to bao1x HAL server");
140        IoxHal { conn }
141    }
142
143    pub fn set_gpio_pin_value(&self, port: IoxPort, pin: u8, value: IoxValue) {
144        xous::send_message(
145            self.conn,
146            xous::Message::new_blocking_scalar(
147                HalOpcode::SetGpioBank.to_usize().unwrap(),
148                port as usize,
149                // values to set
150                (value as usize) << (pin as usize),
151                // which pin, as a bitmask
152                1 << pin as usize,
153                0,
154            ),
155        )
156        .expect("Couldn't set GPIO pin value");
157    }
158
159    pub fn set_gpio_bank_value(&self, port: IoxPort, value: u16, bitmask: u16) {
160        xous::send_message(
161            self.conn,
162            xous::Message::new_blocking_scalar(
163                HalOpcode::SetGpioBank.to_usize().unwrap(),
164                port as usize,
165                // values to set
166                value as usize,
167                // mask of valid bits
168                bitmask as usize,
169                0,
170            ),
171        )
172        .expect("Couldn't set GPIO pin value");
173    }
174
175    pub fn get_gpio_pin_value(&self, port: IoxPort, pin: u8) -> IoxValue {
176        match xous::send_message(
177            self.conn,
178            xous::Message::new_blocking_scalar(
179                HalOpcode::GetGpioBank.to_usize().unwrap(),
180                port as usize,
181                0,
182                0,
183                0,
184            ),
185        ) {
186            Ok(xous::Result::Scalar5(_, value, _, _, _)) => {
187                if value & (1 << pin as usize) != 0 {
188                    IoxValue::High
189                } else {
190                    IoxValue::Low
191                }
192            }
193            _ => panic!("Internal Error: Couldn't get GPIO pin value"),
194        }
195    }
196
197    pub fn get_gpio_bank_value(&self, port: IoxPort) -> u32 {
198        match xous::send_message(
199            self.conn,
200            xous::Message::new_blocking_scalar(
201                HalOpcode::GetGpioBank.to_usize().unwrap(),
202                port as usize,
203                0,
204                0,
205                0,
206            ),
207        ) {
208            Ok(xous::Result::Scalar5(_, value, _, _, _)) => value as u32,
209            _ => panic!("Internal Error: Couldn't get GPIO pin value"),
210        }
211    }
212
213    /// This function takes a 32-bit bitmask, corresponding to BIO 31 through 0, where
214    /// a `1` indicates to map that BIO to a GPIO.
215    ///
216    /// This function will automatically remap the AF and BIO settings for the BIO pins
217    /// specified in the bitmask, corresponding to the BIO GPIO pin number. If a `0` is
218    /// present in a bit position, it will turn off the BIO mux, but not change the AF setting.
219    ///
220    /// VERY IMPORTANT: Note that the BIO GPIO number is *not* consistent with the
221    /// numbering order of the GPIO ports: in fact, it is reverse-order for PORT B and in-order with skips for
222    /// PORT C. Also, bits 22, 27, 30 and 31 are not mappable for the BIO.
223    ///
224    /// Returns: a 32-entry array which records which GPIO bank and pin number was affected
225    /// by the mapping request. The index of the array corresponds to the bit position in
226    /// the bitmask. You may use this to pass as arguments to further functions
227    /// that do things like control slew rate or apply pull-ups.
228    ///
229    /// Note: IoConfigMode is only lightly tested. ClearOnly mode in particular could have bugs.
230    pub fn set_ports_from_bio_bitmask(&self, enable_bitmask: u32, io_mode: crate::bio::IoConfigMode) {
231        xous::send_message(
232            self.conn,
233            xous::Message::new_blocking_scalar(
234                HalOpcode::ConfigureBio.to_usize().unwrap(),
235                enable_bitmask as usize,
236                0,
237                1,
238                io_mode as usize,
239            ),
240        )
241        .expect("Internal error setting up BIO");
242    }
243
244    /// Returns the BIO bit that was disabled based on the port and pin specifier given;
245    /// returns `None` if the proposed mapping is invalid. Does not change the AF mapping,
246    /// simply disables the bit in the BIO mux register.
247    pub fn unset_bio_bit_from_port_and_pin(&self, _port: IoxPort, _pin: u8) -> Option<u8> {
248        todo!("Do this when we get around to filling in the BIO drivers")
249    }
250}
251
252#[cfg(feature = "std")]
253impl IoSetup for IoxHal {
254    fn setup_pin(
255        &self,
256        port: IoxPort,
257        pin: u8,
258        direction: Option<IoxDir>,
259        function: Option<IoxFunction>,
260        schmitt_trigger: Option<IoxEnable>,
261        pullup: Option<IoxEnable>,
262        slow_slew: Option<IoxEnable>,
263        strength: Option<IoxDriveStrength>,
264    ) {
265        let msg =
266            IoxConfigMessage { port, pin, direction, function, schmitt_trigger, pullup, slow_slew, strength };
267        let buf = xous_ipc::Buffer::into_buf(msg).unwrap();
268        buf.lend(self.conn, HalOpcode::ConfigureIox as u32).expect("Couldn't set up IO");
269    }
270
271    fn set_bio_bit_from_port_and_pin(&self, port: IoxPort, pin: u8) -> Option<u8> {
272        match xous::send_message(
273            self.conn,
274            xous::Message::new_blocking_scalar(
275                HalOpcode::ConfigureBio.to_usize().unwrap(),
276                port as usize,
277                pin as usize,
278                0,
279                0,
280            ),
281        ) {
282            Ok(xous::Result::Scalar5(_, code, ok, _, _)) => {
283                if ok != 0 {
284                    Some(code as u8)
285                } else {
286                    None
287                }
288            }
289            _ => panic!("Internal error setting up BIO"),
290        }
291    }
292
293    fn set_ports_from_bio_bitmask(&self, enable_bitmask: u32, io_mode: crate::bio::IoConfigMode) {
294        xous::send_message(
295            self.conn,
296            xous::Message::new_blocking_scalar(
297                HalOpcode::ConfigureBio.to_usize().unwrap(),
298                enable_bitmask as usize,
299                0,
300                1,
301                io_mode as usize,
302            ),
303        )
304        .expect("Internal error setting up BIO");
305    }
306}
307
308#[cfg(feature = "std")]
309impl IoGpio for IoxHal {
310    fn get_gpio_pin_value(&self, port: IoxPort, pin: u8) -> IoxValue { self.get_gpio_pin_value(port, pin) }
311
312    fn set_gpio_pin_dir(&self, port: IoxPort, pin: u8, dir: IoxDir) {
313        let msg = IoxConfigMessage {
314            port,
315            pin,
316            direction: Some(dir),
317            function: None,
318            schmitt_trigger: None,
319            pullup: None,
320            slow_slew: None,
321            strength: None,
322        };
323        let buf = xous_ipc::Buffer::into_buf(msg).unwrap();
324        buf.lend(self.conn, HalOpcode::ConfigureIox as u32).expect("Couldn't set up IO");
325    }
326
327    fn set_gpio_pin_value(&self, port: IoxPort, pin: u8, value: IoxValue) {
328        self.set_gpio_pin_value(port, pin, value);
329    }
330}
331
332#[cfg(feature = "std")]
333impl Drop for IoxHal {
334    fn drop(&mut self) {
335        // de-allocate myself. It's unsafe because we are responsible to make sure nobody else is using the
336        // connection.
337        if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
338            unsafe {
339                xous::disconnect(self.conn).ok();
340            }
341        }
342    }
343}
344
345#[cfg(feature = "std")]
346impl IoIrq for IoxHal {
347    fn set_irq_pin(&self, port: IoxPort, pin: u8, active: IoxValue, server: &str, opcode: usize) {
348        let msg = IoxIrqRegistration { server: server.to_owned(), opcode, port, pin, active };
349        let buf = xous_ipc::Buffer::into_buf(msg).unwrap();
350        buf.lend(self.conn, HalOpcode::ConfigureIoxIrq as u32).expect("Couldn't set up IRQ");
351    }
352}