capsules_extra/wifi/
device.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright OxidOS Automotive 2025.
4
5//! Hardware-independent interface for WiFi devices.
6//!
7//! This interface provides high-level functionalities: scanning for networks, joining a network,
8//! configure as access point, get MAC address.
9
10use core::num::NonZeroU8;
11use enum_primitive::cast::FromPrimitive;
12use enum_primitive::enum_from_primitive;
13use kernel::ErrorCode;
14
15/// Maximum lengths for buffers
16pub mod len {
17    pub const SSID: usize = 32;
18
19    pub const WPA_PASSPHRASE_MIN: usize = 8;
20    pub const WPA_PASSPHRASE_MAX: usize = 63;
21}
22
23enum_from_primitive!(
24    /// Security method
25    #[derive(Copy, Clone, Debug)]
26    #[non_exhaustive]
27    pub enum Security {
28        WpaPsk = 1,
29        Wpa2Psk = 2,
30        Wpa2PskWpa3Sae = 3,
31        Wpa3Sae = 4,
32    }
33);
34
35pub type Ssid = Credential<{ len::SSID }>;
36pub type Passphrase = Credential<{ len::WPA_PASSPHRASE_MAX }>;
37
38#[derive(Clone, Copy, Debug)]
39pub struct Credential<const LEN: usize> {
40    pub len: NonZeroU8,
41    pub buf: [u8; LEN],
42}
43
44impl<const LEN: usize> Credential<LEN> {
45    pub fn try_new(len: u8) -> Result<Self, ErrorCode> {
46        if len as usize > LEN {
47            return Err(ErrorCode::INVAL);
48        }
49        let len = NonZeroU8::new(len).ok_or(ErrorCode::INVAL)?;
50
51        Ok(Self {
52            len,
53            buf: [0u8; LEN],
54        })
55    }
56}
57
58/// Client trait
59pub trait Client {
60    /// Command is complete. This is an universal callback method
61    /// for all the [`Device`] methods except `set_client` and `mac` which
62    /// are expected to be synchronous.
63    ///
64    /// ## Arguments
65    ///
66    /// - `rval`: Status of the command. `Ok(())` means that it has been completed
67    /// successfully. If the command has failed, it will provide an `ErrorCode` to
68    /// signal the motive.
69    ///
70    /// TODO: This maybe should be split between commands. I haven't figured it out yet
71    /// on the CYW4343x but we might be able to retrieve from the event status code
72    /// why the join failed, for example.
73    fn command_done(&self, rval: Result<(), ErrorCode>);
74
75    /// Scanned a network
76    ///
77    /// ## Arguments
78    ///
79    /// - `ssid`: The SSID of the scanned network
80    ///
81    /// TODO: Also add rssi
82    fn scanned_network(&self, ssid: Ssid);
83
84    /// The device finished scanning on its own
85    fn scan_done(&self);
86}
87
88pub trait Device<'a> {
89    /// Set client
90    fn set_client(&self, client: &'a dyn Client);
91
92    /// Initialize the device
93    fn init(&self) -> Result<(), ErrorCode>;
94
95    /// Return the device's MAC address
96    fn mac(&self) -> Result<[u8; 6], ErrorCode>;
97
98    /// Configure the device as access point (AP)
99    ///
100    /// ## Arguments
101    ///
102    /// - `ssid`: The SSID of the configured network
103    /// - `security`: Security method used by the network:
104    ///     - `None` if the network is open
105    ///     - tuple of the security method and the passphrase buffer
106    /// - `channel`: 802.11 WLAN channel number
107    fn access_point(
108        &self,
109        ssid: Ssid,
110        security: Option<(Security, Passphrase)>,
111        channel: u8,
112    ) -> Result<(), ErrorCode>;
113
114    /// Configure the device as station (STA)
115    fn station(&self) -> Result<(), ErrorCode>;
116
117    /// Join a network, either open or protected
118    ///
119    /// ## Arguments
120    ///
121    /// - `ssid`: WiFi network SSID
122    /// - `security`: Security method to use in order to join the network:
123    ///     - `None` if the network is open
124    ///     - tuple of the security method and the passphrase buffer
125    fn join(&self, ssid: Ssid, security: Option<(Security, Passphrase)>) -> Result<(), ErrorCode>;
126
127    /// Disconnect from the current network
128    fn leave(&self) -> Result<(), ErrorCode>;
129
130    /// Start scanning the available WiFi networks
131    fn scan(&self) -> Result<(), ErrorCode>;
132
133    /// Try to force the device to stop scanning
134    fn stop_scan(&self) -> Result<(), ErrorCode>;
135}