Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 52 additions & 28 deletions drivers/wireless/cyw43439.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const Link = @import("link");
const Bus = @import("cyw43439/bus.zig");
const WiFi = @import("cyw43439/wifi.zig");
pub const JoinOptions = WiFi.JoinOptions;
pub const InitOptions = WiFi.InitOptions;

const log = std.log.scoped(.cyw43);

Expand All @@ -19,52 +20,71 @@ pub fn init(
self: *Self,
spi: Bus.Spi,
sleep_ms: *const fn (delay: u32) void,
opt: InitOptions,
) !void {
self.bus = .{ .spi = spi, .sleep_ms = sleep_ms };
try self.bus.init();

self.wifi = .{ .bus = &self.bus };
try self.wifi.init();
try self.wifi.init(opt);

self.mac = try self.read_mac();
self.mac = try self.wifi.read_mac();
}

pub fn join(self: *Self, ssid: []const u8, pwd: []const u8, opt: JoinOptions) !void {
try self.wifi.join(ssid, pwd, opt);
/// Non blocking join. Returns poller which should be pulled while poll method
/// returns true (has more).
pub fn join(self: *Self, ssid: []const u8, pwd: []const u8, opt: JoinOptions) !WiFi.JoinPoller {
return try self.wifi.join(ssid, pwd, opt);
}

fn show_clm_ver(self: *Self) !void {
var data: [128]u8 = @splat(0);
const n = try self.wifi.get_var("clmver", &data);
var iter = mem.splitScalar(u8, data[0..n], 0x0a);
log.debug("clmver:", .{});
while (iter.next()) |line| {
if (line.len == 0 or line[0] == 0x00) continue;
log.debug(" {s}", .{line});
}
/// Blocking wifi network join
pub fn join_wait(self: *Self, ssid: []const u8, pwd: []const u8, opt: JoinOptions) !void {
var poller = try self.join(ssid, pwd, opt);
try poller.wait(opt.wait_ms);
}

fn read_mac(self: *Self) ![6]u8 {
var mac: [6]u8 = @splat(0);
const n = try self.wifi.get_var("cur_etheraddr", &mac);
if (n != mac.len) {
log.err("read_mac unexpected read bytes: {}", .{n});
return error.ReadMacFailed;
}
return mac;
pub fn join_state(self: *Self) WiFi.JoinState {
return self.wifi.join_state;
}

pub fn is_joined(self: *Self) bool {
return self.wifi.join_state == .joined;
}

/// Non blocking scan. Returns poller.
pub fn scan(self: *Self) !WiFi.ScanPoller {
return try self.wifi.scan();
}

pub fn recv_zc(ptr: *anyopaque, bytes: []u8) anyerror!?struct { usize, usize } {
pub fn scan_result(self: *Self) ?WiFi.ScanResult {
return self.wifi.scan_result;
}

/// Zero copy receive. This buffer is passed to the chip. Buffer has to be 4
/// bytes aligned and at least 1540 bytes. `head` and `len` defines part of the
/// buffer where is received ethernet packet. If `len` is zero there is no packet.
pub fn recv_zc(ptr: *anyopaque, buffer: []u8) anyerror!Link.RecvResponse {
const self: *Self = @ptrCast(@alignCast(ptr));
return self.wifi.recv_zc(bytes);
const head, const len, const next_packet_available = try self.wifi.recv_zc(buffer);
if (self.wifi.err) |err| return err;
return .{
.head = head,
.len = len,
.link_state = if (self.is_joined()) .up else .down,
.next_packet_available = next_packet_available,
};
}

pub fn send_zc(ptr: *anyopaque, bytes: []u8) Link.Error!void {
/// Zero copy send. Buffer has to have 22 bytes of headrom for chip control
/// command. Ethernet packet has to start at byte 22. Buffer has to be 4 bytes
/// aligned.
pub fn send_zc(ptr: *anyopaque, buffer: []u8) Link.Error!void {
const self: *Self = @ptrCast(@alignCast(ptr));
self.wifi.send_zc(bytes) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.LinkDown => return error.LinkDown,
else => return error.InternalError,
if (self.wifi.join_state != .joined) return error.LinkDown;
if (!self.wifi.has_credit()) return error.OutOfMemory;
self.wifi.send_zc(buffer) catch |err| {
log.err("cyw43 send {}", .{err});
return error.InternalError;
};
}

Expand All @@ -84,6 +104,10 @@ pub const Pin = struct {
pub fn toggle(self: *Pin) void {
self.wifi.gpio_toggle(self.pin);
}

pub fn put(self: *Pin, value: u1) void {
self.wifi.gpio_put(self.pin, value);
}
};

pub fn link(self: *Self) Link {
Expand Down
14 changes: 8 additions & 6 deletions drivers/wireless/cyw43439/bus.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ pub const Spi = struct {
self.vtable.write(self.ptr, buffer);
}

pub fn irq_cleared(self: *@This()) void {
self.vtable.irq_cleared(self.ptr);
}

pub const VTable = struct {
read: *const fn (*anyopaque, []u32) void,
write: *const fn (*anyopaque, []u32) void,
irq_cleared: *const fn (*anyopaque) void,
};
};

Expand Down Expand Up @@ -68,7 +73,7 @@ pub fn init(self: *Self) !void {
.wake_up = true,
},
.response_delay = .{
.unknown = 0x4, // 32-bit response delay?
.unknown = 0,
},
.status_enable = .{
.status_enable = true,
Expand Down Expand Up @@ -354,11 +359,8 @@ pub const Irq = packed struct {
f2_packet_available: bool = false,
f3_packet_available: bool = false,
f1_overflow: bool = false, // Due to last write. Bkplane has pending write requests
misc_intr0: bool = false,
misc_intr1: bool = false,
misc_intr2: bool = false,
misc_intr3: bool = false,
misc_intr4: bool = false,

_reserved1: u5 = 0,
f1_intr: bool = false,
f2_intr: bool = false,
f3_intr: bool = false,
Expand Down
179 changes: 128 additions & 51 deletions drivers/wireless/cyw43439/ioctl.zig
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,12 @@ pub const Cmd = enum(u32) {
set_auth = 22,
set_ssid = 26,
set_antdiv = 64,
set_pm = 86,
set_gmode = 110,
set_wsec = 134,
set_band = 142,
set_wpa_auth = 165,
set_scan_channel_time = 185,
get_var = 262,
set_var = 263,
set_wsec_pmk = 268,
Expand Down Expand Up @@ -184,7 +186,7 @@ pub const Response = struct {
return .{ head, self.sdp.len - head };
}

pub fn event(self: Self) EventPacket {
pub fn event(self: Self) ?EventPacket {
assert(self.sdp.channel() == .event);
const buf = self.data();
if (buf.len < @sizeOf(EventPacket)) {
Expand All @@ -195,8 +197,64 @@ pub const Response = struct {
var evt: EventPacket = undefined;
@memcpy(std.mem.asBytes(&evt), buf[0..@sizeOf(EventPacket)]);
std.mem.byteSwapAllFields(EventPacket, &evt);

if (evt.eth.ether_type != 0x886c) return null;
if (!mem.eql(u8, &evt.hdr.oui, &.{ 0x00, 0x10, 0x18 })) return null;

return evt;
}

pub fn event_scan_result(self: Self) !struct { EventScanResult, Security } {
var res: EventScanResult = undefined;
var sec: Security = .{};

assert(self.sdp.channel() == .event);
const buf = self.data();
if (buf.len < @sizeOf(EventPacket) + @sizeOf(EventScanResult)) {
return error.Cyw43InsufficientData;
}
const res_buf = buf[@sizeOf(EventPacket)..];
@memcpy(std.mem.asBytes(&res), res_buf[0..@sizeOf(EventScanResult)]);
res.channel &= 0xff;
if (res_buf.len < res.ie_offset + res.ie_length) {
return error.Cyw43InsufficientData;
}

// ref: https://github.com/georgerobotics/cyw43-driver/blob/13004039ffe127519f33824bf7d240e1f23fbdcd/src/cyw43_ll.c#L538
const is_open = res.capability & 0x0010 == 0;
if (!is_open) sec.wep_psk = true;

var ie_buf = res_buf[res.ie_offset..][0..res.ie_length];
while (ie_buf.len >= 2) {
const typ = ie_buf[0];
const len = ie_buf[1];
ie_buf = ie_buf[2..];
if (typ == 48) {
sec.wpa2 = true;
} else {
const wpa_oui_type1 = "\x00\x50\xF2\x01";
if (typ == 221 and ie_buf.len >= wpa_oui_type1.len) {
if (mem.eql(u8, ie_buf[0..wpa_oui_type1.len], wpa_oui_type1)) {
sec.wpa = true;
}
}
}
if (ie_buf.len <= len) break;
ie_buf = ie_buf[len..];
}

return .{ res, sec };
}
};

pub const Security = packed struct {
wep_psk: bool = false,
wpa: bool = false,
wpa2: bool = false,

pub fn open(s: Security) bool {
return @as(u3, @bitCast(s)) == 0;
}
};

pub fn response(buf: []const u8) !Response {
Expand Down Expand Up @@ -350,12 +408,43 @@ const EventPacket = extern struct {
};

// Escan result event (excluding 12-byte IOCTL header and BDC header)
const EventScanResult = extern struct {
eth: EthernetHeader,
hdr: EventHeader,
msg: EventMessage,
scan: ScanResultHeader,
info: BssInfo,
pub const EventScanResult = extern struct {
// Scan result header
const Header = extern struct {
buflen: u32,
version: u32,
sync_id: u16,
bss_count: u16,
};

hdr: Header,

version: u32, // version field
length: u32, // byte length of data in this record, starting at version and including IEs
bssid: [6]u8, // The MAC address of the Access Point (AP)
beacon_period: u16, // Interval between two consecutive beacon frames. Units are Kusec
capability: u16, // Capability information
ssid_len: u8, // SSID length
ssid: [32]u8, // Array to store SSID
nrates: u32, // Count of rates in this set
rates: [16]u8, // rates in 500kbps units, higher bit set if basic
channel: u16, // Channel specification for basic service set
atim_window: u16, // Announcement traffic indication message window size. Units are Kusec
dtim_period: u8, // Delivery traffic indication message period
rssi: u16, // receive signal strength (in dBm)
phy_noise: u8, // noise (in dBm)
// The following fields assume the 'version' field is 109 (0x6D)
n_cap: u8, // BSS is 802.11N Capable
nbss_cap: u32, // 802.11N BSS Capabilities (based on HT_CAP_*)
ctl_ch: u8, // 802.11N BSS control channel number
reserved1: u32, // Reserved for expansion of BSS properties
flags: u8, // flags
reserved2: [3]u8, // Reserved for expansion of BSS properties
basic_mcs: [16]u8, // 802.11N BSS required MCS set
ie_offset: u16, // offset at which IEs start, from beginning
ie_length: u32, // byte length of Information Elements
snr: u16, // Average SNR(signal to noise ratio) during frame reception
// Variable-length Information Elements follow, see cyw43_ll_wifi_parse_scan_result
};

// Ethernet header (sdpcm_ethernet_header_t)
Expand All @@ -375,7 +464,7 @@ const EventHeader = extern struct {
};

// Raw event header (sdpcm_raw_event_header_t)
const EventMessage = extern struct {
pub const EventMessage = extern struct {
version: u16,
flags: u16,
event_type: EventType,
Expand All @@ -389,48 +478,9 @@ const EventMessage = extern struct {
bsscfgidx: u8,
};

// Scan result header (part of wl_escan_result_t)
const ScanResultHeader = extern struct {
buflen: u32,
version: u32,
sync_id: u16,
bss_count: u16,
};

// BSS info from EScan (part of wl_bss_info_t)
const BssInfo = extern struct {
version: u32, // version field
length: u32, // byte length of data in this record, starting at version and including IEs
bssid: [6]u8, // Unique 6-byte MAC address
beacon_period: u16, // Interval between two consecutive beacon frames. Units are Kusec
capability: u16, // Capability information
ssid_len: u8, // SSID length
ssid: [32]u8, // Array to store SSID
nrates: u32, // Count of rates in this set
rates: [16]u8, // rates in 500kbps units, higher bit set if basic
channel: u16, // Channel specification for basic service set
atim_window: u16, // Announcement traffic indication message window size. Units are Kusec
dtim_period: u8, // Delivery traffic indication message period
rssi: u16, // receive signal strength (in dBm)
phy_noise: u8, // noise (in dBm)
// The following fields assume the 'version' field is 109 (0x6D)
n_cap: u8, // BSS is 802.11N Capable
nbss_cap: u32, // 802.11N BSS Capabilities (based on HT_CAP_*)
ctl_ch: u8, // 802.11N BSS control channel number
reserved1: u32, // Reserved for expansion of BSS properties
flags: u8, // flags
reserved2: [3]u8, // Reserved for expansion of BSS properties
basic_mcs: [16]u8, // 802.11N BSS required MCS set
ie_offset: u16, // offset at which IEs start, from beginning
ie_length: u32, // byte length of Information Elements
snr: u16, // Average SNR(signal to noise ratio) during frame reception
// Variable-length Information Elements follow, see cyw43_ll_wifi_parse_scan_result
};

// zig fmt: off

// Async events
const EventType = enum(u32) {
pub const EventType = enum(u32) {
// zig fmt: off
none = 0xffffffff,
set_ssid = 0, // indicates status of set ssid ,
join = 1, // differentiates join ibss from found (wlc_e_start) ibss
Expand Down Expand Up @@ -582,9 +632,17 @@ const EventType = enum(u32) {
ext_auth_frame_rx = 188, // authentication request received
mgmt_frame_txstatus = 189, // mgmt frame Tx complete
_,
};
// zig fmt: on

// zig fmt: on
pub fn mask(events: []const EventType) [26]u8 {
var m: [26]u8 = @splat(0);
for (events) |event| {
const e: u32 = @intFromEnum(event);
m[4 + e / 8] |= @as(u8, 1) << @as(u3, @truncate(e & 7));
}
return m;
}
};

pub const EventStatus = enum(u32) {
/// operation was successful
Expand Down Expand Up @@ -718,3 +776,22 @@ test "small data is padded in request to 4 bytes" {

try testing.expectEqualSlices(u8, r1, r2);
}

test "events mask" {
const buf = hex_to_bytes("000000008B120102004000000000800100000000000000000000");
try testing.expectEqual(26, buf.len);
const mask = EventType.mask(&.{
.join,
.assoc,
.reassoc,
.assoc_req_ie,
.assoc_resp_ie,
.set_ssid,
.link,
.auth,
.psk_sup,
.eapol_msg,
.disassoc_ind,
});
try testing.expectEqualSlices(u8, &buf, &mask);
}
Loading
Loading