capsules_extra/cyw4343/
driver.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//! Driver for CYW4343x
6//!
7//! The driver handles encoding/decoding SDPCM protocol packets:
8//! - CDC = control packets
9//! - BDC = data/Ethernet packets
10//!
11//! Control packets are chained to implement WiFi functionalities (see `tasks`) and data packets
12//! are used to implement the Ethernet interface.
13
14use super::macros::reset_and_restore_bufs;
15use super::{bus, constants, sdpcm};
16use crate::wifi;
17use core::cell::Cell;
18use core::iter::{Enumerate, Peekable};
19use core::slice::Chunks;
20use enum_primitive::cast::FromPrimitive;
21use kernel::hil::time::ConvertTicks;
22use kernel::utilities::cells::{MapCell, OptionalCell};
23use kernel::utilities::leasable_buffer::SubSliceMut;
24use kernel::{hil, ErrorCode};
25
26/// Current state of the CYW43x device driver
27#[derive(Clone, Copy, Debug, Default)]
28enum State {
29    #[default]
30    Idle,
31    NotInit,
32    PoweredDown,
33    PoweredUp,
34    BusInit,
35    Command(Command),
36    Ethernet,
37}
38
39#[derive(Clone, Copy, Debug)]
40enum Command {
41    Init,
42    Join,
43    Leave,
44    Scan,
45    StopScan,
46    Ap,
47    Sta,
48}
49
50/// Possible pending IOCTLs
51#[derive(Clone, Copy)]
52enum Pending {
53    Scan,
54    MacAddr,
55}
56
57/// CYW4343x device driver
58pub struct CYW4343x<'a, P: hil::gpio::Pin, A: hil::time::Alarm<'a>, B: bus::CYW4343xBus<'a>> {
59    /// Alarm used for setting delays to wait some
60    /// operations to take effect
61    alarm: &'a A,
62    /// Data bus (gSPI/SDIO)
63    bus: &'a B,
64    /// Power pin
65    pwr: &'a P,
66    /// Current driver state
67    state: Cell<State>,
68    /// Inner buffer for constructing WLAN packets
69    buffer: OptionalCell<SubSliceMut<'static, u8>>,
70    /// Wifi device client
71    client: OptionalCell<&'a dyn wifi::Client>,
72    /// Ethernet driver client
73    eth_client: OptionalCell<&'a dyn hil::ethernet::EthernetAdapterDatapathClient>,
74    /// Sequence number, part of the SDPCM header
75    sdpcm_seq: Cell<u8>,
76    /// Unique id for rx/tx pair, part of the CDC header
77    id: Cell<u16>,
78    /// Ethernet receive enable
79    receive: Cell<bool>,
80    /// Ethernet transmission identifier and buffer
81    eth_tx_data: OptionalCell<(usize, &'static mut [u8])>,
82    /// Reference to the current IOCTL task list and index
83    ioctl_tasks: ioctl::Tasks,
84    /// CLM chunk index
85    clm: MapCell<Peekable<Enumerate<Chunks<'static, u8>>>>,
86    /// SSID
87    ssid: OptionalCell<wifi::Ssid>,
88    /// Security passphrase
89    security: OptionalCell<wifi::Passphrase>,
90    /// Wifi channel
91    channel: OptionalCell<u8>,
92    /// Current async operation (the chip may send a SDPCM response packet anytime)
93    pending: OptionalCell<Pending>,
94    /// MAC address
95    mac: OptionalCell<[u8; 6]>,
96}
97
98impl<'a, P: hil::gpio::Pin, A: hil::time::Alarm<'a>, B: bus::CYW4343xBus<'a>>
99    CYW4343x<'a, P, A, B>
100{
101    pub fn new(
102        alarm: &'a A,
103        bus: &'a B,
104        pwr: &'a P,
105        clm: &'static [u8],
106        buffer: &'static mut [u8; 1600],
107    ) -> Self {
108        Self {
109            alarm,
110            bus,
111            pwr,
112            state: Cell::new(State::NotInit),
113            client: OptionalCell::empty(),
114            eth_client: OptionalCell::empty(),
115            sdpcm_seq: Cell::new(0),
116            id: Cell::new(0),
117            receive: Cell::new(false),
118            ssid: OptionalCell::empty(),
119            security: OptionalCell::empty(),
120            channel: OptionalCell::empty(),
121            ioctl_tasks: ioctl::Tasks::new(),
122            eth_tx_data: OptionalCell::empty(),
123            pending: OptionalCell::empty(),
124            clm: MapCell::new(clm.chunks(constants::CLM_CHUNK_SIZE).enumerate().peekable()),
125            buffer: OptionalCell::new(SubSliceMut::new(buffer)),
126            mac: OptionalCell::empty(),
127        }
128    }
129
130    /// Send BDC (Bulk Data) packet
131    fn send_bdc(&self, data: &[u8]) -> Result<(), ErrorCode> {
132        let Some(mut buffer) = self.buffer.take() else {
133            return Err(ErrorCode::NOMEM);
134        };
135
136        let total_len = sdpcm::SdpcmHeader::SIZE
137            + constants::BDC_PADDING_SIZE
138            + sdpcm::BdcHeader::SIZE
139            + data.len();
140
141        let seq = self.sdpcm_seq.get();
142        self.sdpcm_seq.set(seq.wrapping_add(1));
143
144        let sdpcm_header = sdpcm::SdpcmHeader {
145            len: total_len as u16,
146            len_inv: !total_len as u16,
147            seq,
148            flags: sdpcm::ChannelType::Data as _,
149            next_len: 0,
150            data_offset: (sdpcm::SdpcmHeader::SIZE + constants::BDC_PADDING_SIZE) as _,
151            flow_ctrl: 0,
152            data_credit: 0,
153            reserved: 0,
154        }
155        .into_bytes();
156
157        let bdc_header = sdpcm::BdcHeader {
158            flags: constants::BDC_VERSION << constants::BDC_VERSION_SHIFT,
159            priority: 0,
160            flags2: 0,
161            data_offset: 0,
162        }
163        .into_bytes();
164
165        buffer.slice(0..total_len);
166        let slice = buffer.as_mut_slice();
167        slice[0..sdpcm::SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header);
168        slice[sdpcm::SdpcmHeader::SIZE + constants::BDC_PADDING_SIZE..][..sdpcm::BdcHeader::SIZE]
169            .copy_from_slice(&bdc_header);
170        slice[sdpcm::SdpcmHeader::SIZE + constants::BDC_PADDING_SIZE + sdpcm::BdcHeader::SIZE..]
171            [..data.len()]
172            .copy_from_slice(data);
173
174        self.bus.write_bytes(buffer).map_err(|(err, mut buffer)| {
175            reset_and_restore_bufs!(self, buffer);
176            err
177        })
178    }
179
180    fn send_cdc(
181        &self,
182        ioctl: sdpcm::IoctlType,
183        cmd: sdpcm::IoctlCommand,
184        data: &[u8],
185    ) -> Result<(), ErrorCode> {
186        let Some(mut buffer) = self.buffer.take() else {
187            return Err(ErrorCode::NOMEM);
188        };
189
190        let total_len = sdpcm::SdpcmHeader::SIZE + sdpcm::CdcHeader::SIZE + data.len();
191
192        let seq = self.sdpcm_seq.get();
193        self.sdpcm_seq.set(seq.wrapping_add(1));
194        self.id.set(self.id.get().wrapping_add(1));
195
196        let sdpcm_header = sdpcm::SdpcmHeader {
197            len: total_len as u16,
198            len_inv: !total_len as u16,
199            seq,
200            flags: sdpcm::ChannelType::Control as u8,
201            next_len: 0,
202            data_offset: sdpcm::SdpcmHeader::SIZE as _,
203            flow_ctrl: 0,
204            data_credit: 0,
205            reserved: 0,
206        }
207        .into_bytes();
208
209        let cdc_header = sdpcm::CdcHeader {
210            cmd: cmd as u32,
211            len: data.len() as _,
212            flags: ioctl as u32 | (self.id.get() as u32) << 16,
213            status: 0,
214        }
215        .into_bytes();
216
217        buffer.slice(0..total_len);
218        let slice = buffer.as_mut_slice();
219        slice[0..sdpcm::SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header);
220        slice[sdpcm::SdpcmHeader::SIZE..][..sdpcm::CdcHeader::SIZE].copy_from_slice(&cdc_header);
221        slice[sdpcm::SdpcmHeader::SIZE + sdpcm::CdcHeader::SIZE..][..data.len()]
222            .copy_from_slice(data);
223
224        self.bus.write_bytes(buffer).map_err(|(err, mut buffer)| {
225            reset_and_restore_bufs!(self, buffer);
226            err
227        })
228    }
229}
230
231impl<'a, P: hil::gpio::Pin, A: hil::time::Alarm<'a>, B: bus::CYW4343xBus<'a>>
232    CYW4343x<'a, P, A, B>
233{
234    /// Initialize the task buffer and executes the first task
235    fn init_tasks(&self, tasks: &'static [ioctl::Op]) -> Result<(), ErrorCode> {
236        if !self.ioctl_tasks.is_empty() || self.pending.is_some() {
237            Err(ErrorCode::BUSY)
238        } else {
239            self.ioctl_tasks.init(tasks);
240            let advance = self.do_task(tasks[0])?;
241            if advance {
242                self.ioctl_tasks.advance();
243            }
244            Ok(())
245        }
246    }
247
248    /// Do a task (start a CDC transfer or set an alarm)
249    ///
250    /// This returns whether this should advance or not
251    fn do_task(&self, task: ioctl::Op) -> Result<bool, ErrorCode> {
252        let mut advance = true;
253        match task {
254            ioctl::Op::Ioctl(ioctl) => self.ioctl(ioctl),
255            ioctl::Op::LoadCLM => self
256                .clm
257                .map(|chunks| {
258                    let Some(curr) = chunks.next() else {
259                        return Err(ErrorCode::FAIL);
260                    };
261                    let next = chunks.peek();
262                    let mut flag = constants::CLM_DOWNLOAD_FLAG_HANDLER_VER;
263
264                    if next.is_none() {
265                        flag |= constants::CLM_DOWNLOAD_FLAG_END;
266                    } else {
267                        advance = false;
268                    }
269
270                    if curr.0 == 0 {
271                        flag |= constants::CLM_DOWNLOAD_FLAG_BEGIN;
272                    }
273                    let header = sdpcm::WlDloadData {
274                        flag,
275                        dload_type: constants::CLM_DOWNLOAD_TYPE,
276                        len: curr.1.len() as _,
277                        crc: 0,
278                    }
279                    .into_bytes();
280                    const IOVAR_SIZE: usize = sdpcm::Iovar::ClmLoad.len();
281                    let mut data =
282                        [0; IOVAR_SIZE + sdpcm::WlDloadData::SIZE + constants::CLM_CHUNK_SIZE];
283                    data[0..IOVAR_SIZE].copy_from_slice(sdpcm::Iovar::ClmLoad.into());
284                    data[IOVAR_SIZE..][..sdpcm::WlDloadData::SIZE].copy_from_slice(&header);
285
286                    data[IOVAR_SIZE + sdpcm::WlDloadData::SIZE..][..curr.1.len()]
287                        .copy_from_slice(curr.1);
288
289                    let total_len = IOVAR_SIZE + sdpcm::WlDloadData::SIZE + curr.1.len();
290                    self.send_cdc(
291                        sdpcm::IoctlType::Set,
292                        sdpcm::IoctlCommand::SetVar,
293                        &data[..total_len],
294                    )
295                })
296                .unwrap_or(Err(ErrorCode::FAIL)),
297            ioctl::Op::WaitMs(ms) => {
298                self.alarm
299                    .set_alarm(self.alarm.now(), self.alarm.ticks_from_ms(ms));
300                Ok(())
301            }
302            ioctl::Op::MacAddr => {
303                self.pending.set(Pending::MacAddr);
304                self.send_cdc(
305                    sdpcm::IoctlType::Get,
306                    sdpcm::IoctlCommand::GetVar,
307                    sdpcm::Iovar::CurEthAddr.into(),
308                )
309                .map(|()| self.pending.set(Pending::MacAddr))
310            }
311        }
312        .map(|()| advance)
313    }
314
315    /// Do a IOCTL operation
316    fn ioctl(&self, ioctl: ioctl::Ioctl) -> Result<(), ErrorCode> {
317        let data = match ioctl.data {
318            ioctl::IoctlData::Empty => &[],
319            ioctl::IoctlData::Word(ref bytes) => &bytes[..],
320            ioctl::IoctlData::DWord(ref bytes) => &bytes[..],
321            ioctl::IoctlData::BssSsid => &self
322                .ssid
323                .take()
324                .map(|wifi::Ssid { len, buf }| {
325                    sdpcm::SsidInfoWithIndex {
326                        idx: 0,
327                        len: len.get() as _,
328                        buf,
329                    }
330                    .into_bytes()
331                })
332                .ok_or(ErrorCode::FAIL)?,
333            ioctl::IoctlData::Ssid => &self
334                .ssid
335                .take()
336                .map(|wifi::Ssid { len, buf }| {
337                    sdpcm::SsidInfo {
338                        len: len.get() as _,
339                        buf,
340                    }
341                    .into_bytes()
342                })
343                .ok_or(ErrorCode::FAIL)?,
344            ioctl::IoctlData::Wpa1Passphrase => &self
345                .security
346                .take()
347                .map(<[u8; sdpcm::PassphraseInfo::SIZE]>::from)
348                .ok_or(ErrorCode::FAIL)?,
349            ioctl::IoctlData::Wpa3Passphrase => &self
350                .security
351                .take()
352                .map(<[u8; sdpcm::SaePassphraseInfo::SIZE]>::from)
353                .ok_or(ErrorCode::FAIL)?,
354            ioctl::IoctlData::Channel => &self
355                .channel
356                .take()
357                .map(|channel| [channel])
358                .ok_or(ErrorCode::FAIL)?,
359            ioctl::IoctlData::ScanParameters => &ioctl::start_scan::SCAN_PARAMS,
360            ioctl::IoctlData::AbortScanParameters => &ioctl::stop_scan::SCAN_PARAMS,
361            ioctl::IoctlData::CountryInfo => &ioctl::init::COUNTRY_INFO,
362            ioctl::IoctlData::EventMask => &ioctl::init::EVENTS,
363        };
364
365        if let Some(name) = ioctl.name {
366            const MAX_LEN: usize = sdpcm::SaePassphraseInfo::SIZE + sdpcm::MAX_IOVAR_LEN;
367            let len = name.len() + data.len();
368
369            let mut iovar: [u8; MAX_LEN] = [0; MAX_LEN];
370            iovar[..name.len()].copy_from_slice(name.into());
371            iovar[name.len()..][..data.len()].copy_from_slice(data);
372
373            self.send_cdc(sdpcm::IoctlType::Set, ioctl.cmd, &iovar[..len])
374        } else {
375            self.send_cdc(sdpcm::IoctlType::Set, ioctl.cmd, data)
376        }
377    }
378
379    /// Parse a slice as a WLAN packet
380    fn parse(&self, buffer: &[u8]) {
381        if buffer.len() < sdpcm::SdpcmHeader::SIZE {
382            return;
383        }
384
385        let mut header = &buffer[..sdpcm::SdpcmHeader::SIZE];
386        let sdpcm_header = sdpcm::SdpcmHeader::from_bytes(header);
387
388        let Some(channel) = sdpcm::ChannelType::from_u8(sdpcm_header.flags & 0xf) else {
389            return;
390        };
391        let mut data = &buffer[sdpcm_header.data_offset as usize..];
392
393        match channel {
394            // IOCTL response
395            sdpcm::ChannelType::Control => {
396                if data.len() < sdpcm::CdcHeader::SIZE {
397                    return;
398                }
399
400                (header, data) = data.split_at(sdpcm::CdcHeader::SIZE);
401                let cdc_header = sdpcm::CdcHeader::from_bytes(header);
402                if cdc_header.status != 0 {
403                    return;
404                }
405
406                if (cdc_header.flags >> 16) as u16 == self.id.get()
407                    && cdc_header.cmd == sdpcm::IoctlCommand::GetVar as u32
408                {
409                    if let Some(Pending::MacAddr) = self.pending.get() {
410                        let mut mac = [0u8; 6];
411                        mac[..].copy_from_slice(&data[..6]);
412                        self.mac.set(mac);
413                        self.pending.clear();
414                    }
415                }
416            }
417            // Asynchronous events
418            sdpcm::ChannelType::Event => {
419                // Events have BDC headers
420                if data.len()
421                    < sdpcm::BdcHeader::SIZE
422                        + sdpcm::EthernetHeader::SIZE
423                        + sdpcm::EventHeader::SIZE
424                        + sdpcm::EventMessage::SIZE
425                {
426                    return;
427                }
428
429                (header, data) = data.split_at(sdpcm::BdcHeader::SIZE);
430                let bdc_hdr = sdpcm::BdcHeader::from_bytes(header);
431                let offset = 4 * bdc_hdr.data_offset as usize;
432                if offset > data.len() {
433                    return;
434                }
435                data = &data[offset..];
436                (header, data) = data.split_at(sdpcm::EthernetHeader::SIZE);
437                let eth_hdr = sdpcm::EthernetHeader::from_bytes(header);
438                (header, data) = data.split_at(sdpcm::EventHeader::SIZE);
439                let event_hdr = sdpcm::EventHeader::from_bytes(header);
440
441                if eth_hdr.ethertype.to_be() != constants::ETHER_TYPE_BRCM
442                    || event_hdr.oui != constants::BRCM_OUI
443                    || event_hdr.subtype.to_be() != constants::EVT_SUBTYPE
444                {
445                    return;
446                }
447
448                (header, data) = data.split_at(sdpcm::EventMessage::SIZE);
449                let evt_msg = sdpcm::EventMessage::from_bytes(header);
450                let Some(evt_type) = sdpcm::Event::from_u8(evt_msg.event_type.to_be() as _) else {
451                    return;
452                };
453
454                const ESCAN_PARTIAL: u32 = 8;
455                match evt_type {
456                    sdpcm::Event::EscanResult if evt_msg.status.to_be() == ESCAN_PARTIAL => {
457                        let Some(Pending::Scan) = self.pending.get() else {
458                            return;
459                        };
460                        if data.len() < sdpcm::ScanResults::SIZE + sdpcm::BssInfo::SIZE {
461                            return;
462                        }
463                        data = &data[sdpcm::ScanResults::SIZE..];
464                        let bss_info = sdpcm::BssInfo::from_bytes(&data[..sdpcm::BssInfo::SIZE]);
465                        if let Ok(mut ssid) = wifi::Ssid::try_new(bss_info.ssid_len) {
466                            ssid.buf = bss_info.ssid;
467                            self.client.map(|client| client.scanned_network(ssid));
468                        }
469                    }
470                    sdpcm::Event::EscanResult => {
471                        let Some(Pending::Scan) = self.pending.get() else {
472                            return;
473                        };
474                        // TODO: Notify client that scanning is done
475                        self.client.map(|client| client.scan_done());
476                        self.pending.clear();
477                    }
478                    sdpcm::Event::SetSsid => {
479                        let State::Command(Command::Join) = self.state.get() else {
480                            return;
481                        };
482                        self.client.map(|client| {
483                            client.command_done(if evt_msg.status == 0 {
484                                Ok(())
485                            } else {
486                                Err(ErrorCode::FAIL)
487                            });
488                            self.tasks_done();
489                        });
490                    }
491                    _ => (),
492                }
493            }
494            // Data packets
495            sdpcm::ChannelType::Data if self.receive.get() => {
496                if data.len() < sdpcm::BdcHeader::SIZE {
497                    return;
498                }
499                (_, data) = data.split_at(sdpcm::BdcHeader::SIZE);
500                self.eth_client
501                    .map(|client| client.received_frame(data, None));
502            }
503            _ => (),
504        }
505    }
506
507    fn waiting_or_busy(&self) -> bool {
508        if self.pending.is_some() {
509            true
510        } else {
511            match self.bus.state().unwrap() {
512                bus::State::Incoming => true,
513                bus::State::Available(len) => {
514                    self.bus
515                        .read_bytes(self.buffer.take().unwrap(), len)
516                        .unwrap();
517                    true
518                }
519                bus::State::Idle => false,
520            }
521        }
522    }
523
524    fn tasks_done(&self) {
525        let _ = self.state.take();
526        self.ioctl_tasks.reset();
527    }
528
529    /// Do the current task and advance the task list
530    fn update_task(&self) {
531        let Some(task) = self.ioctl_tasks.get() else {
532            if let State::Command(command) = self.state.get() {
533                if let Command::Join = command {
534                    // Here we wait for the `SET_SSID` event.
535                    return;
536                } else if let Command::Scan = command {
537                    self.pending.set(Pending::Scan);
538                }
539                self.client.map(|client| client.command_done(Ok(())));
540                self.tasks_done();
541            }
542            return;
543        };
544
545        match self.do_task(*task) {
546            Ok(advance) => {
547                if advance {
548                    self.ioctl_tasks.advance();
549                }
550            }
551            Err(err) => {
552                if let State::Command(_) = self.state.take() {
553                    self.client.map(|client| client.command_done(Err(err)));
554                }
555                self.tasks_done();
556            }
557        }
558    }
559}
560
561impl<'a, P: hil::gpio::Pin, A: hil::time::Alarm<'a>, B: bus::CYW4343xBus<'a>>
562    hil::ethernet::EthernetAdapterDatapath<'a> for CYW4343x<'a, P, A, B>
563{
564    fn set_client(&self, client: &'a dyn hil::ethernet::EthernetAdapterDatapathClient) {
565        self.eth_client.set(client)
566    }
567
568    fn enable_receive(&self) {
569        self.receive.set(true);
570    }
571
572    fn disable_receive(&self) {
573        self.receive.set(false);
574    }
575
576    fn transmit_frame(
577        &self,
578        frame_buffer: &'static mut [u8],
579        len: u16,
580        transmission_identifier: usize,
581    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
582        let State::Idle = self.state.get() else {
583            return Err((ErrorCode::BUSY, frame_buffer));
584        };
585
586        if let Err(err) = self.send_bdc(&frame_buffer[..len as _]) {
587            Err((err, frame_buffer))
588        } else {
589            self.eth_tx_data
590                .set((transmission_identifier, frame_buffer));
591            self.state.set(State::Ethernet);
592            Ok(())
593        }
594    }
595}
596
597impl<'a, P: hil::gpio::Pin, A: hil::time::Alarm<'a>, B: bus::CYW4343xBus<'a>> wifi::Device<'a>
598    for CYW4343x<'a, P, A, B>
599{
600    fn set_client(&self, client: &'a dyn wifi::Client) {
601        self.client.set(client);
602    }
603
604    fn init(&self) -> Result<(), kernel::ErrorCode> {
605        let State::NotInit = self.state.get() else {
606            return Err(ErrorCode::ALREADY);
607        };
608
609        // Start the process by powering down the chip
610        self.pwr.clear();
611        let now = self.alarm.now();
612        self.alarm.set_alarm(now, self.alarm.ticks_from_ms(20));
613        self.state.set(State::PoweredDown);
614
615        Ok(())
616    }
617
618    fn mac(&self) -> Result<[u8; 6], ErrorCode> {
619        self.mac.get().ok_or(ErrorCode::BUSY)
620    }
621
622    fn join(
623        &self,
624        ssid: wifi::Ssid,
625        security: Option<(wifi::Security, wifi::Passphrase)>,
626    ) -> Result<(), kernel::ErrorCode> {
627        if let State::NotInit = self.state.get() {
628            return Err(ErrorCode::FAIL);
629        }
630
631        if let Some((security, passphrase)) = security {
632            match security {
633                wifi::Security::WpaPsk => self.init_tasks(&ioctl::join_wpa::WPA1)?,
634                wifi::Security::Wpa2Psk => self.init_tasks(&ioctl::join_wpa::WPA2)?,
635                wifi::Security::Wpa2PskWpa3Sae => {
636                    self.init_tasks(&ioctl::join_wpa::WPA2_WPA3)?;
637                }
638                wifi::Security::Wpa3Sae => self.init_tasks(&ioctl::join_wpa::WPA3)?,
639            }
640            self.security.set(passphrase);
641        } else {
642            self.init_tasks(&ioctl::join_open::OPS)?;
643        }
644        self.ssid.set(ssid);
645        self.state.set(State::Command(Command::Join));
646
647        Ok(())
648    }
649
650    fn leave(&self) -> Result<(), kernel::ErrorCode> {
651        if let State::NotInit = self.state.get() {
652            return Err(ErrorCode::FAIL);
653        }
654
655        self.init_tasks(&[ioctl::leave::OP])?;
656        self.state.set(State::Command(Command::Leave));
657        Ok(())
658    }
659
660    fn scan(&self) -> Result<(), kernel::ErrorCode> {
661        if let State::NotInit = self.state.get() {
662            return Err(ErrorCode::FAIL);
663        }
664
665        self.init_tasks(&[ioctl::start_scan::OP])?;
666        self.state.set(State::Command(Command::Scan));
667        Ok(())
668    }
669
670    fn stop_scan(&self) -> Result<(), kernel::ErrorCode> {
671        if let State::NotInit = self.state.get() {
672            return Err(ErrorCode::FAIL);
673        }
674
675        self.init_tasks(&[ioctl::stop_scan::OP])?;
676        self.state.set(State::Command(Command::StopScan));
677        Ok(())
678    }
679
680    fn access_point(
681        &self,
682        ssid: wifi::Ssid,
683        security: Option<(wifi::Security, wifi::Passphrase)>,
684        channel: u8,
685    ) -> Result<(), kernel::ErrorCode> {
686        let (None | Some((wifi::Security::Wpa2Psk, _))) = security else {
687            return Err(ErrorCode::NOSUPPORT);
688        };
689
690        if let State::NotInit = self.state.get() {
691            return Err(ErrorCode::FAIL);
692        }
693
694        if let Some((_, passphrase)) = security {
695            self.init_tasks(&ioctl::start_ap_wpa::OPS)?;
696            self.security.set(passphrase);
697        } else {
698            self.init_tasks(&ioctl::start_ap::OPS)?;
699        }
700
701        self.ssid.set(ssid);
702        self.channel.set(channel);
703        self.state.set(State::Command(Command::Ap));
704        Ok(())
705    }
706
707    fn station(&self) -> Result<(), kernel::ErrorCode> {
708        if let State::NotInit = self.state.get() {
709            return Err(ErrorCode::FAIL);
710        }
711
712        self.init_tasks(&ioctl::stop_ap::OPS)?;
713        self.state.set(State::Command(Command::Sta));
714        Ok(())
715    }
716}
717
718impl<'a, P: hil::gpio::Pin, A: hil::time::Alarm<'a>, B: bus::CYW4343xBus<'a>> hil::time::AlarmClient
719    for CYW4343x<'a, P, A, B>
720{
721    fn alarm(&self) {
722        match self.state.get() {
723            State::PoweredDown => {
724                self.pwr.set();
725
726                let now = self.alarm.now();
727                self.alarm.set_alarm(now, self.alarm.ticks_from_ms(250));
728                self.state.set(State::PoweredUp);
729            }
730            State::PoweredUp => {
731                // Now we can start initialising the bus
732                if let Err(err) = self.bus.init().map(|()| self.state.set(State::BusInit)) {
733                    self.client.map(|client| client.command_done(Err(err)));
734                }
735            }
736            State::Command(_) if !self.waiting_or_busy() => self.update_task(),
737            // The driver alarm shouldn't fire in any other state
738            _ => (),
739        }
740    }
741}
742
743impl<'a, P: hil::gpio::Pin, A: hil::time::Alarm<'a>, B: bus::CYW4343xBus<'a>> bus::CYW4343xBusClient
744    for CYW4343x<'a, P, A, B>
745{
746    fn init_done(&self, rval: Result<(), ErrorCode>) {
747        if let Err(err) = rval.and_then(|()| self.init_tasks(ioctl::init::OPS)) {
748            self.client.map(|client| client.command_done(Err(err)));
749            self.state.set(State::Idle);
750        } else {
751            self.state.set(State::Command(Command::Init));
752        }
753    }
754
755    fn packet_available(&self, len: usize) {
756        if len == 0 && !self.waiting_or_busy() {
757            self.update_task();
758        } else {
759            self.bus
760                .read_bytes(self.buffer.take().unwrap(), len)
761                .unwrap();
762        }
763    }
764
765    fn write_bytes_done(
766        &self,
767        mut buffer: SubSliceMut<'static, u8>,
768        rval: Result<(), kernel::ErrorCode>,
769    ) {
770        reset_and_restore_bufs!(self, buffer);
771
772        match (self.state.get(), rval) {
773            (State::Command(_), Err(err)) => {
774                self.client.map(|client| client.command_done(Err(err)));
775                if let State::Command(Command::Init) = self.state.get() {
776                    self.state.set(State::NotInit);
777                } else {
778                    self.state.set(State::Idle);
779                }
780            }
781            (State::Command(_), Ok(())) if !self.waiting_or_busy() => {
782                self.update_task();
783            }
784            (State::Ethernet, _) => todo!(),
785            _ => {}
786        }
787    }
788
789    fn read_bytes_done(
790        &self,
791        mut buffer: SubSliceMut<'static, u8>,
792        rval: Result<(), kernel::ErrorCode>,
793    ) {
794        if rval.is_ok() {
795            self.parse(buffer.as_mut_slice());
796        }
797
798        reset_and_restore_bufs!(self, buffer);
799        if let State::Command(_) = self.state.get() {
800            if !self.waiting_or_busy() {
801                self.update_task()
802            }
803        }
804    }
805}
806
807/// Configuring a WiFi functionality requires a set of IOCTL commands to be sent to the chip.
808/// This module defines lists of IOCTL operations needed
809/// for each functionality required by the WiFi device interface.
810mod ioctl {
811    use crate::cyw4343::sdpcm::{self, IoctlCommand};
812    use core::cell::Cell;
813    use kernel::utilities::cells::OptionalCell;
814
815    #[derive(Default)]
816    pub struct Tasks {
817        list: OptionalCell<&'static [Op]>,
818        idx: Cell<u8>,
819    }
820
821    impl Tasks {
822        pub fn new() -> Self {
823            Self::default()
824        }
825
826        pub fn init(&self, list: &'static [Op]) {
827            self.list.set(list);
828            self.idx.set(0);
829        }
830
831        pub fn reset(&self) {
832            self.list.clear();
833            self.idx.set(0);
834        }
835
836        pub fn get(&self) -> Option<&Op> {
837            self.list
838                .map(|list| list.get(self.idx.get() as usize))
839                .flatten()
840        }
841
842        pub fn advance(&self) {
843            self.idx.set(self.idx.get() + 1)
844        }
845
846        pub fn is_empty(&self) -> bool {
847            self.list.is_none()
848        }
849    }
850
851    /// Driver task
852    ///
853    /// Commands sent to the WiFi chip (join a network, start AP mode, etc.)
854    /// can be broken down as a list of IOCTL operations. In a few cases,
855    /// a delay between operations is necessary. In order to keep the tasks in lists,
856    /// the types of operations are aggregated in the [`Op`] enum.
857    #[derive(Debug, Clone, Copy)]
858    pub enum Op {
859        /// Delay between IOCTLs
860        WaitMs(u32),
861        /// IOCTL operation
862        Ioctl(Ioctl),
863        /// Load CLM
864        LoadCLM,
865        /// Get the MAC address
866        MacAddr,
867    }
868
869    impl Default for Op {
870        fn default() -> Self {
871            Self::WaitMs(0)
872        }
873    }
874
875    impl Op {
876        pub(super) const fn ioctl(cmd: IoctlCommand, data: IoctlData) -> Self {
877            Self::Ioctl(Ioctl {
878                cmd,
879                data,
880                name: None,
881            })
882        }
883
884        pub(super) const fn iovar(name: sdpcm::Iovar, data: IoctlData) -> Self {
885            Self::Ioctl(Ioctl {
886                cmd: IoctlCommand::SetVar,
887                data,
888                name: Some(name),
889            })
890        }
891
892        pub(super) const fn wait_ms(ms: u32) -> Self {
893            Self::WaitMs(ms)
894        }
895    }
896
897    /// IOCTL operation
898    #[derive(Debug, Clone, Copy)]
899    pub struct Ioctl {
900        pub cmd: IoctlCommand,
901        pub data: IoctlData,
902        pub name: Option<sdpcm::Iovar>,
903    }
904
905    /// IOCTL data sources
906    #[derive(Debug, Clone, Copy)]
907    pub enum IoctlData {
908        // Pre-configured
909        Word([u8; 4]),
910        DWord([u8; 8]),
911        Empty,
912        // These should be retrieved from the driver
913        Ssid,
914        BssSsid,
915        Wpa1Passphrase,
916        Wpa3Passphrase,
917        Channel,
918        // These are constants
919        ScanParameters,
920        AbortScanParameters,
921        CountryInfo,
922        EventMask,
923    }
924
925    impl IoctlData {
926        pub const fn from_2xu32(val0: u32, val1: u32) -> Self {
927            let mut bytes = [0u8; 8];
928            let val0_bytes = val0.to_le_bytes();
929            [bytes[0], bytes[1], bytes[2], bytes[3]] =
930                [val0_bytes[0], val0_bytes[1], val0_bytes[2], val0_bytes[3]];
931            let val1_bytes = val1.to_le_bytes();
932            [bytes[4], bytes[5], bytes[6], bytes[7]] =
933                [val1_bytes[0], val1_bytes[1], val1_bytes[2], val1_bytes[3]];
934
935            Self::DWord(bytes)
936        }
937
938        pub const fn from_u32(val: u32) -> Self {
939            Self::Word(val.to_le_bytes())
940        }
941
942        pub const fn empty() -> Self {
943            Self::Empty
944        }
945    }
946
947    //////////// Task lists
948
949    pub mod init {
950        use super::sdpcm::{CountryInfo, Event, EventMask, Iovar};
951        use super::{IoctlCommand as Cmd, IoctlData as Data, Op};
952
953        pub const COUNTRY_INFO: [u8; CountryInfo::SIZE] = CountryInfo {
954            country_abbrev: [88, 88, 0, 0],
955            country_code: [88, 88, 0, 0],
956            rev: -1,
957        }
958        .into_bytes();
959
960        pub const EVENTS: [u8; EventMask::SIZE] = EventMask::with_masked_evts(&[
961            Event::Radio,
962            Event::If,
963            Event::ProbreqMsg,
964            Event::ProbreqMsgRx,
965            Event::Roam,
966            Event::ProbreqMsg,
967        ])
968        .into_bytes();
969
970        pub static OPS: &[Op] = &[
971            Op::WaitMs(2000),
972            Op::LoadCLM,
973            Op::iovar(Iovar::BusTxGlom, Data::from_u32(0)),
974            Op::iovar(Iovar::Apsta, Data::from_u32(1)),
975            Op::MacAddr,
976            Op::iovar(Iovar::Country, Data::CountryInfo),
977            Op::WaitMs(100),
978            Op::ioctl(Cmd::SetAntdiv, Data::from_u32(0)),
979            Op::iovar(Iovar::BusTxGlom, Data::from_u32(0)),
980            Op::WaitMs(100),
981            Op::iovar(Iovar::AmpduBaWsize, Data::from_u32(8)),
982            Op::WaitMs(100),
983            Op::iovar(Iovar::AmpduMpdu, Data::from_u32(4)),
984            Op::WaitMs(100),
985            Op::iovar(Iovar::BssCfgEventMsgs, Data::EventMask),
986            Op::WaitMs(100),
987            Op::ioctl(Cmd::Up, Data::Empty),
988            Op::WaitMs(100),
989            Op::ioctl(Cmd::SetGmode, Data::from_u32(1)),
990            Op::ioctl(Cmd::SetBand, Data::from_u32(0)),
991            Op::WaitMs(100),
992        ];
993    }
994
995    /// Access point (open) start tasks list
996    pub mod start_ap {
997        use super::sdpcm::Iovar;
998        use super::{IoctlCommand as Cmd, IoctlData as Data, Op};
999
1000        pub static OPS: [Op; 9] = [
1001            Op::ioctl(Cmd::Down, Data::empty()),
1002            Op::iovar(Iovar::Apsta, Data::from_u32(0)),
1003            Op::ioctl(Cmd::Up, Data::empty()),
1004            Op::ioctl(Cmd::SetAp, Data::from_u32(1)),
1005            Op::iovar(Iovar::BssCfgSsid, Data::BssSsid),
1006            Op::ioctl(Cmd::SetChannel, Data::Channel),
1007            Op::iovar(Iovar::BssCfgWsec, Data::from_2xu32(0, 0)),
1008            Op::iovar(Iovar::G2Mrate, Data::from_u32(11000000 / 500000)),
1009            Op::iovar(Iovar::Bss, Data::from_2xu32(0, 1)),
1010        ];
1011    }
1012
1013    /// Access point (WPA) start tasks list
1014    pub mod start_ap_wpa {
1015        use super::sdpcm::Iovar;
1016        use super::{IoctlCommand as Cmd, IoctlData as Data, Op};
1017
1018        pub static OPS: [Op; 12] = [
1019            Op::ioctl(Cmd::Down, Data::empty()),
1020            Op::iovar(Iovar::Apsta, Data::from_u32(0)),
1021            Op::ioctl(Cmd::Up, Data::empty()),
1022            Op::ioctl(Cmd::SetAp, Data::from_u32(1)),
1023            Op::iovar(Iovar::BssCfgSsid, Data::BssSsid),
1024            Op::ioctl(Cmd::SetChannel, Data::Channel),
1025            Op::iovar(Iovar::BssCfgWsec, Data::from_2xu32(0, 0x4)),
1026            Op::iovar(Iovar::BssCfgWpaAuth, Data::from_2xu32(0, 0x084)),
1027            Op::wait_ms(100),
1028            Op::ioctl(Cmd::SetWsecPmk, Data::Wpa1Passphrase),
1029            Op::iovar(Iovar::G2Mrate, Data::from_u32(11000000 / 500000)),
1030            Op::iovar(Iovar::Bss, Data::from_2xu32(0, 1)),
1031        ];
1032    }
1033
1034    /// Access point stop tasks list
1035    pub mod stop_ap {
1036        use super::sdpcm::Iovar;
1037        use super::{IoctlCommand as Cmd, IoctlData as Data, Op};
1038
1039        pub static OPS: [Op; 5] = [
1040            Op::iovar(Iovar::Bss, Data::from_2xu32(0, 0)),
1041            Op::ioctl(Cmd::SetAp, Data::from_u32(0)),
1042            Op::ioctl(Cmd::Down, Data::empty()),
1043            Op::iovar(Iovar::Apsta, Data::from_u32(1)),
1044            Op::ioctl(Cmd::Up, Data::empty()),
1045        ];
1046    }
1047
1048    /// Join open network tasks list
1049    pub mod join_open {
1050        use super::sdpcm::Iovar;
1051        use super::{IoctlCommand as Cmd, IoctlData as Data, Op};
1052
1053        pub static OPS: [Op; 7] = [
1054            Op::iovar(Iovar::AmpduBaWsize, Data::from_u32(8)),
1055            Op::ioctl(Cmd::SetWsec, Data::from_u32(0)),
1056            Op::iovar(Iovar::BssCfgSupWpa, Data::from_2xu32(0, 0)),
1057            Op::ioctl(Cmd::SetInfra, Data::from_u32(1)),
1058            Op::ioctl(Cmd::SetAuth, Data::from_u32(0)),
1059            Op::ioctl(Cmd::SetWpaAuth, Data::from_u32(0)),
1060            Op::ioctl(Cmd::SetSsid, Data::Ssid),
1061        ];
1062    }
1063
1064    /// Join secured network tasks list
1065    pub mod join_wpa {
1066        use super::sdpcm::Iovar;
1067        use super::{IoctlCommand as Cmd, IoctlData as Data, Op};
1068        use crate::cyw4343::constants;
1069
1070        mod wpa1 {
1071            pub(super) const MFP: u32 = 0;
1072            pub(super) const AUTH: u32 = 0;
1073            pub(super) const WPA_AUTH: u32 = 0x4;
1074        }
1075
1076        mod wpa2 {
1077            pub(super) const MFP: u32 = 1;
1078            pub(super) const AUTH: u32 = 0;
1079            pub(super) const WPA_AUTH: u32 = 0x80;
1080        }
1081
1082        mod wpa3 {
1083            pub(super) const MFP: u32 = 2;
1084            pub(super) const AUTH: u32 = 3;
1085            pub(super) const WPA_AUTH: u32 = 0x40000;
1086        }
1087
1088        const WPA1_SET: Op = Op::ioctl(Cmd::SetWsecPmk, Data::Wpa1Passphrase);
1089        const WPA3_SET: Op = Op::iovar(Iovar::SaePassword, Data::Wpa3Passphrase);
1090        const fn ops(wpa_set: Op, mfp: u32, auth: u32, wpa_auth: u32) -> [Op; 12] {
1091            [
1092                Op::iovar(Iovar::AmpduBaWsize, Data::from_u32(8)),
1093                Op::ioctl(Cmd::SetWsec, Data::from_u32(constants::WSEC_AES)),
1094                Op::iovar(Iovar::BssCfgSupWpa, Data::from_2xu32(0, 1)),
1095                Op::iovar(Iovar::BssCfgSupWpa2Eapver, Data::from_2xu32(0, 0xFFFF_FFFF)),
1096                Op::iovar(Iovar::BssCfgSupWpaTmo, Data::from_2xu32(0, 2500)),
1097                Op::wait_ms(110),
1098                wpa_set,
1099                Op::ioctl(Cmd::SetInfra, Data::from_u32(1)),
1100                Op::ioctl(Cmd::SetAuth, Data::from_u32(auth)),
1101                Op::iovar(Iovar::Mfp, Data::from_u32(mfp)),
1102                Op::ioctl(Cmd::SetWpaAuth, Data::from_u32(wpa_auth)),
1103                Op::ioctl(Cmd::SetSsid, Data::Ssid),
1104            ]
1105        }
1106
1107        pub static WPA1: [Op; 12] = ops(WPA1_SET, wpa1::MFP, wpa1::AUTH, wpa1::WPA_AUTH);
1108        pub static WPA2: [Op; 12] = ops(WPA1_SET, wpa2::MFP, wpa2::AUTH, wpa2::WPA_AUTH);
1109        pub static WPA3: [Op; 12] = ops(WPA3_SET, wpa3::MFP, wpa3::AUTH, wpa3::WPA_AUTH);
1110        pub static WPA2_WPA3: [Op; 13] = [
1111            Op::iovar(Iovar::AmpduBaWsize, Data::from_u32(8)),
1112            Op::ioctl(Cmd::SetWsec, Data::from_u32(constants::WSEC_AES)),
1113            Op::iovar(Iovar::BssCfgSupWpa, Data::from_2xu32(0, 1)),
1114            Op::iovar(Iovar::BssCfgSupWpa2Eapver, Data::from_2xu32(0, 0xFFFF_FFFF)),
1115            Op::iovar(Iovar::BssCfgSupWpaTmo, Data::from_2xu32(0, 2500)),
1116            Op::wait_ms(110),
1117            WPA1_SET,
1118            WPA3_SET,
1119            Op::ioctl(Cmd::SetInfra, Data::from_u32(1)),
1120            Op::ioctl(Cmd::SetAuth, Data::from_u32(wpa3::AUTH)),
1121            Op::iovar(Iovar::Mfp, Data::from_u32(wpa2::MFP)),
1122            Op::ioctl(Cmd::SetWpaAuth, Data::from_u32(wpa3::WPA_AUTH)),
1123            Op::ioctl(Cmd::SetSsid, Data::Ssid),
1124        ];
1125    }
1126
1127    /// Leave network task
1128    pub mod leave {
1129        use super::{IoctlCommand as Cmd, IoctlData as Data, Op};
1130
1131        pub const OP: Op = Op::ioctl(Cmd::Disassoc, Data::empty());
1132    }
1133
1134    /// Start scan task
1135    pub mod start_scan {
1136        use super::sdpcm::Iovar;
1137        use super::{IoctlData as Data, Op};
1138        use crate::cyw4343::{constants, sdpcm};
1139
1140        pub const SCAN_PARAMS: [u8; sdpcm::ScanParams::SIZE] = sdpcm::ScanParams {
1141            version: 1,
1142            action: constants::WL_SCAN_ACTION_START,
1143            sync_id: 1,
1144            ssid_len: 0,
1145            ssid: [0; 32],
1146            bssid: [0xff; 6],
1147            bss_type: 2,
1148            scan_type: constants::SCANTYPE_PASSIVE,
1149            nprobes: !0,
1150            active_time: !0,
1151            passive_time: !0,
1152            home_time: !0,
1153            channel_num: 0,
1154            channel_list: 0,
1155        }
1156        .into_bytes();
1157
1158        pub const OP: Op = Op::iovar(Iovar::Escan, Data::ScanParameters);
1159    }
1160
1161    /// Stop scan task
1162    pub mod stop_scan {
1163        use super::sdpcm::Iovar;
1164        use super::{IoctlData as Data, Op};
1165        use crate::cyw4343::{constants, sdpcm};
1166
1167        pub const SCAN_PARAMS: [u8; sdpcm::ScanParams::SIZE] = sdpcm::ScanParams {
1168            version: 1,
1169            action: constants::WL_SCAN_ACTION_ABORT,
1170            sync_id: 0,
1171            ssid_len: 0,
1172            ssid: [0; 32],
1173            bssid: [0x00; 6],
1174            bss_type: 0,
1175            scan_type: 0,
1176            nprobes: 0,
1177            active_time: 0,
1178            passive_time: 0,
1179            home_time: 0,
1180            channel_num: 0,
1181            channel_list: 0,
1182        }
1183        .into_bytes();
1184        pub const OP: Op = Op::iovar(Iovar::Escan, Data::AbortScanParameters);
1185    }
1186}