Skip to content

Commit e6f560f

Browse files
authored
rpi net: networks scan and interrupt mode (#885)
1 parent 4db37e8 commit e6f560f

18 files changed

Lines changed: 1164 additions & 363 deletions

File tree

drivers/wireless/cyw43439.zig

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const Link = @import("link");
66
const Bus = @import("cyw43439/bus.zig");
77
const WiFi = @import("cyw43439/wifi.zig");
88
pub const JoinOptions = WiFi.JoinOptions;
9+
pub const InitOptions = WiFi.InitOptions;
910

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

@@ -19,52 +20,71 @@ pub fn init(
1920
self: *Self,
2021
spi: Bus.Spi,
2122
sleep_ms: *const fn (delay: u32) void,
23+
opt: InitOptions,
2224
) !void {
2325
self.bus = .{ .spi = spi, .sleep_ms = sleep_ms };
2426
try self.bus.init();
2527

2628
self.wifi = .{ .bus = &self.bus };
27-
try self.wifi.init();
29+
try self.wifi.init(opt);
2830

29-
self.mac = try self.read_mac();
31+
self.mac = try self.wifi.read_mac();
3032
}
3133

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

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

47-
fn read_mac(self: *Self) ![6]u8 {
48-
var mac: [6]u8 = @splat(0);
49-
const n = try self.wifi.get_var("cur_etheraddr", &mac);
50-
if (n != mac.len) {
51-
log.err("read_mac unexpected read bytes: {}", .{n});
52-
return error.ReadMacFailed;
53-
}
54-
return mac;
46+
pub fn join_state(self: *Self) WiFi.JoinState {
47+
return self.wifi.join_state;
48+
}
49+
50+
pub fn is_joined(self: *Self) bool {
51+
return self.wifi.join_state == .joined;
52+
}
53+
54+
/// Non blocking scan. Returns poller.
55+
pub fn scan(self: *Self) !WiFi.ScanPoller {
56+
return try self.wifi.scan();
5557
}
5658

57-
pub fn recv_zc(ptr: *anyopaque, bytes: []u8) anyerror!?struct { usize, usize } {
59+
pub fn scan_result(self: *Self) ?WiFi.ScanResult {
60+
return self.wifi.scan_result;
61+
}
62+
63+
/// Zero copy receive. This buffer is passed to the chip. Buffer has to be 4
64+
/// bytes aligned and at least 1540 bytes. `head` and `len` defines part of the
65+
/// buffer where is received ethernet packet. If `len` is zero there is no packet.
66+
pub fn recv_zc(ptr: *anyopaque, buffer: []u8) anyerror!Link.RecvResponse {
5867
const self: *Self = @ptrCast(@alignCast(ptr));
59-
return self.wifi.recv_zc(bytes);
68+
const head, const len, const next_packet_available = try self.wifi.recv_zc(buffer);
69+
if (self.wifi.err) |err| return err;
70+
return .{
71+
.head = head,
72+
.len = len,
73+
.link_state = if (self.is_joined()) .up else .down,
74+
.next_packet_available = next_packet_available,
75+
};
6076
}
6177

62-
pub fn send_zc(ptr: *anyopaque, bytes: []u8) Link.Error!void {
78+
/// Zero copy send. Buffer has to have 22 bytes of headrom for chip control
79+
/// command. Ethernet packet has to start at byte 22. Buffer has to be 4 bytes
80+
/// aligned.
81+
pub fn send_zc(ptr: *anyopaque, buffer: []u8) Link.Error!void {
6382
const self: *Self = @ptrCast(@alignCast(ptr));
64-
self.wifi.send_zc(bytes) catch |err| switch (err) {
65-
error.OutOfMemory => return error.OutOfMemory,
66-
error.LinkDown => return error.LinkDown,
67-
else => return error.InternalError,
83+
if (self.wifi.join_state != .joined) return error.LinkDown;
84+
if (!self.wifi.has_credit()) return error.OutOfMemory;
85+
self.wifi.send_zc(buffer) catch |err| {
86+
log.err("cyw43 send {}", .{err});
87+
return error.InternalError;
6888
};
6989
}
7090

@@ -84,6 +104,10 @@ pub const Pin = struct {
84104
pub fn toggle(self: *Pin) void {
85105
self.wifi.gpio_toggle(self.pin);
86106
}
107+
108+
pub fn put(self: *Pin, value: u1) void {
109+
self.wifi.gpio_put(self.pin, value);
110+
}
87111
};
88112

89113
pub fn link(self: *Self) Link {

drivers/wireless/cyw43439/bus.zig

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ pub const Spi = struct {
1919
self.vtable.write(self.ptr, buffer);
2020
}
2121

22+
pub fn irq_cleared(self: *@This()) void {
23+
self.vtable.irq_cleared(self.ptr);
24+
}
25+
2226
pub const VTable = struct {
2327
read: *const fn (*anyopaque, []u32) void,
2428
write: *const fn (*anyopaque, []u32) void,
29+
irq_cleared: *const fn (*anyopaque) void,
2530
};
2631
};
2732

@@ -68,7 +73,7 @@ pub fn init(self: *Self) !void {
6873
.wake_up = true,
6974
},
7075
.response_delay = .{
71-
.unknown = 0x4, // 32-bit response delay?
76+
.unknown = 0,
7277
},
7378
.status_enable = .{
7479
.status_enable = true,
@@ -354,11 +359,8 @@ pub const Irq = packed struct {
354359
f2_packet_available: bool = false,
355360
f3_packet_available: bool = false,
356361
f1_overflow: bool = false, // Due to last write. Bkplane has pending write requests
357-
misc_intr0: bool = false,
358-
misc_intr1: bool = false,
359-
misc_intr2: bool = false,
360-
misc_intr3: bool = false,
361-
misc_intr4: bool = false,
362+
363+
_reserved1: u5 = 0,
362364
f1_intr: bool = false,
363365
f2_intr: bool = false,
364366
f3_intr: bool = false,

drivers/wireless/cyw43439/ioctl.zig

Lines changed: 128 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,12 @@ pub const Cmd = enum(u32) {
9292
set_auth = 22,
9393
set_ssid = 26,
9494
set_antdiv = 64,
95+
set_pm = 86,
9596
set_gmode = 110,
9697
set_wsec = 134,
9798
set_band = 142,
9899
set_wpa_auth = 165,
100+
set_scan_channel_time = 185,
99101
get_var = 262,
100102
set_var = 263,
101103
set_wsec_pmk = 268,
@@ -184,7 +186,7 @@ pub const Response = struct {
184186
return .{ head, self.sdp.len - head };
185187
}
186188

187-
pub fn event(self: Self) EventPacket {
189+
pub fn event(self: Self) ?EventPacket {
188190
assert(self.sdp.channel() == .event);
189191
const buf = self.data();
190192
if (buf.len < @sizeOf(EventPacket)) {
@@ -195,8 +197,64 @@ pub const Response = struct {
195197
var evt: EventPacket = undefined;
196198
@memcpy(std.mem.asBytes(&evt), buf[0..@sizeOf(EventPacket)]);
197199
std.mem.byteSwapAllFields(EventPacket, &evt);
200+
201+
if (evt.eth.ether_type != 0x886c) return null;
202+
if (!mem.eql(u8, &evt.hdr.oui, &.{ 0x00, 0x10, 0x18 })) return null;
203+
198204
return evt;
199205
}
206+
207+
pub fn event_scan_result(self: Self) !struct { EventScanResult, Security } {
208+
var res: EventScanResult = undefined;
209+
var sec: Security = .{};
210+
211+
assert(self.sdp.channel() == .event);
212+
const buf = self.data();
213+
if (buf.len < @sizeOf(EventPacket) + @sizeOf(EventScanResult)) {
214+
return error.Cyw43InsufficientData;
215+
}
216+
const res_buf = buf[@sizeOf(EventPacket)..];
217+
@memcpy(std.mem.asBytes(&res), res_buf[0..@sizeOf(EventScanResult)]);
218+
res.channel &= 0xff;
219+
if (res_buf.len < res.ie_offset + res.ie_length) {
220+
return error.Cyw43InsufficientData;
221+
}
222+
223+
// ref: https://github.com/georgerobotics/cyw43-driver/blob/13004039ffe127519f33824bf7d240e1f23fbdcd/src/cyw43_ll.c#L538
224+
const is_open = res.capability & 0x0010 == 0;
225+
if (!is_open) sec.wep_psk = true;
226+
227+
var ie_buf = res_buf[res.ie_offset..][0..res.ie_length];
228+
while (ie_buf.len >= 2) {
229+
const typ = ie_buf[0];
230+
const len = ie_buf[1];
231+
ie_buf = ie_buf[2..];
232+
if (typ == 48) {
233+
sec.wpa2 = true;
234+
} else {
235+
const wpa_oui_type1 = "\x00\x50\xF2\x01";
236+
if (typ == 221 and ie_buf.len >= wpa_oui_type1.len) {
237+
if (mem.eql(u8, ie_buf[0..wpa_oui_type1.len], wpa_oui_type1)) {
238+
sec.wpa = true;
239+
}
240+
}
241+
}
242+
if (ie_buf.len <= len) break;
243+
ie_buf = ie_buf[len..];
244+
}
245+
246+
return .{ res, sec };
247+
}
248+
};
249+
250+
pub const Security = packed struct {
251+
wep_psk: bool = false,
252+
wpa: bool = false,
253+
wpa2: bool = false,
254+
255+
pub fn open(s: Security) bool {
256+
return @as(u3, @bitCast(s)) == 0;
257+
}
200258
};
201259

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

352410
// Escan result event (excluding 12-byte IOCTL header and BDC header)
353-
const EventScanResult = extern struct {
354-
eth: EthernetHeader,
355-
hdr: EventHeader,
356-
msg: EventMessage,
357-
scan: ScanResultHeader,
358-
info: BssInfo,
411+
pub const EventScanResult = extern struct {
412+
// Scan result header
413+
const Header = extern struct {
414+
buflen: u32,
415+
version: u32,
416+
sync_id: u16,
417+
bss_count: u16,
418+
};
419+
420+
hdr: Header,
421+
422+
version: u32, // version field
423+
length: u32, // byte length of data in this record, starting at version and including IEs
424+
bssid: [6]u8, // The MAC address of the Access Point (AP)
425+
beacon_period: u16, // Interval between two consecutive beacon frames. Units are Kusec
426+
capability: u16, // Capability information
427+
ssid_len: u8, // SSID length
428+
ssid: [32]u8, // Array to store SSID
429+
nrates: u32, // Count of rates in this set
430+
rates: [16]u8, // rates in 500kbps units, higher bit set if basic
431+
channel: u16, // Channel specification for basic service set
432+
atim_window: u16, // Announcement traffic indication message window size. Units are Kusec
433+
dtim_period: u8, // Delivery traffic indication message period
434+
rssi: u16, // receive signal strength (in dBm)
435+
phy_noise: u8, // noise (in dBm)
436+
// The following fields assume the 'version' field is 109 (0x6D)
437+
n_cap: u8, // BSS is 802.11N Capable
438+
nbss_cap: u32, // 802.11N BSS Capabilities (based on HT_CAP_*)
439+
ctl_ch: u8, // 802.11N BSS control channel number
440+
reserved1: u32, // Reserved for expansion of BSS properties
441+
flags: u8, // flags
442+
reserved2: [3]u8, // Reserved for expansion of BSS properties
443+
basic_mcs: [16]u8, // 802.11N BSS required MCS set
444+
ie_offset: u16, // offset at which IEs start, from beginning
445+
ie_length: u32, // byte length of Information Elements
446+
snr: u16, // Average SNR(signal to noise ratio) during frame reception
447+
// Variable-length Information Elements follow, see cyw43_ll_wifi_parse_scan_result
359448
};
360449

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

377466
// Raw event header (sdpcm_raw_event_header_t)
378-
const EventMessage = extern struct {
467+
pub const EventMessage = extern struct {
379468
version: u16,
380469
flags: u16,
381470
event_type: EventType,
@@ -389,48 +478,9 @@ const EventMessage = extern struct {
389478
bsscfgidx: u8,
390479
};
391480

392-
// Scan result header (part of wl_escan_result_t)
393-
const ScanResultHeader = extern struct {
394-
buflen: u32,
395-
version: u32,
396-
sync_id: u16,
397-
bss_count: u16,
398-
};
399-
400-
// BSS info from EScan (part of wl_bss_info_t)
401-
const BssInfo = extern struct {
402-
version: u32, // version field
403-
length: u32, // byte length of data in this record, starting at version and including IEs
404-
bssid: [6]u8, // Unique 6-byte MAC address
405-
beacon_period: u16, // Interval between two consecutive beacon frames. Units are Kusec
406-
capability: u16, // Capability information
407-
ssid_len: u8, // SSID length
408-
ssid: [32]u8, // Array to store SSID
409-
nrates: u32, // Count of rates in this set
410-
rates: [16]u8, // rates in 500kbps units, higher bit set if basic
411-
channel: u16, // Channel specification for basic service set
412-
atim_window: u16, // Announcement traffic indication message window size. Units are Kusec
413-
dtim_period: u8, // Delivery traffic indication message period
414-
rssi: u16, // receive signal strength (in dBm)
415-
phy_noise: u8, // noise (in dBm)
416-
// The following fields assume the 'version' field is 109 (0x6D)
417-
n_cap: u8, // BSS is 802.11N Capable
418-
nbss_cap: u32, // 802.11N BSS Capabilities (based on HT_CAP_*)
419-
ctl_ch: u8, // 802.11N BSS control channel number
420-
reserved1: u32, // Reserved for expansion of BSS properties
421-
flags: u8, // flags
422-
reserved2: [3]u8, // Reserved for expansion of BSS properties
423-
basic_mcs: [16]u8, // 802.11N BSS required MCS set
424-
ie_offset: u16, // offset at which IEs start, from beginning
425-
ie_length: u32, // byte length of Information Elements
426-
snr: u16, // Average SNR(signal to noise ratio) during frame reception
427-
// Variable-length Information Elements follow, see cyw43_ll_wifi_parse_scan_result
428-
};
429-
430-
// zig fmt: off
431-
432481
// Async events
433-
const EventType = enum(u32) {
482+
pub const EventType = enum(u32) {
483+
// zig fmt: off
434484
none = 0xffffffff,
435485
set_ssid = 0, // indicates status of set ssid ,
436486
join = 1, // differentiates join ibss from found (wlc_e_start) ibss
@@ -582,9 +632,17 @@ const EventType = enum(u32) {
582632
ext_auth_frame_rx = 188, // authentication request received
583633
mgmt_frame_txstatus = 189, // mgmt frame Tx complete
584634
_,
585-
};
635+
// zig fmt: on
586636

587-
// zig fmt: on
637+
pub fn mask(events: []const EventType) [26]u8 {
638+
var m: [26]u8 = @splat(0);
639+
for (events) |event| {
640+
const e: u32 = @intFromEnum(event);
641+
m[4 + e / 8] |= @as(u8, 1) << @as(u3, @truncate(e & 7));
642+
}
643+
return m;
644+
}
645+
};
588646

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

719777
try testing.expectEqualSlices(u8, r1, r2);
720778
}
779+
780+
test "events mask" {
781+
const buf = hex_to_bytes("000000008B120102004000000000800100000000000000000000");
782+
try testing.expectEqual(26, buf.len);
783+
const mask = EventType.mask(&.{
784+
.join,
785+
.assoc,
786+
.reassoc,
787+
.assoc_req_ie,
788+
.assoc_resp_ie,
789+
.set_ssid,
790+
.link,
791+
.auth,
792+
.psk_sup,
793+
.eapol_msg,
794+
.disassoc_ind,
795+
});
796+
try testing.expectEqualSlices(u8, &buf, &mask);
797+
}

0 commit comments

Comments
 (0)