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}