Skip to content

Commit 5dfb8ea

Browse files
tom-dudleyTom Dudley
andauthored
Add basic WiFi support for CYW43 driver (ZigEmbeddedGroup#777)
Co-authored-by: Tom Dudley <[email protected]>
1 parent 6fb9d0c commit 5dfb8ea

File tree

15 files changed

+1619
-6
lines changed

15 files changed

+1619
-6
lines changed

drivers/framework.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub const IO_expander = struct {
6969
};
7070

7171
pub const wireless = struct {
72+
pub const cyw43 = @import("wireless/cyw43/cyw43.zig");
7273
pub const cyw43_bus = @import("wireless/cyw43/bus.zig");
7374
pub const cyw43_runner = @import("wireless/cyw43/runner.zig");
7475
pub const Cyw43_Spi = cyw43_bus.Cyw43_Spi;

drivers/wireless/cyw43/bus.zig

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ pub const Cyw43_Bus = struct {
3939
internal_delay_ms: *const delayus_callback,
4040
backplane_window: u32 = 0xAAAA_AAAA,
4141

42+
// WLAN packet buffers (u32 aligned for SPI)
43+
wlan_tx_buf: [512]u32 = undefined, // 2048 bytes
44+
wlan_rx_buf: [512]u32 = undefined, // 2048 bytes
45+
4246
pub fn init_bus(self: *Self) !void {
4347
// Init sequence
4448
try self.pwr_pin.write(.low);
@@ -328,6 +332,45 @@ pub const Cyw43_Bus = struct {
328332
inline fn swap16(x: u32) u32 {
329333
return x << 16 | x >> 16;
330334
}
335+
336+
/// Get buffer for building WLAN TX packets.
337+
/// Returns byte slice starting after the command word (which wlan_send will fill).
338+
pub fn get_wlan_tx_buffer(self: *Self) []u8 {
339+
return std.mem.sliceAsBytes(self.wlan_tx_buf[1..]);
340+
}
341+
342+
/// Send WLAN packet. Caller should have written packet data to get_wlan_tx_buffer().
343+
/// Builds command word and performs SPI transaction.
344+
pub fn wlan_send(self: *Self, len: usize) void {
345+
const aligned_len = (len + 3) & ~@as(usize, 3);
346+
const cmd = Cyw43Cmd{
347+
.cmd = .write,
348+
.incr = .incremental,
349+
.func = .wlan,
350+
.addr = 0,
351+
.len = @intCast(aligned_len),
352+
};
353+
self.wlan_tx_buf[0] = @bitCast(cmd);
354+
355+
const words = (aligned_len + 3) / 4;
356+
// spi_write_blocking returns chip status register, not byte count
357+
_ = self.spi.spi_write_blocking(self.wlan_tx_buf[0 .. words + 1]);
358+
}
359+
360+
/// Receive WLAN packet. Returns slice of received data.
361+
pub fn wlan_recv(self: *Self, len: usize) []const u8 {
362+
const words = (len + 3) / 4;
363+
const cmd = Cyw43Cmd{
364+
.cmd = .read,
365+
.incr = .incremental,
366+
.func = .wlan,
367+
.addr = 0,
368+
.len = @intCast(len),
369+
};
370+
// spi_read_blocking returns chip status register, not byte count
371+
_ = self.spi.spi_read_blocking(@bitCast(cmd), self.wlan_rx_buf[0..words]);
372+
return std.mem.sliceAsBytes(self.wlan_rx_buf[0..words])[0..len];
373+
}
331374
};
332375

333376
const CmdType = enum(u1) {

drivers/wireless/cyw43/cyw43.zig

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// CYW43 WiFi Driver
2+
// Top-level entry point for the CYW43439 WiFi chip
3+
4+
const std = @import("std");
5+
const bus = @import("bus.zig");
6+
const runner_mod = @import("runner.zig");
7+
const wifi_mod = @import("wifi.zig");
8+
9+
pub const Runner = runner_mod.Cyw43_Runner;
10+
pub const Wifi = wifi_mod.CYW43_Wifi;
11+
pub const Bus = bus.Cyw43_Bus;
12+
pub const Security = wifi_mod.Security;
13+
pub const Event = wifi_mod.Event;
14+
15+
/// Initialize CYW43 chip and return Runner
16+
/// Loads firmware and prepares chip for WiFi operations.
17+
/// Use runner.wifi() to access WiFi configuration (enable, join, etc.)
18+
/// Use runner.run() in your main loop to poll for packets
19+
/// Use runner.get_rx_frame() to retrieve received data
20+
pub fn init(cyw43_bus: *Bus, delay_ms: *const fn (u32) void) !Runner {
21+
var cyw43_runner = Runner{
22+
.bus = cyw43_bus,
23+
.internal_delay_ms = delay_ms,
24+
};
25+
try cyw43_runner.init();
26+
27+
return cyw43_runner;
28+
}

drivers/wireless/cyw43/runner.zig

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const std = @import("std");
22
const bus = @import("bus.zig");
33
const consts = @import("consts.zig");
44
const nvram = @import("nvram.zig");
5+
const wifi_mod = @import("wifi.zig");
56

67
/// Callback for microsecond delays
78
pub const delayus_callback = fn (delay: u32) void;
@@ -15,6 +16,17 @@ pub const Cyw43_Runner = struct {
1516
bus: *bus.Cyw43_Bus,
1617
internal_delay_ms: *const delayus_callback,
1718

19+
// WiFi instance (initialized in init())
20+
wifi_instance: wifi_mod.CYW43_Wifi = undefined,
21+
22+
// RX frame buffer (valid until next run() call)
23+
// NOTE: Single-frame buffer. If get_rx_frame() is not called before the next
24+
// run() receives data, the previous frame is lost. For high-throughput or
25+
// bursty traffic, we may want to replace with a ring buffer.
26+
rx_frame_buf: [1600]u8 = undefined,
27+
rx_frame_len: usize = 0,
28+
has_rx_frame: bool = false,
29+
1830
pub fn init(self: *Self) !void {
1931
try self.bus.init_bus();
2032

@@ -99,6 +111,9 @@ pub const Cyw43_Runner = struct {
99111

100112
self.log_init();
101113

114+
// Initialize WiFi layer
115+
self.wifi_instance = wifi_mod.CYW43_Wifi.init(self.bus);
116+
102117
// TODO: bluetooth setup
103118

104119
log.debug("cyw43 runner init done", .{});
@@ -208,10 +223,37 @@ pub const Cyw43_Runner = struct {
208223
}
209224
}
210225

211-
pub fn run(self: *Self) void {
212-
while (true) {
213-
self.log_read();
226+
/// Run one poll cycle. Returns true if there was activity.
227+
/// Received data frames are buffered and can be retrieved via get_rx_frame().
228+
pub fn run(self: *Self) bool {
229+
// Clear previous frame
230+
self.has_rx_frame = false;
231+
232+
// Poll WiFi - processes events, may return data
233+
if (try self.wifi_instance.poll()) |frame| {
234+
// Buffer the received frame
235+
const copy_len = @min(frame.len, self.rx_frame_buf.len);
236+
@memcpy(self.rx_frame_buf[0..copy_len], frame[0..copy_len]);
237+
self.rx_frame_len = copy_len;
238+
self.has_rx_frame = true;
239+
return true;
214240
}
241+
242+
return false;
243+
}
244+
245+
/// Get the last received ethernet frame, if any.
246+
/// Returns null if no frame was received in the last run() call.
247+
pub fn get_rx_frame(self: *Self) ?[]const u8 {
248+
if (self.has_rx_frame) {
249+
return self.rx_frame_buf[0..self.rx_frame_len];
250+
}
251+
return null;
252+
}
253+
254+
/// Get pointer to WiFi instance for configuration (enable, join, etc.)
255+
pub fn wifi(self: *Self) *wifi_mod.CYW43_Wifi {
256+
return &self.wifi_instance;
215257
}
216258
};
217259

0 commit comments

Comments
 (0)