* [9front] Fwd: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org)
[not found] <1734B8F2A873B2D850DB1C299ECACF8F@9front.org>
@ 2023-10-22 18:43 ` Stanley Lieber
2023-10-22 18:45 ` [9front] " Stanley Lieber
0 siblings, 1 reply; 3+ messages in thread
From: Stanley Lieber @ 2023-10-22 18:43 UTC (permalink / raw)
To: 9front
this is for rpi, yes? is there a list of specific hardware this is known to work with?
sl
-------- Original Message --------
From: commits@git.9front.org
Sent: October 22, 2023 10:53:02 AM EDT
To: 9front-commits@9front.org
Subject: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org)
Thanks Richard Miller
---
diff f2cfee358f329519e913a20142d96b1e0029633c fb96a050f8a9d5f23da3557ba89251ffb73889bf
--- a/sys/src/9/bcm/emmc.c Sun Oct 22 09:21:23 2023
+++ b/sys/src/9/bcm/emmc.c Sun Oct 22 10:53:02 2023
@@ -129,6 +129,7 @@
typedef struct Ctlr Ctlr;
struct Ctlr {
+ Rendez cardr;
Rendez r;
int fastclock;
ulong extclk;
@@ -266,6 +267,23 @@
intrenable(IRQmmc, emmcinterrupt, nil, BUSUNKNOWN, io->name);
}
+int
+sdiocardintr(int wait)
+{
+ u32int *r = (u32int*)EMMCREGS;
+ int i;
+
+ WR(Interrupt, Cardintr);
+ while(((i = r[Interrupt]) & Cardintr) == 0){
+ if(!wait)
+ return 0;
+ WR(Irpten, r[Irpten] | Cardintr);
+ sleep(&emmc.cardr, cardintready, 0);
+ }
+ WR(Interrupt, Cardintr);
+ return i;
+}
+
static int
emmccmd(SDio*, SDiocmd *cmd, u32int arg, u32int *resp)
{
@@ -434,21 +452,25 @@
i = r[Interrupt];
if(i&(Datadone|Err))
wakeup(&emmc.r);
+ if(i&Cardintr)
+ wakeup(&emmc.cardr);
WR(Irpten, r[Irpten] & ~i);
}
+
+SDio sdio = {
+ "emmc",
+ emmcinit,
+ emmcenable,
+ emmcinquiry,
+ emmccmd,
+ emmciosetup,
+ emmcio,
+ emmcbus,
+};
+
void
emmclink(void)
{
- static SDio io = {
- "emmc",
- emmcinit,
- emmcenable,
- emmcinquiry,
- emmccmd,
- emmciosetup,
- emmcio,
- emmcbus,
- };
- addmmcio(&io);
+ addmmcio(&sdio);
}
--- /dev/null Sun Aug 27 23:09:05 2023
+++ b/sys/src/9/bcm/ether4330.c Sun Oct 22 10:53:02 2023
@@ -0,0 +1,2411 @@
+/*
+ * Broadcom bcm4330 wifi (sdio interface)
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "../port/sd.h"
+#include "../port/etherif.h"
+
+#define CACHELINESZ 64 /* temp */
+
+extern SDio sdio;
+extern int sdiocardintr(int);
+
+SDiocmd IO_SEND_OP_COND = { 5, 3, 0, 0, "IO_SEND_OP_COND" };
+SDiocmd IO_RW_DIRECT = { 52, 1, 0, 0, "IO_RW_DIRECT" };
+
+enum{
+ SDIODEBUG = 0,
+ SBDEBUG = 0,
+ EVENTDEBUG = 0,
+ VARDEBUG = 0,
+ FWDEBUG = 0,
+
+ Corescansz = 512,
+ Uploadsz = 2048,
+ Sdpcmsz = 12, /* sizeof(Sdpcmsz) */
+ Cmdsz = 16, /* sizeof(Cmd) */
+
+ SDfreq = 25*Mhz, /* standard SD frequency */
+ SDfreqhs = 50*Mhz, /* high speed frequency */
+
+ Wifichan = 0, /* default channel */
+ Firmwarecmp = 1,
+
+ ARMcm3 = 0x82A,
+ ARM7tdmi = 0x825,
+ ARMcr4 = 0x83E,
+
+ Fn0 = 0,
+ Fn1 = 1,
+ Fn2 = 2,
+ Fbr1 = 0x100,
+ Fbr2 = 0x200,
+
+ /* CCCR */
+ Ioenable = 0x02,
+ Ioready = 0x03,
+ Intenable = 0x04,
+ Intpend = 0x05,
+ Ioabort = 0x06,
+ Busifc = 0x07,
+ Capability = 0x08,
+ Blksize = 0x10,
+ Highspeed = 0x13,
+
+ /* SELECT_CARD args */
+ Rcashift = 16,
+
+ /* SEND_OP_COND args */
+ Hcs = 1<<30, /* host supports SDHC & SDXC */
+ V3_3 = 3<<20, /* 3.2-3.4 volts */
+ V2_8 = 3<<15, /* 2.7-2.9 volts */
+ V2_0 = 1<<8, /* 2.0-2.1 volts */
+ S18R = 1<<24, /* switch to 1.8V request */
+
+ /* Sonics Silicon Backplane (access to cores on chip) */
+ Sbwsize = 0x8000,
+ Sb32bit = 0x8000,
+ Sbaddr = 0x1000a,
+ Enumbase = 0x18000000,
+ Framectl= 0x1000d,
+ Rfhalt = 0x01,
+ Wfhalt = 0x02,
+ Clkcsr = 0x1000e,
+ ForceALP = 0x01, /* active low-power clock */
+ ForceHT = 0x02, /* high throughput clock */
+ ForceILP = 0x04, /* idle low-power clock */
+ ReqALP = 0x08,
+ ReqHT = 0x10,
+ Nohwreq = 0x20,
+ ALPavail = 0x40,
+ HTavail = 0x80,
+ Pullups = 0x1000f,
+ Wfrmcnt = 0x10019,
+ Rfrmcnt = 0x1001b,
+
+ /* core control regs */
+ Ioctrl = 0x408,
+ Resetctrl = 0x800,
+
+ /* socram regs */
+ Coreinfo = 0x00,
+ Bankidx = 0x10,
+ Bankinfo = 0x40,
+ Bankpda = 0x44,
+
+ /* armcr4 regs */
+ Cr4Cap = 0x04,
+ Cr4Bankidx = 0x40,
+ Cr4Bankinfo = 0x44,
+ Cr4Cpuhalt = 0x20,
+
+ /* chipcommon regs */
+ Gpiopullup = 0x58,
+ Gpiopulldown = 0x5c,
+ Chipctladdr = 0x650,
+ Chipctldata = 0x654,
+
+ /* sdio core regs */
+ Intstatus = 0x20,
+ Fcstate = 1<<4,
+ Fcchange = 1<<5,
+ FrameInt = 1<<6,
+ MailboxInt = 1<<7,
+ Intmask = 0x24,
+ Sbmbox = 0x40,
+ Sbmboxdata = 0x48,
+ Hostmboxdata= 0x4c,
+ Fwready = 0x80,
+
+ /* wifi control commands */
+ GetVar = 262,
+ SetVar = 263,
+
+ /* status */
+ Disconnected= 0,
+ Connecting,
+ Connected,
+};
+
+typedef struct Ctlr Ctlr;
+
+enum{
+ Wpa = 1,
+ Wep = 2,
+ Wpa2 = 3,
+ WNameLen = 32,
+ WNKeys = 4,
+ WKeyLen = 32,
+ WMinKeyLen = 5,
+ WMaxKeyLen = 13,
+};
+
+typedef struct WKey WKey;
+struct WKey
+{
+ ushort len;
+ char dat[WKeyLen];
+};
+
+struct Ctlr {
+ Ether* edev;
+ QLock cmdlock;
+ QLock pktlock;
+ QLock tlock;
+ QLock alock;
+ Lock txwinlock;
+ Rendez cmdr;
+ Rendez joinr;
+ int joinstatus;
+ int cryptotype;
+ int chanid;
+ char essid[WNameLen + 1];
+ WKey keys[WNKeys];
+ Block *rsp;
+ Block *scanb;
+ int scansecs;
+ int status;
+ int chipid;
+ int chiprev;
+ int armcore;
+ char *regufile;
+ union {
+ u32int i;
+ uchar c[4];
+ } resetvec;
+ ulong chipcommon;
+ ulong armctl;
+ ulong armregs;
+ ulong d11ctl;
+ ulong socramregs;
+ ulong socramctl;
+ ulong sdregs;
+ int sdiorev;
+ int socramrev;
+ ulong socramsize;
+ ulong rambase;
+ short reqid;
+ uchar fcmask;
+ uchar txwindow;
+ uchar txseq;
+ uchar rxseq;
+};
+
+enum{
+ CMauth,
+ CMchannel,
+ CMcrypt,
+ CMessid,
+ CMkey1,
+ CMkey2,
+ CMkey3,
+ CMkey4,
+ CMrxkey,
+ CMrxkey0,
+ CMrxkey1,
+ CMrxkey2,
+ CMrxkey3,
+ CMtxkey,
+ CMdebug,
+ CMjoin,
+};
+
+static Cmdtab cmds[] = {
+ {CMauth, "auth", 2},
+ {CMchannel, "channel", 2},
+ {CMcrypt, "crypt", 2},
+ {CMessid, "essid", 2},
+ {CMkey1, "key1", 2},
+ {CMkey2, "key1", 2},
+ {CMkey3, "key1", 2},
+ {CMkey4, "key1", 2},
+ {CMrxkey, "rxkey", 3},
+ {CMrxkey0, "rxkey0", 3},
+ {CMrxkey1, "rxkey1", 3},
+ {CMrxkey2, "rxkey2", 3},
+ {CMrxkey3, "rxkey3", 3},
+ {CMtxkey, "txkey", 3},
+ {CMdebug, "debug", 2},
+ {CMjoin, "join", 5},
+};
+
+typedef struct Sdpcm Sdpcm;
+typedef struct Cmd Cmd;
+struct Sdpcm {
+ uchar len[2];
+ uchar lenck[2];
+ uchar seq;
+ uchar chanflg;
+ uchar nextlen;
+ uchar doffset;
+ uchar fcmask;
+ uchar window;
+ uchar version;
+ uchar pad;
+};
+
+struct Cmd {
+ uchar cmd[4];
+ uchar len[4];
+ uchar flags[2];
+ uchar id[2];
+ uchar status[4];
+};
+
+static char config40181[] = "bcmdhd.cal.40181";
+static char config40183[] = "bcmdhd.cal.40183.26MHz";
+
+struct {
+ int chipid;
+ int chiprev;
+ char *fwfile;
+ char *cfgfile;
+ char *regufile;
+} firmware[] = {
+ { 0x4330, 3, "fw_bcm40183b1.bin", config40183, 0 },
+ { 0x4330, 4, "fw_bcm40183b2.bin", config40183, 0 },
+ { 43362, 0, "fw_bcm40181a0.bin", config40181, 0 },
+ { 43362, 1, "fw_bcm40181a2.bin", config40181, 0 },
+ { 43430, 1, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt", 0 },
+ { 43430, 2, "brcmfmac43436-sdio.bin", "brcmfmac43436-sdio.txt", "brcmfmac43436-sdio.clm_blob" },
+ { 0x4345, 6, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt", "brcmfmac43455-sdio.clm_blob" },
+ { 0x4345, 9, "brcmfmac43456-sdio.bin", "brcmfmac43456-sdio.txt", "brcmfmac43456-sdio.clm_blob" },
+};
+
+static QLock sdiolock;
+static int iodebug;
+
+static void etherbcmintr(void *);
+static void bcmevent(Ctlr*, uchar*, int);
+static void wlscanresult(Ether*, uchar*, int);
+static void wlsetvar(Ctlr*, char*, void*, int);
+
+static uchar*
+put2(uchar *p, short v)
+{
+ p[0] = v;
+ p[1] = v >> 8;
+ return p + 2;
+}
+
+static uchar*
+put4(uchar *p, long v)
+{
+ p[0] = v;
+ p[1] = v >> 8;
+ p[2] = v >> 16;
+ p[3] = v >> 24;
+ return p + 4;
+}
+
+static ushort
+get2(uchar *p)
+{
+ return p[0] | p[1]<<8;
+}
+
+static ulong
+get4(uchar *p)
+{
+ return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
+}
+
+static void
+dump(char *s, void *a, int n)
+{
+ int i;
+ uchar *p;
+
+ p = a;
+ print("%s:", s);
+ for(i = 0; i < n; i++)
+ print("%c%2.2x", i&15? ' ' : '\n', *p++);
+ print("\n");
+}
+
+/*
+ * SDIO communication with dongle
+ */
+static ulong
+sdiocmd_locked(SDiocmd *cmd, ulong arg)
+{
+ u32int resp[4];
+
+ sdio.cmd(&sdio, cmd, arg, resp);
+ return resp[0];
+}
+
+static ulong
+sdiocmd(SDiocmd *cmd, ulong arg)
+{
+ ulong r;
+
+ qlock(&sdiolock);
+ if(waserror()){
+ if(SDIODEBUG) print("sdiocmd error: cmd %s arg %lux\n", cmd->name, arg);
+ qunlock(&sdiolock);
+ nexterror();
+ }
+ r = sdiocmd_locked(cmd, arg);
+ qunlock(&sdiolock);
+ poperror();
+ return r;
+
+}
+
+static ulong
+trysdiocmd(SDiocmd *cmd, ulong arg)
+{
+ ulong r;
+
+ if(waserror())
+ return 0;
+ r = sdiocmd(cmd, arg);
+ poperror();
+ return r;
+}
+
+static int
+sdiord(int fn, int addr)
+{
+ int r;
+
+ r = sdiocmd(&IO_RW_DIRECT, (0<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9));
+ if(r & 0xCF00){
+ print("ether4330: sdiord(%x, %x) fail: %2.2ux %2.2ux\n", fn, addr, (r>>8)&0xFF, r&0xFF);
+ error(Eio);
+ }
+ return r & 0xFF;
+}
+
+static void
+sdiowr(int fn, int addr, int data)
+{
+ int r;
+ int retry;
+ ulong arg;
+
+ r = 0;
+ for(retry = 0; retry < 10; retry++){
+ arg = (1<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)|(data&0xFF);
+ if((arg & ~0xFF) == (1<<31|0<<28|7<<9)){
+ switch(arg&3){
+ case 0:
+ sdio.bus(&sdio, 1, 0);
+ break;
+ case 2:
+ sdio.bus(&sdio, 4, 0);
+ break;
+ }
+ }
+ r = sdiocmd(&IO_RW_DIRECT, arg);
+ if((r & 0xCF00) == 0)
+ return;
+ }
+ print("ether4330: sdiowr(%x, %x, %x) fail: %2.2ux %2.2ux\n", fn, addr, data, (r>>8)&0xFF, r&0xFF);
+ error(Eio);
+}
+
+static void
+sdiorwext(int fn, int write, void *a, int len, int addr, int incr)
+{
+ int bsize, blk, bcount, m;
+ SDiocmd cmd = { 53, 1, 0, 0, "IO_RW_EXTENDED" };
+
+ bsize = fn == Fn2? 512 : 64;
+ while(len > 0){
+ if(len >= 511*bsize){
+ blk = 1;
+ bcount = 511;
+ m = bcount*bsize;
+ }else if(len > bsize){
+ blk = 1;
+ bcount = len/bsize;
+ m = bcount*bsize;
+ }else{
+ blk = 0;
+ bcount = len;
+ m = bcount;
+ }
+ qlock(&sdiolock);
+ if(waserror()){
+ print("ether4330: sdiorwext fail: %s\n", up->errstr);
+ qunlock(&sdiolock);
+ nexterror();
+ }
+ cmd.data = write? 2 : 1; /* Host2card : Card2host */
+ if(blk){
+ cmd.data += 2; /* Multiblock | Blkcnten */
+ sdio.iosetup(&sdio, write, a, bsize, bcount);
+ }else
+ sdio.iosetup(&sdio, write, a, bcount, 1);
+ sdiocmd_locked(&cmd, write<<31 | (fn&7)<<28 | blk<<27 | incr<<26 | (addr&0x1FFFF)<<9 | (bcount&0x1FF));
+ sdio.io(&sdio, write, a, m);
+ qunlock(&sdiolock);
+ poperror();
+ len -= m;
+ a = (char*)a + m;
+ if(incr)
+ addr += m;
+ }
+}
+
+static void
+sdioset(int fn, int addr, int bits)
+{
+ sdiowr(fn, addr, sdiord(fn, addr) | bits);
+}
+
+static void
+sdioinit(void)
+{
+ ulong ocr, rca;
+ int i;
+
+ /* disconnect emmc from SD card (connect sdhost instead) */
+ for(i = 48; i <= 53; i++)
+ gpiosel(i, Alt0);
+ /* connect emmc to wifi */
+ for(i = 34; i <= 39; i++){
+ gpiosel(i, Alt3);
+ if(i == 34)
+ gpiopulloff(i);
+ else
+ gpiopullup(i);
+ }
+ sdio.init(&sdio);
+ sdio.enable(&sdio);
+ sdiocmd(&GO_IDLE_STATE, 0);
+ ocr = trysdiocmd(&IO_SEND_OP_COND, 0);
+ i = 0;
+ while((ocr & (1<<31)) == 0){
+ if(++i > 5){
+ print("ether4330: no response to sdio access: ocr = %lux\n", ocr);
+ error(Eio);
+ }
+ ocr = trysdiocmd(&IO_SEND_OP_COND, V3_3);
+ tsleep(&up->sleep, return0, nil, 100);
+ }
+ rca = sdiocmd(&SEND_RELATIVE_ADDR, 0) >> Rcashift;
+ sdiocmd(&SELECT_CARD, rca << Rcashift);
+ sdio.bus(&sdio, 0, SDfreq);
+ sdioset(Fn0, Highspeed, 2);
+ sdioset(Fn0, Busifc, 2); /* bus width 4 */
+ sdiowr(Fn0, Fbr1+Blksize, 64);
+ sdiowr(Fn0, Fbr1+Blksize+1, 64>>8);
+ sdiowr(Fn0, Fbr2+Blksize, 512);
+ sdiowr(Fn0, Fbr2+Blksize+1, 512>>8);
+ sdioset(Fn0, Ioenable, 1<<Fn1);
+ sdiowr(Fn0, Intenable, 0);
+ for(i = 0; !(sdiord(Fn0, Ioready) & 1<<Fn1); i++){
+ if(i == 10){
+ print("ether4330: can't enable SDIO function\n");
+ error(Eio);
+ }
+ tsleep(&up->sleep, return0, nil, 100);
+ }
+}
+
+static void
+sdioreset(void)
+{
+ sdiowr(Fn0, Ioabort, 1<<3); /* reset */
+}
+
+static void
+sdioabort(int fn)
+{
+ sdiowr(Fn0, Ioabort, fn);
+}
+
+/*
+ * Chip register and memory access via SDIO
+ */
+
+static void
+cfgw(ulong off, int val)
+{
+ sdiowr(Fn1, off, val);
+}
+
+static int
+cfgr(ulong off)
+{
+ return sdiord(Fn1, off);
+}
+
+static ulong
+cfgreadl(int fn, ulong off)
+{
+ uchar cbuf[2*CACHELINESZ];
+ uchar *p;
+
+ p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ);
+ memset(p, 0, 4);
+ sdiorwext(fn, 0, p, 4, off|Sb32bit, 1);
+ if(SDIODEBUG) print("cfgreadl %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]);
+ return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
+}
+
+static void
+cfgwritel(int fn, ulong off, u32int data)
+{
+ uchar cbuf[2*CACHELINESZ];
+ uchar *p;
+ int retry;
+
+ p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ);
+ put4(p, data);
+ if(SDIODEBUG) print("cfgwritel %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]);
+ retry = 0;
+ while(waserror()){
+ print("ether4330: cfgwritel retry %lux %ux\n", off, data);
+ sdioabort(fn);
+ if(++retry == 3)
+ nexterror();
+ }
+ sdiorwext(fn, 1, p, 4, off|Sb32bit, 1);
+ poperror();
+}
+
+static void
+sbwindow(ulong addr)
+{
+ addr &= ~(Sbwsize-1);
+ cfgw(Sbaddr, addr>>8);
+ cfgw(Sbaddr+1, addr>>16);
+ cfgw(Sbaddr+2, addr>>24);
+}
+
+static void
+sbrw(int fn, int write, uchar *buf, int len, ulong off)
+{
+ int n;
+ USED(fn);
+
+ if(waserror()){
+ print("ether4330: sbrw err off %lux len %ud\n", off, len);
+ nexterror();
+ }
+ if(write){
+ if(len >= 4){
+ n = len;
+ n &= ~3;
+ sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1);
+ off += n;
+ buf += n;
+ len -= n;
+ }
+ while(len > 0){
+ sdiowr(Fn1, off|Sb32bit, *buf);
+ off++;
+ buf++;
+ len--;
+ }
+ }else{
+ if(len >= 4){
+ n = len;
+ n &= ~3;
+ sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1);
+ off += n;
+ buf += n;
+ len -= n;
+ }
+ while(len > 0){
+ *buf = sdiord(Fn1, off|Sb32bit);
+ off++;
+ buf++;
+ len--;
+ }
+ }
+ poperror();
+}
+
+static void
+sbmem(int write, uchar *buf, int len, ulong off)
+{
+ ulong n;
+
+ n = ROUNDUP(off, Sbwsize) - off;
+ if(n == 0)
+ n = Sbwsize;
+ while(len > 0){
+ if(n > len)
+ n = len;
+ sbwindow(off);
+ sbrw(Fn1, write, buf, n, off & (Sbwsize-1));
+ off += n;
+ buf += n;
+ len -= n;
+ n = Sbwsize;
+ }
+}
+
+static void
+packetrw(int write, uchar *buf, int len)
+{
+ int n;
+ int retry;
+
+ n = 2048;
+ while(len > 0){
+ if(n > len)
+ n = ROUND(len, 4);
+ retry = 0;
+ while(waserror()){
+ sdioabort(Fn2);
+ if(++retry == 3)
+ nexterror();
+ }
+ sdiorwext(Fn2, write, buf, n, Enumbase, 0);
+ poperror();
+ buf += n;
+ len -= n;
+ }
+}
+
+/*
+ * Configuration and control of chip cores via Silicon Backplane
+ */
+
+static void
+sbdisable(ulong regs, int pre, int ioctl)
+{
+ sbwindow(regs);
+ if((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){
+ cfgwritel(Fn1, regs + Ioctrl, 3|ioctl);
+ cfgreadl(Fn1, regs + Ioctrl);
+ return;
+ }
+ cfgwritel(Fn1, regs + Ioctrl, 3|pre);
+ cfgreadl(Fn1, regs + Ioctrl);
+ cfgwritel(Fn1, regs + Resetctrl, 1);
+ microdelay(10);
+ while((cfgreadl(Fn1, regs + Resetctrl) & 1) == 0)
+ ;
+ cfgwritel(Fn1, regs + Ioctrl, 3|ioctl);
+ cfgreadl(Fn1, regs + Ioctrl);
+}
+
+static void
+sbreset(ulong regs, int pre, int ioctl)
+{
+ sbdisable(regs, pre, ioctl);
+ sbwindow(regs);
+ if(SBDEBUG) print("sbreset %#lux %#lux %#lux ->", regs,
+ cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl));
+ while((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){
+ cfgwritel(Fn1, regs + Resetctrl, 0);
+ microdelay(40);
+ }
+ cfgwritel(Fn1, regs + Ioctrl, 1|ioctl);
+ cfgreadl(Fn1, regs + Ioctrl);
+ if(SBDEBUG) print("%#lux %#lux\n",
+ cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl));
+}
+
+static void
+corescan(Ctlr *ctl, ulong r)
+{
+ uchar *buf;
+ int i, coreid, corerev;
+ ulong addr;
+
+ buf = sdmalloc(Corescansz);
+ if(buf == nil)
+ error(Enomem);
+ sbmem(0, buf, Corescansz, r);
+ coreid = 0;
+ corerev = 0;
+ for(i = 0; i < Corescansz; i += 4){
+ switch(buf[i]&0xF){
+ case 0xF: /* end */
+ sdfree(buf);
+ return;
+ case 0x1: /* core info */
+ if((buf[i+4]&0xF) != 0x1)
+ break;
+ coreid = (buf[i+1] | buf[i+2]<<8) & 0xFFF;
+ i += 4;
+ corerev = buf[i+3];
+ break;
+ case 0x05: /* address */
+ addr = buf[i+1]<<8 | buf[i+2]<<16 | buf[i+3]<<24;
+ addr &= ~0xFFF;
+ if(SBDEBUG) print("core %x %s %#lux\n", coreid, buf[i]&0xC0? "ctl" : "mem", addr);
+ switch(coreid){
+ case 0x800:
+ if((buf[i] & 0xC0) == 0)
+ ctl->chipcommon = addr;
+ break;
+ case ARMcm3:
+ case ARM7tdmi:
+ case ARMcr4:
+ ctl->armcore = coreid;
+ if(buf[i] & 0xC0){
+ if(ctl->armctl == 0)
+ ctl->armctl = addr;
+ }else{
+ if(ctl->armregs == 0)
+ ctl->armregs = addr;
+ }
+ break;
+ case 0x80E:
+ if(buf[i] & 0xC0)
+ ctl->socramctl = addr;
+ else if(ctl->socramregs == 0)
+ ctl->socramregs = addr;
+ ctl->socramrev = corerev;
+ break;
+ case 0x829:
+ if((buf[i] & 0xC0) == 0)
+ ctl->sdregs = addr;
+ ctl->sdiorev = corerev;
+ break;
+ case 0x812:
+ if(buf[i] & 0xC0)
+ ctl->d11ctl = addr;
+ break;
+ }
+ }
+ }
+ sdfree(buf);
+}
+
+static void
+ramscan(Ctlr *ctl)
+{
+ ulong r, n, size;
+ int banks, i;
+
+ if(ctl->armcore == ARMcr4){
+ r = ctl->armregs;
+ sbwindow(r);
+ n = cfgreadl(Fn1, r + Cr4Cap);
+ if(SBDEBUG) print("cr4 banks %lux\n", n);
+ banks = ((n>>4) & 0xF) + (n & 0xF);
+ size = 0;
+ for(i = 0; i < banks; i++){
+ cfgwritel(Fn1, r + Cr4Bankidx, i);
+ n = cfgreadl(Fn1, r + Cr4Bankinfo);
+ if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1));
+ size += 8192 * ((n & 0x3F) + 1);
+ }
+ ctl->socramsize = size;
+ ctl->rambase = 0x198000;
+ return;
+ }
+ if(ctl->socramrev <= 7 || ctl->socramrev == 12){
+ print("ether4330: SOCRAM rev %d not supported\n", ctl->socramrev);
+ error(Eio);
+ }
+ sbreset(ctl->socramctl, 0, 0);
+ r = ctl->socramregs;
+ sbwindow(r);
+ n = cfgreadl(Fn1, r + Coreinfo);
+ if(SBDEBUG) print("socramrev %d coreinfo %lux\n", ctl->socramrev, n);
+ banks = (n>>4) & 0xF;
+ size = 0;
+ for(i = 0; i < banks; i++){
+ cfgwritel(Fn1, r + Bankidx, i);
+ n = cfgreadl(Fn1, r + Bankinfo);
+ if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1));
+ size += 8192 * ((n & 0x3F) + 1);
+ }
+ ctl->socramsize = size;
+ ctl->rambase = 0;
+ if(ctl->chipid == 43430){
+ cfgwritel(Fn1, r + Bankidx, 3);
+ cfgwritel(Fn1, r + Bankpda, 0);
+ }
+}
+
+static void
+sbinit(Ctlr *ctl)
+{
+ ulong r;
+ int chipid;
+ char buf[16];
+
+ sbwindow(Enumbase);
+ r = cfgreadl(Fn1, Enumbase);
+ chipid = r & 0xFFFF;
+ sprint(buf, chipid > 43000 ? "%d" : "%#x", chipid);
+ print("ether4330: chip %s rev %ld type %ld\n", buf, (r>>16)&0xF, (r>>28)&0xF);
+ switch(chipid){
+ case 0x4330:
+ case 43362:
+ case 43430:
+ case 0x4345:
+ ctl->chipid = chipid;
+ ctl->chiprev = (r>>16)&0xF;
+ break;
+ default:
+ print("ether4330: chipid %#x (%d) not supported\n", chipid, chipid);
+ error(Eio);
+ }
+ r = cfgreadl(Fn1, Enumbase + 63*4);
+ corescan(ctl, r);
+ if(ctl->armctl == 0 || ctl->d11ctl == 0 ||
+ (ctl->armcore == ARMcm3 && (ctl->socramctl == 0 || ctl->socramregs == 0)))
+ error("corescan didn't find essential cores\n");
+ if(ctl->armcore == ARMcr4)
+ sbreset(ctl->armctl, Cr4Cpuhalt, Cr4Cpuhalt);
+ else
+ sbdisable(ctl->armctl, 0, 0);
+ sbreset(ctl->d11ctl, 8|4, 4);
+ ramscan(ctl);
+ if(SBDEBUG) print("ARM %#lux D11 %#lux SOCRAM %#lux,%#lux %lud bytes @ %#lux\n",
+ ctl->armctl, ctl->d11ctl, ctl->socramctl, ctl->socramregs, ctl->socramsize, ctl->rambase);
+ cfgw(Clkcsr, 0);
+ microdelay(10);
+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
+ cfgw(Clkcsr, Nohwreq | ReqALP);
+ while((cfgr(Clkcsr) & (HTavail|ALPavail)) == 0)
+ microdelay(10);
+ cfgw(Clkcsr, Nohwreq | ForceALP);
+ microdelay(65);
+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
+ cfgw(Pullups, 0);
+ sbwindow(ctl->chipcommon);
+ cfgwritel(Fn1, ctl->chipcommon + Gpiopullup, 0);
+ cfgwritel(Fn1, ctl->chipcommon + Gpiopulldown, 0);
+ if(ctl->chipid != 0x4330 && ctl->chipid != 43362)
+ return;
+ cfgwritel(Fn1, ctl->chipcommon + Chipctladdr, 1);
+ if(cfgreadl(Fn1, ctl->chipcommon + Chipctladdr) != 1)
+ print("ether4330: can't set Chipctladdr\n");
+ else{
+ r = cfgreadl(Fn1, ctl->chipcommon + Chipctldata);
+ if(SBDEBUG) print("chipcommon PMU (%lux) %lux", cfgreadl(Fn1, ctl->chipcommon + Chipctladdr), r);
+ /* set SDIO drive strength >= 6mA */
+ r &= ~0x3800;
+ if(ctl->chipid == 0x4330)
+ r |= 3<<11;
+ else
+ r |= 7<<11;
+ cfgwritel(Fn1, ctl->chipcommon + Chipctldata, r);
+ if(SBDEBUG) print("-> %lux (= %lux)\n", r, cfgreadl(Fn1, ctl->chipcommon + Chipctldata));
+ }
+}
+
+static void
+sbenable(Ctlr *ctl)
+{
+ int i;
+
+ if(SBDEBUG) print("enabling HT clock...");
+ cfgw(Clkcsr, 0);
+ delay(1);
+ cfgw(Clkcsr, ReqHT);
+ for(i = 0; (cfgr(Clkcsr) & HTavail) == 0; i++){
+ if(i == 50){
+ print("ether4330: can't enable HT clock: csr %x\n", cfgr(Clkcsr));
+ error(Eio);
+ }
+ tsleep(&up->sleep, return0, nil, 100);
+ }
+ cfgw(Clkcsr, cfgr(Clkcsr) | ForceHT);
+ delay(10);
+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
+ sbwindow(ctl->sdregs);
+ cfgwritel(Fn1, ctl->sdregs + Sbmboxdata, 4 << 16); /* protocol version */
+ cfgwritel(Fn1, ctl->sdregs + Intmask, FrameInt | MailboxInt | Fcchange);
+ sdioset(Fn0, Ioenable, 1<<Fn2);
+ for(i = 0; !(sdiord(Fn0, Ioready) & 1<<Fn2); i++){
+ if(i == 10){
+ print("ether4330: can't enable SDIO function 2 - ioready %x\n", sdiord(Fn0, Ioready));
+ error(Eio);
+ }
+ tsleep(&up->sleep, return0, nil, 100);
+ }
+ sdiowr(Fn0, Intenable, (1<<Fn1) | (1<<Fn2) | 1);
+}
+
+
+/*
+ * Firmware and config file uploading
+ */
+
+/*
+ * Condense config file contents (in buffer buf with length n)
+ * to 'var=value\0' list for firmware:
+ * - remove comments (starting with '#') and blank lines
+ * - remove carriage returns
+ * - convert newlines to nulls
+ * - mark end with two nulls
+ * - pad with nulls to multiple of 4 bytes total length
+ */
+static int
+condense(uchar *buf, int n)
+{
+ uchar *p, *ep, *lp, *op;
+ int c, skipping;
+
+ skipping = 0; /* true if in a comment */
+ ep = buf + n; /* end of input */
+ op = buf; /* end of output */
+ lp = buf; /* start of current output line */
+ for(p = buf; p < ep; p++){
+ switch(c = *p){
+ case '#':
+ skipping = 1;
+ break;
+ case '\0':
+ case '\n':
+ skipping = 0;
+ if(op != lp){
+ *op++ = '\0';
+ lp = op;
+ }
+ break;
+ case '\r':
+ break;
+ default:
+ if(!skipping)
+ *op++ = c;
+ break;
+ }
+ }
+ if(!skipping && op != lp)
+ *op++ = '\0';
+ *op++ = '\0';
+ for(n = op - buf; n & 03; n++)
+ *op++ = '\0';
+ return n;
+}
+
+/*
+ * Try to find firmware file in /boot or in /lib/firmware.
+ * Throw an error if not found.
+ */
+static Chan*
+findfirmware(char *file)
+{
+ char nbuf[64];
+ Chan *c;
+
+ if(!waserror()){
+ snprint(nbuf, sizeof nbuf, "/boot/%s", file);
+ c = namec(nbuf, Aopen, OREAD, 0);
+ poperror();
+ }else if(!waserror()){
+ snprint(nbuf, sizeof nbuf, "/lib/firmware/%s", file);
+ c = namec(nbuf, Aopen, OREAD, 0);
+ poperror();
+ }else{
+ c = nil;
+ snprint(up->genbuf, sizeof up->genbuf, "can't find %s in /boot or /lib/firmware", file);
+ error(up->genbuf);
+ }
+ return c;
+}
+
+static int
+upload(Ctlr *ctl, char *file, int isconfig)
+{
+ Chan *c;
+ uchar *buf;
+ uchar *cbuf;
+ int off, n;
+
+ buf = cbuf = nil;
+ c = findfirmware(file);
+ if(waserror()){
+ cclose(c);
+ sdfree(buf);
+ sdfree(cbuf);
+ nexterror();
+ }
+ buf = sdmalloc(Uploadsz);
+ if(buf == nil)
+ error(Enomem);
+ if(Firmwarecmp){
+ cbuf = sdmalloc(Uploadsz);
+ if(cbuf == nil)
+ error(Enomem);
+ }
+ off = 0;
+ for(;;){
+ n = devtab[c->type]->read(c, buf, Uploadsz, off);
+ if(n <= 0)
+ break;
+ if(isconfig){
+ n = condense(buf, n);
+ off = ctl->socramsize - n - 4;
+ }else if(off == 0)
+ memmove(ctl->resetvec.c, buf, sizeof(ctl->resetvec.c));
+ while(n&3)
+ buf[n++] = 0;
+ sbmem(1, buf, n, ctl->rambase + off);
+ if(isconfig)
+ break;
+ off += n;
+ }
+ if(Firmwarecmp){
+ if(FWDEBUG) print("compare...");
+ if(!isconfig)
+ off = 0;
+ for(;;){
+ if(!isconfig){
+ n = devtab[c->type]->read(c, buf, Uploadsz, off);
+ if(n <= 0)
+ break;
+ while(n&3)
+ buf[n++] = 0;
+ }
+ sbmem(0, cbuf, n, ctl->rambase + off);
+ if(memcmp(buf, cbuf, n) != 0){
+ print("ether4330: firmware load failed offset %d\n", off);
+ error(Eio);
+ }
+ if(isconfig)
+ break;
+ off += n;
+ }
+ }
+ if(FWDEBUG) print("\n");
+ poperror();
+ cclose(c);
+ sdfree(buf);
+ sdfree(cbuf);
+ return n;
+}
+
+/*
+ * Upload regulatory file (.clm) to firmware.
+ * Packet format is
+ * [2]flag [2]type [4]len [4]crc [len]data
+ */
+static void
+reguload(Ctlr *ctl, char *file)
+{
+ Chan *c;
+ uchar *buf;
+ int off, n, flag;
+ enum {
+ Reguhdr = 2+2+4+4,
+ Regusz = 1400,
+ Regutyp = 2,
+ Flagclm = 1<<12,
+ Firstpkt= 1<<1,
+ Lastpkt = 1<<2,
+ };
+
+ buf = nil;
+ c = findfirmware(file);
+ if(waserror()){
+ cclose(c);
+ free(buf);
+ nexterror();
+ }
+ buf = malloc(Reguhdr+Regusz+1);
+ if(buf == nil)
+ error(Enomem);
+ put2(buf+2, Regutyp);
+ put2(buf+8, 0);
+ off = 0;
+ flag = Flagclm | Firstpkt;
+ while((flag&Lastpkt) == 0){
+ n = devtab[c->type]->read(c, buf+Reguhdr, Regusz+1, off);
+ if(n <= 0)
+ break;
+ if(n == Regusz+1)
+ --n;
+ else{
+ while(n&7)
+ buf[Reguhdr+n++] = 0;
+ flag |= Lastpkt;
+ }
+ put2(buf+0, flag);
+ put4(buf+4, n);
+ wlsetvar(ctl, "clmload", buf, Reguhdr + n);
+ off += n;
+ flag &= ~Firstpkt;
+ }
+ poperror();
+ cclose(c);
+ free(buf);
+}
+
+static void
+fwload(Ctlr *ctl)
+{
+ uchar buf[4];
+ uint i, n;
+
+ i = 0;
+ while(firmware[i].chipid != ctl->chipid ||
+ firmware[i].chiprev != ctl->chiprev){
+ if(++i == nelem(firmware)){
+ print("ether4330: no firmware for chipid %x (%d) chiprev %d\n",
+ ctl->chipid, ctl->chipid, ctl->chiprev);
+ error("no firmware");
+ }
+ }
+ ctl->regufile = firmware[i].regufile;
+ cfgw(Clkcsr, ReqALP);
+ while((cfgr(Clkcsr) & ALPavail) == 0)
+ microdelay(10);
+ memset(buf, 0, 4);
+ sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4);
+ if(FWDEBUG) print("firmware load...");
+ upload(ctl, firmware[i].fwfile, 0);
+ if(FWDEBUG) print("config load...");
+ n = upload(ctl, firmware[i].cfgfile, 1);
+ n /= 4;
+ n = (n & 0xFFFF) | (~n << 16);
+ put4(buf, n);
+ sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4);
+ if(ctl->armcore == ARMcr4){
+ sbwindow(ctl->sdregs);
+ cfgwritel(Fn1, ctl->sdregs + Intstatus, ~0);
+ if(ctl->resetvec.i != 0){
+ if(SBDEBUG) print("%ux\n", ctl->resetvec.i);
+ sbmem(1, ctl->resetvec.c, sizeof(ctl->resetvec.c), 0);
+ }
+ sbreset(ctl->armctl, Cr4Cpuhalt, 0);
+ }else
+ sbreset(ctl->armctl, 0, 0);
+}
+
+/*
+ * Communication of data and control packets
+ */
+
+void
+intwait(Ctlr *ctlr, int wait)
+{
+ ulong ints, mbox;
+ int i;
+
+ if(waserror())
+ return;
+ for(;;){
+ sdiocardintr(wait);
+ sbwindow(ctlr->sdregs);
+ i = sdiord(Fn0, Intpend);
+ if(i == 0){
+ tsleep(&up->sleep, return0, 0, 10);
+ continue;
+ }
+ ints = cfgreadl(Fn1, ctlr->sdregs + Intstatus);
+ cfgwritel(Fn1, ctlr->sdregs + Intstatus, ints);
+ if(0) print("INTS: (%x) %lux -> %lux\n", i, ints, cfgreadl(Fn1, ctlr->sdregs + Intstatus));
+ if(ints & MailboxInt){
+ mbox = cfgreadl(Fn1, ctlr->sdregs + Hostmboxdata);
+ cfgwritel(Fn1, ctlr->sdregs + Sbmbox, 2); /* ack */
+ if(mbox & 0x8)
+ print("ether4330: firmware ready\n");
+ }
+ if(ints & FrameInt)
+ break;
+ }
+ poperror();
+}
+
+static Block*
+wlreadpkt(Ctlr *ctl)
+{
+ Block *b;
+ Sdpcm *p;
+ int len, lenck;
+
+ b = allocb(2048);
+ p = (Sdpcm*)b->wp;
+ qlock(&ctl->pktlock);
+ for(;;){
+ packetrw(0, b->wp, Sdpcmsz);
+ len = p->len[0] | p->len[1]<<8;
+ if(len == 0){
+ freeb(b);
+ b = nil;
+ break;
+ }
+ lenck = p->lenck[0] | p->lenck[1]<<8;
+ if(lenck != (len ^ 0xFFFF) ||
+ len < Sdpcmsz || len > 2048){
+ print("ether4330: wlreadpkt error len %.4x lenck %.4x\n", len, lenck);
+ cfgw(Framectl, Rfhalt);
+ while(cfgr(Rfrmcnt+1))
+ ;
+ while(cfgr(Rfrmcnt))
+ ;
+ continue;
+ }
+ if(len > Sdpcmsz)
+ packetrw(0, b->wp + Sdpcmsz, len - Sdpcmsz);
+ b->wp += len;
+ break;
+ }
+ qunlock(&ctl->pktlock);
+ return b;
+}
+
+static void
+txstart(Ether *edev)
+{
+ Ctlr *ctl;
+ Sdpcm *p;
+ Block *b;
+ int len, off;
+
+ ctl = edev->ctlr;
+ if(!canqlock(&ctl->tlock))
+ return;
+ if(waserror()){
+ qunlock(&ctl->tlock);
+ return;
+ }
+ for(;;){
+ lock(&ctl->txwinlock);
+ if(ctl->txseq == ctl->txwindow){
+ //print("f");
+ unlock(&ctl->txwinlock);
+ break;
+ }
+ if(ctl->fcmask & 1<<2){
+ //print("x");
+ unlock(&ctl->txwinlock);
+ break;
+ }
+ unlock(&ctl->txwinlock);
+ b = qget(edev->oq);
+ if(b == nil)
+ break;
+ off = ((uintptr)b->rp & 3) + Sdpcmsz;
+ b = padblock(b, off + 4);
+ len = BLEN(b);
+ p = (Sdpcm*)b->rp;
+ memset(p, 0, off); /* TODO: refactor dup code */
+ put2(p->len, len);
+ put2(p->lenck, ~len);
+ p->chanflg = 2;
+ p->seq = ctl->txseq;
+ p->doffset = off;
+ put4(b->rp + off, 0x20); /* BDC header */
+ if(iodebug) dump("send", b->rp, len);
+ qlock(&ctl->pktlock);
+ if(waserror()){
+ if(iodebug) print("halt frame %x %x\n", cfgr(Wfrmcnt+1), cfgr(Wfrmcnt+1));
+ cfgw(Framectl, Wfhalt);
+ while(cfgr(Wfrmcnt+1))
+ ;
+ while(cfgr(Wfrmcnt))
+ ;
+ qunlock(&ctl->pktlock);
+ nexterror();
+ }
+ packetrw(1, b->rp, len);
+ ctl->txseq++;
+ poperror();
+ qunlock(&ctl->pktlock);
+ freeb(b);
+ }
+ poperror();
+ qunlock(&ctl->tlock);
+}
+
+static void
+rproc(void *a)
+{
+ Ether *edev;
+ Ctlr *ctl;
+ Block *b;
+ Sdpcm *p;
+ Cmd *q;
+ int flowstart;
+ int bdc;
+
+ edev = a;
+ ctl = edev->ctlr;
+ flowstart = 0;
+ for(;;){
+ if(flowstart){
+ //print("F");
+ flowstart = 0;
+ txstart(edev);
+ }
+ b = wlreadpkt(ctl);
+ if(b == nil){
+ intwait(ctl, 1);
+ continue;
+ }
+ p = (Sdpcm*)b->rp;
+ if(p->window != ctl->txwindow || p->fcmask != ctl->fcmask){
+ lock(&ctl->txwinlock);
+ if(p->window != ctl->txwindow){
+ if(ctl->txseq == ctl->txwindow)
+ flowstart = 1;
+ ctl->txwindow = p->window;
+ }
+ if(p->fcmask != ctl->fcmask){
+ if((p->fcmask & 1<<2) == 0)
+ flowstart = 1;
+ ctl->fcmask = p->fcmask;
+ }
+ unlock(&ctl->txwinlock);
+ }
+ switch(p->chanflg & 0xF){
+ case 0:
+ if(iodebug) dump("rsp", b->rp, BLEN(b));
+ if(BLEN(b) < Sdpcmsz + Cmdsz)
+ break;
+ q = (Cmd*)(b->rp + Sdpcmsz);
+ if((q->id[0] | q->id[1]<<8) != ctl->reqid)
+ break;
+ ctl->rsp = b;
+ wakeup(&ctl->cmdr);
+ continue;
+ case 1:
+ if(iodebug) dump("event", b->rp, BLEN(b));
+ if(BLEN(b) > p->doffset + 4){
+ bdc = 4 + (b->rp[p->doffset + 3] << 2);
+ if(BLEN(b) > p->doffset + bdc){
+ b->rp += p->doffset + bdc; /* skip BDC header */
+ bcmevent(ctl, b->rp, BLEN(b));
+ break;
+ }
+ }
+ if(iodebug && BLEN(b) != p->doffset)
+ print("short event %lld %d\n", BLEN(b), p->doffset);
+ break;
+ case 2:
+ if(iodebug) dump("packet", b->rp, BLEN(b));
+ if(BLEN(b) > p->doffset + 4){
+ bdc = 4 + (b->rp[p->doffset + 3] << 2);
+ if(BLEN(b) >= p->doffset + bdc + ETHERHDRSIZE){
+ b->rp += p->doffset + bdc; /* skip BDC header */
+ etheriq(edev, b);
+ continue;
+ }
+ }
+ break;
+ default:
+ dump("ether4330: bad packet", b->rp, BLEN(b));
+ break;
+ }
+ freeb(b);
+ }
+}
+
+static void
+linkdown(Ctlr *ctl)
+{
+ Ether *edev;
+ Netfile *f;
+ int i;
+
+ edev = ctl->edev;
+ if(edev == nil || ctl->status != Connected)
+ return;
+ ctl->status = Disconnected;
+ /* send eof to aux/wpa */
+ for(i = 0; i < edev->nfile; i++){
+ f = edev->f[i];
+ if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e)
+ continue;
+ qwrite(f->in, 0, 0);
+ }
+}
+
+/*
+ * Command interface between host and firmware
+ */
+
+static char *eventnames[] = {
+ [0] = "set ssid",
+ [1] = "join",
+ [2] = "start",
+ [3] = "auth",
+ [4] = "auth ind",
+ [5] = "deauth",
+ [6] = "deauth ind",
+ [7] = "assoc",
+ [8] = "assoc ind",
+ [9] = "reassoc",
+ [10] = "reassoc ind",
+ [11] = "disassoc",
+ [12] = "disassoc ind",
+ [13] = "quiet start",
+ [14] = "quiet end",
+ [15] = "beacon rx",
+ [16] = "link",
+ [17] = "mic error",
+ [18] = "ndis link",
+ [19] = "roam",
+ [20] = "txfail",
+ [21] = "pmkid cache",
+ [22] = "retrograde tsf",
+ [23] = "prune",
+ [24] = "autoauth",
+ [25] = "eapol msg",
+ [26] = "scan complete",
+ [27] = "addts ind",
+ [28] = "delts ind",
+ [29] = "bcnsent ind",
+ [30] = "bcnrx msg",
+ [31] = "bcnlost msg",
+ [32] = "roam prep",
+ [33] = "pfn net found",
+ [34] = "pfn net lost",
+ [35] = "reset complete",
+ [36] = "join start",
+ [37] = "roam start",
+ [38] = "assoc start",
+ [39] = "ibss assoc",
+ [40] = "radio",
+ [41] = "psm watchdog",
+ [44] = "probreq msg",
+ [45] = "scan confirm ind",
+ [46] = "psk sup",
+ [47] = "country code changed",
+ [48] = "exceeded medium time",
+ [49] = "icv error",
+ [50] = "unicast decode error",
+ [51] = "multicast decode error",
+ [52] = "trace",
+ [53] = "bta hci event",
+ [54] = "if",
+ [55] = "p2p disc listen complete",
+ [56] = "rssi",
+ [57] = "pfn scan complete",
+ [58] = "extlog msg",
+ [59] = "action frame",
+ [60] = "action frame complete",
+ [61] = "pre assoc ind",
+ [62] = "pre reassoc ind",
+ [63] = "channel adopted",
+ [64] = "ap started",
+ [65] = "dfs ap stop",
+ [66] = "dfs ap resume",
+ [67] = "wai sta event",
+ [68] = "wai msg",
+ [69] = "escan result",
+ [70] = "action frame off chan complete",
+ [71] = "probresp msg",
+ [72] = "p2p probreq msg",
+ [73] = "dcs request",
+ [74] = "fifo credit map",
+ [75] = "action frame rx",
+ [76] = "wake event",
+ [77] = "rm complete",
+ [78] = "htsfsync",
+ [79] = "overlay req",
+ [80] = "csa complete ind",
+ [81] = "excess pm wake event",
+ [82] = "pfn scan none",
+ [83] = "pfn scan allgone",
+ [84] = "gtk plumbed",
+ [85] = "assoc ind ndis",
+ [86] = "reassoc ind ndis",
+ [87] = "assoc req ie",
+ [88] = "assoc resp ie",
+ [89] = "assoc recreated",
+ [90] = "action frame rx ndis",
+ [91] = "auth req",
+ [92] = "tdls peer event",
+ [127] = "bcmc credit support"
+};
+
+static char*
+evstring(uint event)
+{
+ static char buf[12];
+
+ if(event >= nelem(eventnames) || eventnames[event] == 0){
+ /* not reentrant but only called from one kproc */
+ snprint(buf, sizeof buf, "%d", event);
+ return buf;
+ }
+ return eventnames[event];
+}
+
+static void
+bcmevent(Ctlr *ctl, uchar *p, int len)
+{
+ int flags;
+ long event, status, reason;
+
+ if(len < ETHERHDRSIZE + 10 + 46)
+ return;
+ p += ETHERHDRSIZE + 10; /* skip bcm_ether header */
+ len -= ETHERHDRSIZE + 10;
+ flags = nhgets(p + 2);
+ event = nhgets(p + 6);
+ status = nhgetl(p + 8);
+ reason = nhgetl(p + 12);
+ if(EVENTDEBUG)
+ print("ether4330: [%s] status %ld flags %#x reason %ld\n",
+ evstring(event), status, flags, reason);
+ switch(event){
+ case 19: /* E_ROAM */
+ if(status == 0)
+ break;
+ /* fall through */
+ case 0: /* E_SET_SSID */
+ ctl->joinstatus = 1 + status;
+ wakeup(&ctl->joinr);
+ break;
+ case 16: /* E_LINK */
+ if(flags&1) /* link up */
+ break;
+ /* fall through */
+ case 5: /* E_DEAUTH */
+ case 6: /* E_DEAUTH_IND */
+ case 12: /* E_DISASSOC_IND */
+ linkdown(ctl);
+ break;
+ case 26: /* E_SCAN_COMPLETE */
+ break;
+ case 69: /* E_ESCAN_RESULT */
+ wlscanresult(ctl->edev, p + 48, len - 48);
+ break;
+ default:
+ if(status){
+ if(!EVENTDEBUG)
+ print("ether4330: [%s] error status %ld flags %#x reason %ld\n",
+ evstring(event), status, flags, reason);
+ dump("event", p, len);
+ }
+ }
+}
+
+static int
+joindone(void *a)
+{
+ return ((Ctlr*)a)->joinstatus;
+}
+
+static int
+waitjoin(Ctlr *ctl)
+{
+ int n;
+
+ sleep(&ctl->joinr, joindone, ctl);
+ n = ctl->joinstatus;
+ ctl->joinstatus = 0;
+ return n - 1;
+}
+
+static int
+cmddone(void *a)
+{
+ return ((Ctlr*)a)->rsp != nil;
+}
+
+static void
+wlcmd(Ctlr *ctl, int write, int op, void *data, int dlen, void *res, int rlen)
+{
+ Block *b;
+ Sdpcm *p;
+ Cmd *q;
+ int len, tlen;
+
+ if(write)
+ tlen = dlen + rlen;
+ else
+ tlen = MAX(dlen, rlen);
+ len = Sdpcmsz + Cmdsz + tlen;
+ b = allocb(len);
+ qlock(&ctl->cmdlock);
+ if(waserror()){
+ freeb(b);
+ qunlock(&ctl->cmdlock);
+ nexterror();
+ }
+ memset(b->wp, 0, len);
+ qlock(&ctl->pktlock);
+ p = (Sdpcm*)b->wp;
+ put2(p->len, len);
+ put2(p->lenck, ~len);
+ p->seq = ctl->txseq;
+ p->doffset = Sdpcmsz;
+ b->wp += Sdpcmsz;
+
+ q = (Cmd*)b->wp;
+ put4(q->cmd, op);
+ put4(q->len, tlen);
+ put2(q->flags, write? 2 : 0);
+ put2(q->id, ++ctl->reqid);
+ put4(q->status, 0);
+ b->wp += Cmdsz;
+
+ if(dlen > 0)
+ memmove(b->wp, data, dlen);
+ if(write)
+ memmove(b->wp + dlen, res, rlen);
+ b->wp += tlen;
+
+ if(iodebug) dump("cmd", b->rp, len);
+ packetrw(1, b->rp, len);
+ ctl->txseq++;
+ qunlock(&ctl->pktlock);
+ freeb(b);
+ b = nil;
+ USED(b);
+ sleep(&ctl->cmdr, cmddone, ctl);
+ b = ctl->rsp;
+ ctl->rsp = nil;
+ assert(b != nil);
+ p = (Sdpcm*)b->rp;
+ q = (Cmd*)(b->rp + p->doffset);
+ if(q->status[0] | q->status[1] | q->status[2] | q->status[3]){
+ print("ether4330: cmd %d error status %ld\n", op, get4(q->status));
+ dump("ether4330: cmd error", b->rp, BLEN(b));
+ error("wlcmd error");
+ }
+ if(!write)
+ memmove(res, q + 1, rlen);
+ freeb(b);
+ qunlock(&ctl->cmdlock);
+ poperror();
+}
+
+static void
+wlcmdint(Ctlr *ctl, int op, int val)
+{
+ uchar buf[4];
+
+ put4(buf, val);
+ wlcmd(ctl, 1, op, buf, 4, nil, 0);
+}
+
+static void
+wlgetvar(Ctlr *ctl, char *name, void *val, int len)
+{
+ wlcmd(ctl, 0, GetVar, name, strlen(name) + 1, val, len);
+}
+
+static void
+wlsetvar(Ctlr *ctl, char *name, void *val, int len)
+{
+ if(VARDEBUG){
+ char buf[32];
+ snprint(buf, sizeof buf, "wlsetvar %s:", name);
+ dump(buf, val, len);
+ }
+ wlcmd(ctl, 1, SetVar, name, strlen(name) + 1, val, len);
+}
+
+static void
+wlsetint(Ctlr *ctl, char *name, int val)
+{
+ uchar buf[4];
+
+ put4(buf, val);
+ wlsetvar(ctl, name, buf, 4);
+}
+
+static void
+wlwepkey(Ctlr *ctl, int i)
+{
+ uchar params[164];
+ uchar *p;
+
+ memset(params, 0, sizeof params);
+ p = params;
+ p = put4(p, i); /* index */
+ p = put4(p, ctl->keys[i].len);
+ memmove(p, ctl->keys[i].dat, ctl->keys[i].len);
+ p += 32 + 18*4; /* keydata, pad */
+ if(ctl->keys[i].len == WMinKeyLen)
+ p = put4(p, 1); /* algo = WEP1 */
+ else
+ p = put4(p, 3); /* algo = WEP128 */
+ put4(p, 2); /* flags = Primarykey */
+
+ wlsetvar(ctl, "wsec_key", params, sizeof params);
+}
+
+static void
+memreverse(char *dst, char *src, int len)
+{
+ src += len;
+ while(len-- > 0)
+ *dst++ = *--src;
+}
+
+static void
+wlwpakey(Ctlr *ctl, int id, uvlong iv, uchar *ea)
+{
+ uchar params[164];
+ uchar *p;
+ int pairwise;
+
+ if(id == CMrxkey)
+ return;
+ pairwise = (id == CMrxkey || id == CMtxkey);
+ memset(params, 0, sizeof params);
+ p = params;
+ if(pairwise)
+ p = put4(p, 0);
+ else
+ p = put4(p, id - CMrxkey0); /* group key id */
+ p = put4(p, ctl->keys[0].len);
+ memmove((char*)p, ctl->keys[0].dat, ctl->keys[0].len);
+ p += 32 + 18*4; /* keydata, pad */
+ if(ctl->cryptotype == Wpa)
+ p = put4(p, 2); /* algo = TKIP */
+ else
+ p = put4(p, 4); /* algo = AES_CCM */
+ if(pairwise)
+ p = put4(p, 0);
+ else
+ p = put4(p, 2); /* flags = Primarykey */
+ p += 3*4;
+ p = put4(p, 0); //pairwise); /* iv initialised */
+ p += 4;
+ p = put4(p, iv>>16); /* iv high */
+ p = put2(p, iv&0xFFFF); /* iv low */
+ p += 2 + 2*4; /* align, pad */
+ if(pairwise)
+ memmove(p, ea, Eaddrlen);
+
+ wlsetvar(ctl, "wsec_key", params, sizeof params);
+}
+
+static void
+wljoin(Ctlr *ctl, char *ssid, int chan)
+{
+ uchar params[72];
+ uchar *p;
+ int n;
+
+ if(chan != 0)
+ chan |= 0x2b00; /* 20Mhz channel width */
+ p = params;
+ n = strlen(ssid);
+ n = MIN(n, 32);
+ p = put4(p, n);
+ memmove(p, ssid, n);
+ memset(p + n, 0, 32 - n);
+ p += 32;
+ p = put4(p, 0xff); /* scan type */
+ if(chan != 0){
+ p = put4(p, 2); /* num probes */
+ p = put4(p, 120); /* active time */
+ p = put4(p, 390); /* passive time */
+ }else{
+ p = put4(p, -1); /* num probes */
+ p = put4(p, -1); /* active time */
+ p = put4(p, -1); /* passive time */
+ }
+ p = put4(p, -1); /* home time */
+ memset(p, 0xFF, Eaddrlen); /* bssid */
+ p += Eaddrlen;
+ p = put2(p, 0); /* pad */
+ if(chan != 0){
+ p = put4(p, 1); /* num chans */
+ p = put2(p, chan); /* chan spec */
+ p = put2(p, 0); /* pad */
+ assert(p == params + sizeof(params));
+ }else{
+ p = put4(p, 0); /* num chans */
+ assert(p == params + sizeof(params) - 4);
+ }
+
+ wlsetvar(ctl, "join", params, chan? sizeof params : sizeof params - 4);
+ ctl->status = Connecting;
+ switch(waitjoin(ctl)){
+ case 0:
+ ctl->status = Connected;
+ break;
+ case 3:
+ ctl->status = Disconnected;
+ error("wifi join: network not found");
+ case 1:
+ ctl->status = Disconnected;
+ error("wifi join: failed");
+ default:
+ ctl->status = Disconnected;
+ error("wifi join: error");
+ }
+}
+
+static void
+wlscanstart(Ctlr *ctl)
+{
+ /* version[4] action[2] sync_id[2] ssidlen[4] ssid[32] bssid[6] bss_type[1]
+ scan_type[1] nprobes[4] active_time[4] passive_time[4] home_time[4]
+ nchans[2] nssids[2] chans[nchans][2] ssids[nssids][32] */
+ /* hack - this is only correct on a little-endian cpu */
+ static uchar params[4+2+2+4+32+6+1+1+4*4+2+2+14*2+32+4] = {
+ 1,0,0,0,
+ 1,0,
+ 0x34,0x12,
+ 0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0xff,0xff,0xff,0xff,0xff,0xff,
+ 2,
+ 0,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 14,0,
+ 1,0,
+ 0x01,0x2b,0x02,0x2b,0x03,0x2b,0x04,0x2b,0x05,0x2e,0x06,0x2e,0x07,0x2e,
+ 0x08,0x2b,0x09,0x2b,0x0a,0x2b,0x0b,0x2b,0x0c,0x2b,0x0d,0x2b,0x0e,0x2b,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ };
+
+ wlcmdint(ctl, 49, 0); /* PASSIVE_SCAN */
+ wlsetvar(ctl, "escan", params, sizeof params);
+}
+
+static uchar*
+gettlv(uchar *p, uchar *ep, int tag)
+{
+ int len;
+
+ while(p + 1 < ep){
+ len = p[1];
+ if(p + 2 + len > ep)
+ return nil;
+ if(p[0] == tag)
+ return p;
+ p += 2 + len;
+ }
+ return nil;
+}
+
+static void
+addscan(Block *bp, uchar *p, int len)
+{
+ char bssid[24];
+ char *auth, *auth2;
+ uchar *t, *et;
+ int ielen;
+ static uchar wpaie1[4] = { 0x00, 0x50, 0xf2, 0x01 };
+
+ snprint(bssid, sizeof bssid, ";bssid=%E", p + 8);
+ if(strstr((char*)bp->rp, bssid) != nil)
+ return;
+ bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
+ "ssid=%.*s%s;signal=%d;noise=%d;chan=%d",
+ p[18], (char*)p+19, bssid,
+ (short)get2(p+78), (signed char)p[80],
+ get2(p+72) & 0xF);
+ auth = auth2 = "";
+ if(get2(p + 16) & 0x10)
+ auth = ";wep";
+ ielen = get4(p + 0x78);
+ if(ielen > 0){
+ t = p + get4(p + 0x74);
+ et = t + ielen;
+ if(et > p + len)
+ return;
+ if(gettlv(t, et, 0x30) != nil){
+ auth = "";
+ auth2 = ";wpa2";
+ }
+ while((t = gettlv(t, et, 0xdd)) != nil){
+ if(t[1] > 4 && memcmp(t+2, wpaie1, 4) == 0){
+ auth = ";wpa";
+ break;
+ }
+ t += 2 + t[1];
+ }
+ }
+ bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
+ "%s%s\n", auth, auth2);
+}
+
+
+static void
+wlscanresult(Ether *edev, uchar *p, int len)
+{
+ Ctlr *ctlr;
+ Netfile **ep, *f, **fp;
+ Block *bp;
+ int nbss, i;
+
+ ctlr = edev->ctlr;
+ if(get4(p) > len)
+ return;
+ /* TODO: more syntax checking */
+ bp = ctlr->scanb;
+ if(bp == nil)
+ ctlr->scanb = bp = allocb(8192);
+ nbss = get2(p+10);
+ p += 12;
+ len -= 12;
+ if(0) dump("SCAN", p, len);
+ if(nbss){
+ addscan(bp, p, len);
+ return;
+ }
+ i = edev->scan;
+ ep = &edev->f[Ntypes];
+ for(fp = edev->f; fp < ep && i > 0; fp++){
+ f = *fp;
+ if(f == nil || f->scan == 0)
+ continue;
+ if(i == 1)
+ qpass(f->in, bp);
+ else
+ qpass(f->in, copyblock(bp, BLEN(bp)));
+ i--;
+ }
+ if(i)
+ freeb(bp);
+ ctlr->scanb = nil;
+}
+
+static void
+lproc(void *a)
+{
+ Ether *edev;
+ Ctlr *ctlr;
+ int secs;
+
+ edev = a;
+ ctlr = edev->ctlr;
+ secs = 0;
+ for(;;){
+ tsleep(&up->sleep, return0, 0, 1000);
+ if(ctlr->scansecs){
+ if(secs == 0){
+ if(waserror())
+ ctlr->scansecs = 0;
+ else{
+ wlscanstart(ctlr);
+ poperror();
+ }
+ secs = ctlr->scansecs;
+ }
+ --secs;
+ }else
+ secs = 0;
+ }
+}
+
+static void
+wlinit(Ether *edev, Ctlr *ctlr)
+{
+ uchar ea[Eaddrlen];
+ uchar eventmask[16];
+ char version[128];
+ char *p;
+ static uchar keepalive[12] = {1, 0, 11, 0, 0xd8, 0xd6, 0, 0, 0, 0, 0, 0};
+
+ wlgetvar(ctlr, "cur_etheraddr", ea, Eaddrlen);
+ memmove(edev->ea, ea, Eaddrlen);
+ memmove(edev->addr, ea, Eaddrlen);
+ print("ether4330: addr %E\n", edev->ea);
+ wlsetint(ctlr, "assoc_listen", 10);
+ if(ctlr->chipid == 43430 || ctlr->chipid == 0x4345)
+ wlcmdint(ctlr, 0x56, 0); /* powersave off */
+ else
+ wlcmdint(ctlr, 0x56, 2); /* powersave FAST */
+ wlsetint(ctlr, "bus:txglom", 0);
+ wlsetint(ctlr, "bcn_timeout", 10);
+ wlsetint(ctlr, "assoc_retry_max", 3);
+ if(ctlr->chipid == 0x4330){
+ wlsetint(ctlr, "btc_wire", 4);
+ wlsetint(ctlr, "btc_mode", 1);
+ wlsetvar(ctlr, "mkeep_alive", keepalive, 11);
+ }
+ memset(eventmask, 0xFF, sizeof eventmask);
+#define ENABLE(n) eventmask[n/8] |= 1<<(n%8)
+#define DISABLE(n) eventmask[n/8] &= ~(1<<(n%8))
+ DISABLE(40); /* E_RADIO */
+ DISABLE(44); /* E_PROBREQ_MSG */
+ DISABLE(54); /* E_IF */
+ DISABLE(71); /* E_PROBRESP_MSG */
+ DISABLE(20); /* E_TXFAIL */
+ DISABLE(124); /* ? */
+ wlsetvar(ctlr, "event_msgs", eventmask, sizeof eventmask);
+ wlcmdint(ctlr, 0xb9, 0x28); /* SET_SCAN_CHANNEL_TIME */
+ wlcmdint(ctlr, 0xbb, 0x28); /* SET_SCAN_UNASSOC_TIME */
+ wlcmdint(ctlr, 0x102, 0x82); /* SET_SCAN_PASSIVE_TIME */
+ wlcmdint(ctlr, 2, 0); /* UP */
+ memset(version, 0, sizeof version);
+ wlgetvar(ctlr, "ver", version, sizeof version - 1);
+ if((p = strchr(version, '\n')) != nil)
+ *p = '\0';
+ if(0) print("ether4330: %s\n", version);
+ wlsetint(ctlr, "roam_off", 1);
+ wlcmdint(ctlr, 0x14, 1); /* SET_INFRA 1 */
+ wlcmdint(ctlr, 10, 0); /* SET_PROMISC */
+ //wlcmdint(ctlr, 0x8e, 0); /* SET_BAND 0 */
+ //wlsetint(ctlr, "wsec", 1);
+ wlcmdint(ctlr, 2, 1); /* UP */
+ ctlr->keys[0].len = WMinKeyLen;
+ //wlwepkey(ctlr, 0);
+}
+
+/*
+ * Plan 9 driver interface
+ */
+
+static long
+etherbcmifstat(Ether* edev, void* a, long n, ulong offset)
+{
+ Ctlr *ctlr;
+ char *p;
+ int l;
+ static char *cryptoname[4] = {
+ [0] "off",
+ [Wep] "wep",
+ [Wpa] "wpa",
+ [Wpa2] "wpa2",
+ };
+ /* these strings are known by aux/wpa */
+ static char* connectstate[] = {
+ [Disconnected] = "unassociated",
+ [Connecting] = "connecting",
+ [Connected] = "associated",
+ };
+
+ ctlr = edev->ctlr;
+ if(ctlr == nil)
+ return 0;
+ p = malloc(READSTR);
+ l = 0;
+
+ l += snprint(p+l, READSTR-l, "channel: %d\n", ctlr->chanid);
+ l += snprint(p+l, READSTR-l, "essid: %s\n", ctlr->essid);
+ l += snprint(p+l, READSTR-l, "crypt: %s\n", cryptoname[ctlr->cryptotype]);
+ l += snprint(p+l, READSTR-l, "oq: %d\n", qlen(edev->oq));
+ l += snprint(p+l, READSTR-l, "txwin: %d\n", ctlr->txwindow);
+ l += snprint(p+l, READSTR-l, "txseq: %d\n", ctlr->txseq);
+ l += snprint(p+l, READSTR-l, "status: %s\n", connectstate[ctlr->status]);
+ USED(l);
+ n = readstr(offset, a, n, p);
+ free(p);
+ return n;
+}
+
+static void
+etherbcmtransmit(Ether *edev)
+{
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ if(ctlr == nil)
+ return;
+ txstart(edev);
+}
+
+static int
+parsehex(char *buf, int buflen, char *a)
+{
+ int i, k, n;
+
+ k = 0;
+ for(i = 0;k < buflen && *a; i++){
+ if(*a >= '0' && *a <= '9')
+ n = *a++ - '0';
+ else if(*a >= 'a' && *a <= 'f')
+ n = *a++ - 'a' + 10;
+ else if(*a >= 'A' && *a <= 'F')
+ n = *a++ - 'A' + 10;
+ else
+ break;
+
+ if(i & 1){
+ buf[k] |= n;
+ k++;
+ }
+ else
+ buf[k] = n<<4;
+ }
+ if(i & 1)
+ return -1;
+ return k;
+}
+
+static int
+wepparsekey(WKey* key, char* a)
+{
+ int i, k, len, n;
+ char buf[WMaxKeyLen];
+
+ len = strlen(a);
+ if(len == WMinKeyLen || len == WMaxKeyLen){
+ memset(key->dat, 0, sizeof(key->dat));
+ memmove(key->dat, a, len);
+ key->len = len;
+
+ return 0;
+ }
+ else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
+ k = 0;
+ for(i = 0; i < len; i++){
+ if(*a >= '0' && *a <= '9')
+ n = *a++ - '0';
+ else if(*a >= 'a' && *a <= 'f')
+ n = *a++ - 'a' + 10;
+ else if(*a >= 'A' && *a <= 'F')
+ n = *a++ - 'A' + 10;
+ else
+ return -1;
+
+ if(i & 1){
+ buf[k] |= n;
+ k++;
+ }
+ else
+ buf[k] = n<<4;
+ }
+
+ memset(key->dat, 0, sizeof(key->dat));
+ memmove(key->dat, buf, k);
+ key->len = k;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+wpaparsekey(WKey *key, uvlong *ivp, char *a)
+{
+ int len;
+ char *e;
+
+ if(cistrncmp(a, "tkip:", 5) == 0 || cistrncmp(a, "ccmp:", 5) == 0)
+ a += 5;
+ else
+ return 1;
+ len = parsehex(key->dat, sizeof(key->dat), a);
+ if(len <= 0)
+ return 1;
+ key->len = len;
+ a += 2*len;
+ if(*a++ != '@')
+ return 1;
+ *ivp = strtoull(a, &e, 16);
+ if(e == a)
+ return -1;
+ return 0;
+}
+
+static void
+setauth(Ctlr *ctlr, Cmdbuf *cb, char *a)
+{
+ uchar wpaie[32];
+ int i;
+
+ i = parsehex((char*)wpaie, sizeof wpaie, a);
+ if(i < 2 || i != wpaie[1] + 2)
+ cmderror(cb, "bad wpa ie syntax");
+ if(wpaie[0] == 0xdd)
+ ctlr->cryptotype = Wpa;
+ else if(wpaie[0] == 0x30)
+ ctlr->cryptotype = Wpa2;
+ else
+ cmderror(cb, "bad wpa ie");
+ wlsetvar(ctlr, "wpaie", wpaie, i);
+ if(ctlr->cryptotype == Wpa){
+ wlsetint(ctlr, "wpa_auth", 4|2); /* auth_psk | auth_unspecified */
+ wlsetint(ctlr, "auth", 0);
+ wlsetint(ctlr, "wsec", 2); /* tkip */
+ wlsetint(ctlr, "wpa_auth", 4); /* auth_psk */
+ }else{
+ wlsetint(ctlr, "wpa_auth", 0x80|0x40); /* auth_psk | auth_unspecified */
+ wlsetint(ctlr, "auth", 0);
+ wlsetint(ctlr, "wsec", 4); /* aes */
+ wlsetint(ctlr, "wpa_auth", 0x80); /* auth_psk */
+ }
+}
+
+static int
+setcrypt(Ctlr *ctlr, Cmdbuf*, char *a)
+{
+ if(cistrcmp(a, "wep") == 0 || cistrcmp(a, "on") == 0)
+ ctlr->cryptotype = Wep;
+ else if(cistrcmp(a, "off") == 0 || cistrcmp(a, "none") == 0)
+ ctlr->cryptotype = 0;
+ else
+ return 0;
+ wlsetint(ctlr, "auth", ctlr->cryptotype);
+ return 1;
+}
+
+static long
+etherbcmctl(Ether* edev, void* buf, long n)
+{
+ Ctlr *ctlr;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ uchar ea[Eaddrlen];
+ uvlong iv;
+ int i;
+
+ if((ctlr = edev->ctlr) == nil)
+ error(Enonexist);
+ USED(ctlr);
+
+ cb = parsecmd(buf, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, cmds, nelem(cmds));
+ switch(ct->index){
+ case CMauth:
+ setauth(ctlr, cb, cb->f[1]);
+ if(ctlr->essid[0])
+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
+ break;
+ case CMchannel:
+ if((i = atoi(cb->f[1])) < 0 || i > 16)
+ cmderror(cb, "bad channel number");
+ //wlcmdint(ctlr, 30, i); /* SET_CHANNEL */
+ ctlr->chanid = i;
+ break;
+ case CMcrypt:
+ if(setcrypt(ctlr, cb, cb->f[1])){
+ if(ctlr->essid[0])
+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
+ }else
+ cmderror(cb, "bad crypt type");
+ break;
+ case CMessid:
+ if(cistrcmp(cb->f[1], "default") == 0)
+ memset(ctlr->essid, 0, sizeof(ctlr->essid));
+ else{
+ strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid) - 1);
+ ctlr->essid[sizeof(ctlr->essid) - 1] = '\0';
+ }
+ if(!waserror()){
+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
+ poperror();
+ }
+ break;
+ case CMjoin: /* join essid channel wep|on|off|wpakey */
+ if(strcmp(cb->f[1], "") != 0){ /* empty string for no change */
+ if(cistrcmp(cb->f[1], "default") != 0){
+ strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid)-1);
+ ctlr->essid[sizeof(ctlr->essid)-1] = 0;
+ }else
+ memset(ctlr->essid, 0, sizeof(ctlr->essid));
+ }else if(ctlr->essid[0] == 0)
+ cmderror(cb, "essid not set");
+ if((i = atoi(cb->f[2])) >= 0 && i <= 16)
+ ctlr->chanid = i;
+ else
+ cmderror(cb, "bad channel number");
+ if(!setcrypt(ctlr, cb, cb->f[3]))
+ setauth(ctlr, cb, cb->f[3]);
+ if(ctlr->essid[0])
+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
+ break;
+ case CMkey1:
+ case CMkey2:
+ case CMkey3:
+ case CMkey4:
+ i = ct->index - CMkey1;
+ if(wepparsekey(&ctlr->keys[i], cb->f[1]))
+ cmderror(cb, "bad WEP key syntax");
+ wlsetint(ctlr, "wsec", 1); /* wep enabled */
+ wlwepkey(ctlr, i);
+ break;
+ case CMrxkey:
+ case CMrxkey0:
+ case CMrxkey1:
+ case CMrxkey2:
+ case CMrxkey3:
+ case CMtxkey:
+ if(parseether(ea, cb->f[1]) < 0)
+ cmderror(cb, "bad ether addr");
+ if(wpaparsekey(&ctlr->keys[0], &iv, cb->f[2]))
+ cmderror(cb, "bad wpa key");
+ wlwpakey(ctlr, ct->index, iv, ea);
+ break;
+ case CMdebug:
+ iodebug = atoi(cb->f[1]);
+ break;
+ }
+ poperror();
+ free(cb);
+ return n;
+}
+
+static void
+etherbcmscan(void *a, uint secs)
+{
+ Ether* edev;
+ Ctlr* ctlr;
+
+ edev = a;
+ ctlr = edev->ctlr;
+ ctlr->scansecs = secs;
+}
+
+static void
+etherbcmattach(Ether* edev)
+{
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ qlock(&ctlr->alock);
+ if(waserror()){
+ //print("ether4330: attach failed: %s\n", up->errstr);
+ qunlock(&ctlr->alock);
+ nexterror();
+ }
+ if(ctlr->edev == nil){
+ if(ctlr->chipid == 0){
+ sdioinit();
+ sbinit(ctlr);
+ }
+ fwload(ctlr);
+ sbenable(ctlr);
+ kproc("wifireader", rproc, edev);
+ kproc("wifitimer", lproc, edev);
+ if(ctlr->regufile)
+ reguload(ctlr, ctlr->regufile);
+ wlinit(edev, ctlr);
+ ctlr->edev = edev;
+ }
+ qunlock(&ctlr->alock);
+ poperror();
+}
+
+static void
+etherbcmshutdown(Ether*)
+{
+ sdioreset();
+}
+
+
+static int
+etherbcmpnp(Ether* edev)
+{
+ static Ctlr *ctlr;
+
+ if(ctlr != nil)
+ return -1;
+
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->chanid = Wifichan;
+ edev->ctlr = ctlr;
+ edev->attach = etherbcmattach;
+ edev->transmit = etherbcmtransmit;
+ edev->ifstat = etherbcmifstat;
+ edev->ctl = etherbcmctl;
+ edev->scanbs = etherbcmscan;
+ edev->shutdown = etherbcmshutdown;
+ edev->arg = edev;
+
+ return 0;
+}
+
+void
+ether4330link(void)
+{
+ addethercard("4330", etherbcmpnp);
+}
--- a/sys/src/9/bcm64/pi4 Sun Oct 22 09:21:23 2023
+++ b/sys/src/9/bcm64/pi4 Sun Oct 22 10:53:02 2023
@@ -32,6 +32,7 @@
archbcm4 pci
usbxhcipci pci usbxhci archbcm4
ethergenet ethermii
+ ether4330 emmc
ethermedium
loopbackmedium
netdevmedium
--- a/sys/src/9/port/sd.h Sun Oct 22 09:21:23 2023
+++ b/sys/src/9/port/sd.h Sun Oct 22 10:53:02 2023
@@ -164,6 +164,31 @@
char *name;
};
+/* Commands */
+extern SDiocmd GO_IDLE_STATE;
+extern SDiocmd SEND_OP_COND;
+extern SDiocmd ALL_SEND_CID;
+extern SDiocmd SET_RELATIVE_ADDR;
+extern SDiocmd SEND_RELATIVE_ADDR;
+extern SDiocmd SWITCH;
+extern SDiocmd SWITCH_FUNC;
+extern SDiocmd SELECT_CARD;
+extern SDiocmd SEND_EXT_CSD;
+extern SDiocmd SD_SEND_IF_COND;
+extern SDiocmd SEND_CSD;
+extern SDiocmd STOP_TRANSMISSION;
+extern SDiocmd SEND_STATUS;
+extern SDiocmd SET_BLOCKLEN;
+extern SDiocmd READ_SINGLE_BLOCK;
+extern SDiocmd READ_MULTIPLE_BLOCK;
+extern SDiocmd WRITE_SINGLE_BLOCK;
+extern SDiocmd WRITE_MULTIPLE_BLOCK;
+
+/* prefix for following app-specific commands */
+extern SDiocmd APP_CMD;
+extern SDiocmd SD_SET_BUS_WIDTH;
+extern SDiocmd SD_SEND_OP_COND;
+
struct SDio {
char *name;
int (*init)(SDio*);
^ permalink raw reply [flat|nested] 3+ messages in thread
* [9front] Re: Fwd: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org)
2023-10-22 18:43 ` [9front] Fwd: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org) Stanley Lieber
@ 2023-10-22 18:45 ` Stanley Lieber
2023-10-22 19:01 ` adventures in9
0 siblings, 1 reply; 3+ messages in thread
From: Stanley Lieber @ 2023-10-22 18:45 UTC (permalink / raw)
To: 9front
On October 22, 2023 2:43:42 PM EDT, Stanley Lieber <sl@stanleylieber.com> wrote:
>this is for rpi, yes? is there a list of specific hardware this is known to work with?
>
>sl
>
>
>-------- Original Message --------
>From: commits@git.9front.org
>Sent: October 22, 2023 10:53:02 AM EDT
>To: 9front-commits@9front.org
>Subject: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org)
>
>
>Thanks Richard Miller
>---
>diff f2cfee358f329519e913a20142d96b1e0029633c fb96a050f8a9d5f23da3557ba89251ffb73889bf
>--- a/sys/src/9/bcm/emmc.c Sun Oct 22 09:21:23 2023
>+++ b/sys/src/9/bcm/emmc.c Sun Oct 22 10:53:02 2023
>@@ -129,6 +129,7 @@
>
> typedef struct Ctlr Ctlr;
> struct Ctlr {
>+ Rendez cardr;
> Rendez r;
> int fastclock;
> ulong extclk;
>@@ -266,6 +267,23 @@
> intrenable(IRQmmc, emmcinterrupt, nil, BUSUNKNOWN, io->name);
> }
>
>+int
>+sdiocardintr(int wait)
>+{
>+ u32int *r = (u32int*)EMMCREGS;
>+ int i;
>+
>+ WR(Interrupt, Cardintr);
>+ while(((i = r[Interrupt]) & Cardintr) == 0){
>+ if(!wait)
>+ return 0;
>+ WR(Irpten, r[Irpten] | Cardintr);
>+ sleep(&emmc.cardr, cardintready, 0);
>+ }
>+ WR(Interrupt, Cardintr);
>+ return i;
>+}
>+
> static int
> emmccmd(SDio*, SDiocmd *cmd, u32int arg, u32int *resp)
> {
>@@ -434,21 +452,25 @@
> i = r[Interrupt];
> if(i&(Datadone|Err))
> wakeup(&emmc.r);
>+ if(i&Cardintr)
>+ wakeup(&emmc.cardr);
> WR(Irpten, r[Irpten] & ~i);
> }
>
>+
>+SDio sdio = {
>+ "emmc",
>+ emmcinit,
>+ emmcenable,
>+ emmcinquiry,
>+ emmccmd,
>+ emmciosetup,
>+ emmcio,
>+ emmcbus,
>+};
>+
> void
> emmclink(void)
> {
>- static SDio io = {
>- "emmc",
>- emmcinit,
>- emmcenable,
>- emmcinquiry,
>- emmccmd,
>- emmciosetup,
>- emmcio,
>- emmcbus,
>- };
>- addmmcio(&io);
>+ addmmcio(&sdio);
> }
>--- /dev/null Sun Aug 27 23:09:05 2023
>+++ b/sys/src/9/bcm/ether4330.c Sun Oct 22 10:53:02 2023
>@@ -0,0 +1,2411 @@
>+/*
>+ * Broadcom bcm4330 wifi (sdio interface)
>+ */
>+
>+#include "u.h"
>+#include "../port/lib.h"
>+#include "mem.h"
>+#include "dat.h"
>+#include "fns.h"
>+#include "io.h"
>+#include "../port/error.h"
>+#include "../port/netif.h"
>+#include "../port/sd.h"
>+#include "../port/etherif.h"
>+
>+#define CACHELINESZ 64 /* temp */
>+
>+extern SDio sdio;
>+extern int sdiocardintr(int);
>+
>+SDiocmd IO_SEND_OP_COND = { 5, 3, 0, 0, "IO_SEND_OP_COND" };
>+SDiocmd IO_RW_DIRECT = { 52, 1, 0, 0, "IO_RW_DIRECT" };
>+
>+enum{
>+ SDIODEBUG = 0,
>+ SBDEBUG = 0,
>+ EVENTDEBUG = 0,
>+ VARDEBUG = 0,
>+ FWDEBUG = 0,
>+
>+ Corescansz = 512,
>+ Uploadsz = 2048,
>+ Sdpcmsz = 12, /* sizeof(Sdpcmsz) */
>+ Cmdsz = 16, /* sizeof(Cmd) */
>+
>+ SDfreq = 25*Mhz, /* standard SD frequency */
>+ SDfreqhs = 50*Mhz, /* high speed frequency */
>+
>+ Wifichan = 0, /* default channel */
>+ Firmwarecmp = 1,
>+
>+ ARMcm3 = 0x82A,
>+ ARM7tdmi = 0x825,
>+ ARMcr4 = 0x83E,
>+
>+ Fn0 = 0,
>+ Fn1 = 1,
>+ Fn2 = 2,
>+ Fbr1 = 0x100,
>+ Fbr2 = 0x200,
>+
>+ /* CCCR */
>+ Ioenable = 0x02,
>+ Ioready = 0x03,
>+ Intenable = 0x04,
>+ Intpend = 0x05,
>+ Ioabort = 0x06,
>+ Busifc = 0x07,
>+ Capability = 0x08,
>+ Blksize = 0x10,
>+ Highspeed = 0x13,
>+
>+ /* SELECT_CARD args */
>+ Rcashift = 16,
>+
>+ /* SEND_OP_COND args */
>+ Hcs = 1<<30, /* host supports SDHC & SDXC */
>+ V3_3 = 3<<20, /* 3.2-3.4 volts */
>+ V2_8 = 3<<15, /* 2.7-2.9 volts */
>+ V2_0 = 1<<8, /* 2.0-2.1 volts */
>+ S18R = 1<<24, /* switch to 1.8V request */
>+
>+ /* Sonics Silicon Backplane (access to cores on chip) */
>+ Sbwsize = 0x8000,
>+ Sb32bit = 0x8000,
>+ Sbaddr = 0x1000a,
>+ Enumbase = 0x18000000,
>+ Framectl= 0x1000d,
>+ Rfhalt = 0x01,
>+ Wfhalt = 0x02,
>+ Clkcsr = 0x1000e,
>+ ForceALP = 0x01, /* active low-power clock */
>+ ForceHT = 0x02, /* high throughput clock */
>+ ForceILP = 0x04, /* idle low-power clock */
>+ ReqALP = 0x08,
>+ ReqHT = 0x10,
>+ Nohwreq = 0x20,
>+ ALPavail = 0x40,
>+ HTavail = 0x80,
>+ Pullups = 0x1000f,
>+ Wfrmcnt = 0x10019,
>+ Rfrmcnt = 0x1001b,
>+
>+ /* core control regs */
>+ Ioctrl = 0x408,
>+ Resetctrl = 0x800,
>+
>+ /* socram regs */
>+ Coreinfo = 0x00,
>+ Bankidx = 0x10,
>+ Bankinfo = 0x40,
>+ Bankpda = 0x44,
>+
>+ /* armcr4 regs */
>+ Cr4Cap = 0x04,
>+ Cr4Bankidx = 0x40,
>+ Cr4Bankinfo = 0x44,
>+ Cr4Cpuhalt = 0x20,
>+
>+ /* chipcommon regs */
>+ Gpiopullup = 0x58,
>+ Gpiopulldown = 0x5c,
>+ Chipctladdr = 0x650,
>+ Chipctldata = 0x654,
>+
>+ /* sdio core regs */
>+ Intstatus = 0x20,
>+ Fcstate = 1<<4,
>+ Fcchange = 1<<5,
>+ FrameInt = 1<<6,
>+ MailboxInt = 1<<7,
>+ Intmask = 0x24,
>+ Sbmbox = 0x40,
>+ Sbmboxdata = 0x48,
>+ Hostmboxdata= 0x4c,
>+ Fwready = 0x80,
>+
>+ /* wifi control commands */
>+ GetVar = 262,
>+ SetVar = 263,
>+
>+ /* status */
>+ Disconnected= 0,
>+ Connecting,
>+ Connected,
>+};
>+
>+typedef struct Ctlr Ctlr;
>+
>+enum{
>+ Wpa = 1,
>+ Wep = 2,
>+ Wpa2 = 3,
>+ WNameLen = 32,
>+ WNKeys = 4,
>+ WKeyLen = 32,
>+ WMinKeyLen = 5,
>+ WMaxKeyLen = 13,
>+};
>+
>+typedef struct WKey WKey;
>+struct WKey
>+{
>+ ushort len;
>+ char dat[WKeyLen];
>+};
>+
>+struct Ctlr {
>+ Ether* edev;
>+ QLock cmdlock;
>+ QLock pktlock;
>+ QLock tlock;
>+ QLock alock;
>+ Lock txwinlock;
>+ Rendez cmdr;
>+ Rendez joinr;
>+ int joinstatus;
>+ int cryptotype;
>+ int chanid;
>+ char essid[WNameLen + 1];
>+ WKey keys[WNKeys];
>+ Block *rsp;
>+ Block *scanb;
>+ int scansecs;
>+ int status;
>+ int chipid;
>+ int chiprev;
>+ int armcore;
>+ char *regufile;
>+ union {
>+ u32int i;
>+ uchar c[4];
>+ } resetvec;
>+ ulong chipcommon;
>+ ulong armctl;
>+ ulong armregs;
>+ ulong d11ctl;
>+ ulong socramregs;
>+ ulong socramctl;
>+ ulong sdregs;
>+ int sdiorev;
>+ int socramrev;
>+ ulong socramsize;
>+ ulong rambase;
>+ short reqid;
>+ uchar fcmask;
>+ uchar txwindow;
>+ uchar txseq;
>+ uchar rxseq;
>+};
>+
>+enum{
>+ CMauth,
>+ CMchannel,
>+ CMcrypt,
>+ CMessid,
>+ CMkey1,
>+ CMkey2,
>+ CMkey3,
>+ CMkey4,
>+ CMrxkey,
>+ CMrxkey0,
>+ CMrxkey1,
>+ CMrxkey2,
>+ CMrxkey3,
>+ CMtxkey,
>+ CMdebug,
>+ CMjoin,
>+};
>+
>+static Cmdtab cmds[] = {
>+ {CMauth, "auth", 2},
>+ {CMchannel, "channel", 2},
>+ {CMcrypt, "crypt", 2},
>+ {CMessid, "essid", 2},
>+ {CMkey1, "key1", 2},
>+ {CMkey2, "key1", 2},
>+ {CMkey3, "key1", 2},
>+ {CMkey4, "key1", 2},
>+ {CMrxkey, "rxkey", 3},
>+ {CMrxkey0, "rxkey0", 3},
>+ {CMrxkey1, "rxkey1", 3},
>+ {CMrxkey2, "rxkey2", 3},
>+ {CMrxkey3, "rxkey3", 3},
>+ {CMtxkey, "txkey", 3},
>+ {CMdebug, "debug", 2},
>+ {CMjoin, "join", 5},
>+};
>+
>+typedef struct Sdpcm Sdpcm;
>+typedef struct Cmd Cmd;
>+struct Sdpcm {
>+ uchar len[2];
>+ uchar lenck[2];
>+ uchar seq;
>+ uchar chanflg;
>+ uchar nextlen;
>+ uchar doffset;
>+ uchar fcmask;
>+ uchar window;
>+ uchar version;
>+ uchar pad;
>+};
>+
>+struct Cmd {
>+ uchar cmd[4];
>+ uchar len[4];
>+ uchar flags[2];
>+ uchar id[2];
>+ uchar status[4];
>+};
>+
>+static char config40181[] = "bcmdhd.cal.40181";
>+static char config40183[] = "bcmdhd.cal.40183.26MHz";
>+
>+struct {
>+ int chipid;
>+ int chiprev;
>+ char *fwfile;
>+ char *cfgfile;
>+ char *regufile;
>+} firmware[] = {
>+ { 0x4330, 3, "fw_bcm40183b1.bin", config40183, 0 },
>+ { 0x4330, 4, "fw_bcm40183b2.bin", config40183, 0 },
>+ { 43362, 0, "fw_bcm40181a0.bin", config40181, 0 },
>+ { 43362, 1, "fw_bcm40181a2.bin", config40181, 0 },
>+ { 43430, 1, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt", 0 },
>+ { 43430, 2, "brcmfmac43436-sdio.bin", "brcmfmac43436-sdio.txt", "brcmfmac43436-sdio.clm_blob" },
>+ { 0x4345, 6, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt", "brcmfmac43455-sdio.clm_blob" },
>+ { 0x4345, 9, "brcmfmac43456-sdio.bin", "brcmfmac43456-sdio.txt", "brcmfmac43456-sdio.clm_blob" },
>+};
>+
>+static QLock sdiolock;
>+static int iodebug;
>+
>+static void etherbcmintr(void *);
>+static void bcmevent(Ctlr*, uchar*, int);
>+static void wlscanresult(Ether*, uchar*, int);
>+static void wlsetvar(Ctlr*, char*, void*, int);
>+
>+static uchar*
>+put2(uchar *p, short v)
>+{
>+ p[0] = v;
>+ p[1] = v >> 8;
>+ return p + 2;
>+}
>+
>+static uchar*
>+put4(uchar *p, long v)
>+{
>+ p[0] = v;
>+ p[1] = v >> 8;
>+ p[2] = v >> 16;
>+ p[3] = v >> 24;
>+ return p + 4;
>+}
>+
>+static ushort
>+get2(uchar *p)
>+{
>+ return p[0] | p[1]<<8;
>+}
>+
>+static ulong
>+get4(uchar *p)
>+{
>+ return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
>+}
>+
>+static void
>+dump(char *s, void *a, int n)
>+{
>+ int i;
>+ uchar *p;
>+
>+ p = a;
>+ print("%s:", s);
>+ for(i = 0; i < n; i++)
>+ print("%c%2.2x", i&15? ' ' : '\n', *p++);
>+ print("\n");
>+}
>+
>+/*
>+ * SDIO communication with dongle
>+ */
>+static ulong
>+sdiocmd_locked(SDiocmd *cmd, ulong arg)
>+{
>+ u32int resp[4];
>+
>+ sdio.cmd(&sdio, cmd, arg, resp);
>+ return resp[0];
>+}
>+
>+static ulong
>+sdiocmd(SDiocmd *cmd, ulong arg)
>+{
>+ ulong r;
>+
>+ qlock(&sdiolock);
>+ if(waserror()){
>+ if(SDIODEBUG) print("sdiocmd error: cmd %s arg %lux\n", cmd->name, arg);
>+ qunlock(&sdiolock);
>+ nexterror();
>+ }
>+ r = sdiocmd_locked(cmd, arg);
>+ qunlock(&sdiolock);
>+ poperror();
>+ return r;
>+
>+}
>+
>+static ulong
>+trysdiocmd(SDiocmd *cmd, ulong arg)
>+{
>+ ulong r;
>+
>+ if(waserror())
>+ return 0;
>+ r = sdiocmd(cmd, arg);
>+ poperror();
>+ return r;
>+}
>+
>+static int
>+sdiord(int fn, int addr)
>+{
>+ int r;
>+
>+ r = sdiocmd(&IO_RW_DIRECT, (0<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9));
>+ if(r & 0xCF00){
>+ print("ether4330: sdiord(%x, %x) fail: %2.2ux %2.2ux\n", fn, addr, (r>>8)&0xFF, r&0xFF);
>+ error(Eio);
>+ }
>+ return r & 0xFF;
>+}
>+
>+static void
>+sdiowr(int fn, int addr, int data)
>+{
>+ int r;
>+ int retry;
>+ ulong arg;
>+
>+ r = 0;
>+ for(retry = 0; retry < 10; retry++){
>+ arg = (1<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)|(data&0xFF);
>+ if((arg & ~0xFF) == (1<<31|0<<28|7<<9)){
>+ switch(arg&3){
>+ case 0:
>+ sdio.bus(&sdio, 1, 0);
>+ break;
>+ case 2:
>+ sdio.bus(&sdio, 4, 0);
>+ break;
>+ }
>+ }
>+ r = sdiocmd(&IO_RW_DIRECT, arg);
>+ if((r & 0xCF00) == 0)
>+ return;
>+ }
>+ print("ether4330: sdiowr(%x, %x, %x) fail: %2.2ux %2.2ux\n", fn, addr, data, (r>>8)&0xFF, r&0xFF);
>+ error(Eio);
>+}
>+
>+static void
>+sdiorwext(int fn, int write, void *a, int len, int addr, int incr)
>+{
>+ int bsize, blk, bcount, m;
>+ SDiocmd cmd = { 53, 1, 0, 0, "IO_RW_EXTENDED" };
>+
>+ bsize = fn == Fn2? 512 : 64;
>+ while(len > 0){
>+ if(len >= 511*bsize){
>+ blk = 1;
>+ bcount = 511;
>+ m = bcount*bsize;
>+ }else if(len > bsize){
>+ blk = 1;
>+ bcount = len/bsize;
>+ m = bcount*bsize;
>+ }else{
>+ blk = 0;
>+ bcount = len;
>+ m = bcount;
>+ }
>+ qlock(&sdiolock);
>+ if(waserror()){
>+ print("ether4330: sdiorwext fail: %s\n", up->errstr);
>+ qunlock(&sdiolock);
>+ nexterror();
>+ }
>+ cmd.data = write? 2 : 1; /* Host2card : Card2host */
>+ if(blk){
>+ cmd.data += 2; /* Multiblock | Blkcnten */
>+ sdio.iosetup(&sdio, write, a, bsize, bcount);
>+ }else
>+ sdio.iosetup(&sdio, write, a, bcount, 1);
>+ sdiocmd_locked(&cmd, write<<31 | (fn&7)<<28 | blk<<27 | incr<<26 | (addr&0x1FFFF)<<9 | (bcount&0x1FF));
>+ sdio.io(&sdio, write, a, m);
>+ qunlock(&sdiolock);
>+ poperror();
>+ len -= m;
>+ a = (char*)a + m;
>+ if(incr)
>+ addr += m;
>+ }
>+}
>+
>+static void
>+sdioset(int fn, int addr, int bits)
>+{
>+ sdiowr(fn, addr, sdiord(fn, addr) | bits);
>+}
>+
>+static void
>+sdioinit(void)
>+{
>+ ulong ocr, rca;
>+ int i;
>+
>+ /* disconnect emmc from SD card (connect sdhost instead) */
>+ for(i = 48; i <= 53; i++)
>+ gpiosel(i, Alt0);
>+ /* connect emmc to wifi */
>+ for(i = 34; i <= 39; i++){
>+ gpiosel(i, Alt3);
>+ if(i == 34)
>+ gpiopulloff(i);
>+ else
>+ gpiopullup(i);
>+ }
>+ sdio.init(&sdio);
>+ sdio.enable(&sdio);
>+ sdiocmd(&GO_IDLE_STATE, 0);
>+ ocr = trysdiocmd(&IO_SEND_OP_COND, 0);
>+ i = 0;
>+ while((ocr & (1<<31)) == 0){
>+ if(++i > 5){
>+ print("ether4330: no response to sdio access: ocr = %lux\n", ocr);
>+ error(Eio);
>+ }
>+ ocr = trysdiocmd(&IO_SEND_OP_COND, V3_3);
>+ tsleep(&up->sleep, return0, nil, 100);
>+ }
>+ rca = sdiocmd(&SEND_RELATIVE_ADDR, 0) >> Rcashift;
>+ sdiocmd(&SELECT_CARD, rca << Rcashift);
>+ sdio.bus(&sdio, 0, SDfreq);
>+ sdioset(Fn0, Highspeed, 2);
>+ sdioset(Fn0, Busifc, 2); /* bus width 4 */
>+ sdiowr(Fn0, Fbr1+Blksize, 64);
>+ sdiowr(Fn0, Fbr1+Blksize+1, 64>>8);
>+ sdiowr(Fn0, Fbr2+Blksize, 512);
>+ sdiowr(Fn0, Fbr2+Blksize+1, 512>>8);
>+ sdioset(Fn0, Ioenable, 1<<Fn1);
>+ sdiowr(Fn0, Intenable, 0);
>+ for(i = 0; !(sdiord(Fn0, Ioready) & 1<<Fn1); i++){
>+ if(i == 10){
>+ print("ether4330: can't enable SDIO function\n");
>+ error(Eio);
>+ }
>+ tsleep(&up->sleep, return0, nil, 100);
>+ }
>+}
>+
>+static void
>+sdioreset(void)
>+{
>+ sdiowr(Fn0, Ioabort, 1<<3); /* reset */
>+}
>+
>+static void
>+sdioabort(int fn)
>+{
>+ sdiowr(Fn0, Ioabort, fn);
>+}
>+
>+/*
>+ * Chip register and memory access via SDIO
>+ */
>+
>+static void
>+cfgw(ulong off, int val)
>+{
>+ sdiowr(Fn1, off, val);
>+}
>+
>+static int
>+cfgr(ulong off)
>+{
>+ return sdiord(Fn1, off);
>+}
>+
>+static ulong
>+cfgreadl(int fn, ulong off)
>+{
>+ uchar cbuf[2*CACHELINESZ];
>+ uchar *p;
>+
>+ p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ);
>+ memset(p, 0, 4);
>+ sdiorwext(fn, 0, p, 4, off|Sb32bit, 1);
>+ if(SDIODEBUG) print("cfgreadl %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]);
>+ return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
>+}
>+
>+static void
>+cfgwritel(int fn, ulong off, u32int data)
>+{
>+ uchar cbuf[2*CACHELINESZ];
>+ uchar *p;
>+ int retry;
>+
>+ p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ);
>+ put4(p, data);
>+ if(SDIODEBUG) print("cfgwritel %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]);
>+ retry = 0;
>+ while(waserror()){
>+ print("ether4330: cfgwritel retry %lux %ux\n", off, data);
>+ sdioabort(fn);
>+ if(++retry == 3)
>+ nexterror();
>+ }
>+ sdiorwext(fn, 1, p, 4, off|Sb32bit, 1);
>+ poperror();
>+}
>+
>+static void
>+sbwindow(ulong addr)
>+{
>+ addr &= ~(Sbwsize-1);
>+ cfgw(Sbaddr, addr>>8);
>+ cfgw(Sbaddr+1, addr>>16);
>+ cfgw(Sbaddr+2, addr>>24);
>+}
>+
>+static void
>+sbrw(int fn, int write, uchar *buf, int len, ulong off)
>+{
>+ int n;
>+ USED(fn);
>+
>+ if(waserror()){
>+ print("ether4330: sbrw err off %lux len %ud\n", off, len);
>+ nexterror();
>+ }
>+ if(write){
>+ if(len >= 4){
>+ n = len;
>+ n &= ~3;
>+ sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1);
>+ off += n;
>+ buf += n;
>+ len -= n;
>+ }
>+ while(len > 0){
>+ sdiowr(Fn1, off|Sb32bit, *buf);
>+ off++;
>+ buf++;
>+ len--;
>+ }
>+ }else{
>+ if(len >= 4){
>+ n = len;
>+ n &= ~3;
>+ sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1);
>+ off += n;
>+ buf += n;
>+ len -= n;
>+ }
>+ while(len > 0){
>+ *buf = sdiord(Fn1, off|Sb32bit);
>+ off++;
>+ buf++;
>+ len--;
>+ }
>+ }
>+ poperror();
>+}
>+
>+static void
>+sbmem(int write, uchar *buf, int len, ulong off)
>+{
>+ ulong n;
>+
>+ n = ROUNDUP(off, Sbwsize) - off;
>+ if(n == 0)
>+ n = Sbwsize;
>+ while(len > 0){
>+ if(n > len)
>+ n = len;
>+ sbwindow(off);
>+ sbrw(Fn1, write, buf, n, off & (Sbwsize-1));
>+ off += n;
>+ buf += n;
>+ len -= n;
>+ n = Sbwsize;
>+ }
>+}
>+
>+static void
>+packetrw(int write, uchar *buf, int len)
>+{
>+ int n;
>+ int retry;
>+
>+ n = 2048;
>+ while(len > 0){
>+ if(n > len)
>+ n = ROUND(len, 4);
>+ retry = 0;
>+ while(waserror()){
>+ sdioabort(Fn2);
>+ if(++retry == 3)
>+ nexterror();
>+ }
>+ sdiorwext(Fn2, write, buf, n, Enumbase, 0);
>+ poperror();
>+ buf += n;
>+ len -= n;
>+ }
>+}
>+
>+/*
>+ * Configuration and control of chip cores via Silicon Backplane
>+ */
>+
>+static void
>+sbdisable(ulong regs, int pre, int ioctl)
>+{
>+ sbwindow(regs);
>+ if((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){
>+ cfgwritel(Fn1, regs + Ioctrl, 3|ioctl);
>+ cfgreadl(Fn1, regs + Ioctrl);
>+ return;
>+ }
>+ cfgwritel(Fn1, regs + Ioctrl, 3|pre);
>+ cfgreadl(Fn1, regs + Ioctrl);
>+ cfgwritel(Fn1, regs + Resetctrl, 1);
>+ microdelay(10);
>+ while((cfgreadl(Fn1, regs + Resetctrl) & 1) == 0)
>+ ;
>+ cfgwritel(Fn1, regs + Ioctrl, 3|ioctl);
>+ cfgreadl(Fn1, regs + Ioctrl);
>+}
>+
>+static void
>+sbreset(ulong regs, int pre, int ioctl)
>+{
>+ sbdisable(regs, pre, ioctl);
>+ sbwindow(regs);
>+ if(SBDEBUG) print("sbreset %#lux %#lux %#lux ->", regs,
>+ cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl));
>+ while((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){
>+ cfgwritel(Fn1, regs + Resetctrl, 0);
>+ microdelay(40);
>+ }
>+ cfgwritel(Fn1, regs + Ioctrl, 1|ioctl);
>+ cfgreadl(Fn1, regs + Ioctrl);
>+ if(SBDEBUG) print("%#lux %#lux\n",
>+ cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl));
>+}
>+
>+static void
>+corescan(Ctlr *ctl, ulong r)
>+{
>+ uchar *buf;
>+ int i, coreid, corerev;
>+ ulong addr;
>+
>+ buf = sdmalloc(Corescansz);
>+ if(buf == nil)
>+ error(Enomem);
>+ sbmem(0, buf, Corescansz, r);
>+ coreid = 0;
>+ corerev = 0;
>+ for(i = 0; i < Corescansz; i += 4){
>+ switch(buf[i]&0xF){
>+ case 0xF: /* end */
>+ sdfree(buf);
>+ return;
>+ case 0x1: /* core info */
>+ if((buf[i+4]&0xF) != 0x1)
>+ break;
>+ coreid = (buf[i+1] | buf[i+2]<<8) & 0xFFF;
>+ i += 4;
>+ corerev = buf[i+3];
>+ break;
>+ case 0x05: /* address */
>+ addr = buf[i+1]<<8 | buf[i+2]<<16 | buf[i+3]<<24;
>+ addr &= ~0xFFF;
>+ if(SBDEBUG) print("core %x %s %#lux\n", coreid, buf[i]&0xC0? "ctl" : "mem", addr);
>+ switch(coreid){
>+ case 0x800:
>+ if((buf[i] & 0xC0) == 0)
>+ ctl->chipcommon = addr;
>+ break;
>+ case ARMcm3:
>+ case ARM7tdmi:
>+ case ARMcr4:
>+ ctl->armcore = coreid;
>+ if(buf[i] & 0xC0){
>+ if(ctl->armctl == 0)
>+ ctl->armctl = addr;
>+ }else{
>+ if(ctl->armregs == 0)
>+ ctl->armregs = addr;
>+ }
>+ break;
>+ case 0x80E:
>+ if(buf[i] & 0xC0)
>+ ctl->socramctl = addr;
>+ else if(ctl->socramregs == 0)
>+ ctl->socramregs = addr;
>+ ctl->socramrev = corerev;
>+ break;
>+ case 0x829:
>+ if((buf[i] & 0xC0) == 0)
>+ ctl->sdregs = addr;
>+ ctl->sdiorev = corerev;
>+ break;
>+ case 0x812:
>+ if(buf[i] & 0xC0)
>+ ctl->d11ctl = addr;
>+ break;
>+ }
>+ }
>+ }
>+ sdfree(buf);
>+}
>+
>+static void
>+ramscan(Ctlr *ctl)
>+{
>+ ulong r, n, size;
>+ int banks, i;
>+
>+ if(ctl->armcore == ARMcr4){
>+ r = ctl->armregs;
>+ sbwindow(r);
>+ n = cfgreadl(Fn1, r + Cr4Cap);
>+ if(SBDEBUG) print("cr4 banks %lux\n", n);
>+ banks = ((n>>4) & 0xF) + (n & 0xF);
>+ size = 0;
>+ for(i = 0; i < banks; i++){
>+ cfgwritel(Fn1, r + Cr4Bankidx, i);
>+ n = cfgreadl(Fn1, r + Cr4Bankinfo);
>+ if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1));
>+ size += 8192 * ((n & 0x3F) + 1);
>+ }
>+ ctl->socramsize = size;
>+ ctl->rambase = 0x198000;
>+ return;
>+ }
>+ if(ctl->socramrev <= 7 || ctl->socramrev == 12){
>+ print("ether4330: SOCRAM rev %d not supported\n", ctl->socramrev);
>+ error(Eio);
>+ }
>+ sbreset(ctl->socramctl, 0, 0);
>+ r = ctl->socramregs;
>+ sbwindow(r);
>+ n = cfgreadl(Fn1, r + Coreinfo);
>+ if(SBDEBUG) print("socramrev %d coreinfo %lux\n", ctl->socramrev, n);
>+ banks = (n>>4) & 0xF;
>+ size = 0;
>+ for(i = 0; i < banks; i++){
>+ cfgwritel(Fn1, r + Bankidx, i);
>+ n = cfgreadl(Fn1, r + Bankinfo);
>+ if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1));
>+ size += 8192 * ((n & 0x3F) + 1);
>+ }
>+ ctl->socramsize = size;
>+ ctl->rambase = 0;
>+ if(ctl->chipid == 43430){
>+ cfgwritel(Fn1, r + Bankidx, 3);
>+ cfgwritel(Fn1, r + Bankpda, 0);
>+ }
>+}
>+
>+static void
>+sbinit(Ctlr *ctl)
>+{
>+ ulong r;
>+ int chipid;
>+ char buf[16];
>+
>+ sbwindow(Enumbase);
>+ r = cfgreadl(Fn1, Enumbase);
>+ chipid = r & 0xFFFF;
>+ sprint(buf, chipid > 43000 ? "%d" : "%#x", chipid);
>+ print("ether4330: chip %s rev %ld type %ld\n", buf, (r>>16)&0xF, (r>>28)&0xF);
>+ switch(chipid){
>+ case 0x4330:
>+ case 43362:
>+ case 43430:
>+ case 0x4345:
>+ ctl->chipid = chipid;
>+ ctl->chiprev = (r>>16)&0xF;
>+ break;
>+ default:
>+ print("ether4330: chipid %#x (%d) not supported\n", chipid, chipid);
>+ error(Eio);
>+ }
>+ r = cfgreadl(Fn1, Enumbase + 63*4);
>+ corescan(ctl, r);
>+ if(ctl->armctl == 0 || ctl->d11ctl == 0 ||
>+ (ctl->armcore == ARMcm3 && (ctl->socramctl == 0 || ctl->socramregs == 0)))
>+ error("corescan didn't find essential cores\n");
>+ if(ctl->armcore == ARMcr4)
>+ sbreset(ctl->armctl, Cr4Cpuhalt, Cr4Cpuhalt);
>+ else
>+ sbdisable(ctl->armctl, 0, 0);
>+ sbreset(ctl->d11ctl, 8|4, 4);
>+ ramscan(ctl);
>+ if(SBDEBUG) print("ARM %#lux D11 %#lux SOCRAM %#lux,%#lux %lud bytes @ %#lux\n",
>+ ctl->armctl, ctl->d11ctl, ctl->socramctl, ctl->socramregs, ctl->socramsize, ctl->rambase);
>+ cfgw(Clkcsr, 0);
>+ microdelay(10);
>+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
>+ cfgw(Clkcsr, Nohwreq | ReqALP);
>+ while((cfgr(Clkcsr) & (HTavail|ALPavail)) == 0)
>+ microdelay(10);
>+ cfgw(Clkcsr, Nohwreq | ForceALP);
>+ microdelay(65);
>+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
>+ cfgw(Pullups, 0);
>+ sbwindow(ctl->chipcommon);
>+ cfgwritel(Fn1, ctl->chipcommon + Gpiopullup, 0);
>+ cfgwritel(Fn1, ctl->chipcommon + Gpiopulldown, 0);
>+ if(ctl->chipid != 0x4330 && ctl->chipid != 43362)
>+ return;
>+ cfgwritel(Fn1, ctl->chipcommon + Chipctladdr, 1);
>+ if(cfgreadl(Fn1, ctl->chipcommon + Chipctladdr) != 1)
>+ print("ether4330: can't set Chipctladdr\n");
>+ else{
>+ r = cfgreadl(Fn1, ctl->chipcommon + Chipctldata);
>+ if(SBDEBUG) print("chipcommon PMU (%lux) %lux", cfgreadl(Fn1, ctl->chipcommon + Chipctladdr), r);
>+ /* set SDIO drive strength >= 6mA */
>+ r &= ~0x3800;
>+ if(ctl->chipid == 0x4330)
>+ r |= 3<<11;
>+ else
>+ r |= 7<<11;
>+ cfgwritel(Fn1, ctl->chipcommon + Chipctldata, r);
>+ if(SBDEBUG) print("-> %lux (= %lux)\n", r, cfgreadl(Fn1, ctl->chipcommon + Chipctldata));
>+ }
>+}
>+
>+static void
>+sbenable(Ctlr *ctl)
>+{
>+ int i;
>+
>+ if(SBDEBUG) print("enabling HT clock...");
>+ cfgw(Clkcsr, 0);
>+ delay(1);
>+ cfgw(Clkcsr, ReqHT);
>+ for(i = 0; (cfgr(Clkcsr) & HTavail) == 0; i++){
>+ if(i == 50){
>+ print("ether4330: can't enable HT clock: csr %x\n", cfgr(Clkcsr));
>+ error(Eio);
>+ }
>+ tsleep(&up->sleep, return0, nil, 100);
>+ }
>+ cfgw(Clkcsr, cfgr(Clkcsr) | ForceHT);
>+ delay(10);
>+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
>+ sbwindow(ctl->sdregs);
>+ cfgwritel(Fn1, ctl->sdregs + Sbmboxdata, 4 << 16); /* protocol version */
>+ cfgwritel(Fn1, ctl->sdregs + Intmask, FrameInt | MailboxInt | Fcchange);
>+ sdioset(Fn0, Ioenable, 1<<Fn2);
>+ for(i = 0; !(sdiord(Fn0, Ioready) & 1<<Fn2); i++){
>+ if(i == 10){
>+ print("ether4330: can't enable SDIO function 2 - ioready %x\n", sdiord(Fn0, Ioready));
>+ error(Eio);
>+ }
>+ tsleep(&up->sleep, return0, nil, 100);
>+ }
>+ sdiowr(Fn0, Intenable, (1<<Fn1) | (1<<Fn2) | 1);
>+}
>+
>+
>+/*
>+ * Firmware and config file uploading
>+ */
>+
>+/*
>+ * Condense config file contents (in buffer buf with length n)
>+ * to 'var=value\0' list for firmware:
>+ * - remove comments (starting with '#') and blank lines
>+ * - remove carriage returns
>+ * - convert newlines to nulls
>+ * - mark end with two nulls
>+ * - pad with nulls to multiple of 4 bytes total length
>+ */
>+static int
>+condense(uchar *buf, int n)
>+{
>+ uchar *p, *ep, *lp, *op;
>+ int c, skipping;
>+
>+ skipping = 0; /* true if in a comment */
>+ ep = buf + n; /* end of input */
>+ op = buf; /* end of output */
>+ lp = buf; /* start of current output line */
>+ for(p = buf; p < ep; p++){
>+ switch(c = *p){
>+ case '#':
>+ skipping = 1;
>+ break;
>+ case '\0':
>+ case '\n':
>+ skipping = 0;
>+ if(op != lp){
>+ *op++ = '\0';
>+ lp = op;
>+ }
>+ break;
>+ case '\r':
>+ break;
>+ default:
>+ if(!skipping)
>+ *op++ = c;
>+ break;
>+ }
>+ }
>+ if(!skipping && op != lp)
>+ *op++ = '\0';
>+ *op++ = '\0';
>+ for(n = op - buf; n & 03; n++)
>+ *op++ = '\0';
>+ return n;
>+}
>+
>+/*
>+ * Try to find firmware file in /boot or in /lib/firmware.
>+ * Throw an error if not found.
>+ */
>+static Chan*
>+findfirmware(char *file)
>+{
>+ char nbuf[64];
>+ Chan *c;
>+
>+ if(!waserror()){
>+ snprint(nbuf, sizeof nbuf, "/boot/%s", file);
>+ c = namec(nbuf, Aopen, OREAD, 0);
>+ poperror();
>+ }else if(!waserror()){
>+ snprint(nbuf, sizeof nbuf, "/lib/firmware/%s", file);
>+ c = namec(nbuf, Aopen, OREAD, 0);
>+ poperror();
>+ }else{
>+ c = nil;
>+ snprint(up->genbuf, sizeof up->genbuf, "can't find %s in /boot or /lib/firmware", file);
>+ error(up->genbuf);
>+ }
>+ return c;
>+}
>+
>+static int
>+upload(Ctlr *ctl, char *file, int isconfig)
>+{
>+ Chan *c;
>+ uchar *buf;
>+ uchar *cbuf;
>+ int off, n;
>+
>+ buf = cbuf = nil;
>+ c = findfirmware(file);
>+ if(waserror()){
>+ cclose(c);
>+ sdfree(buf);
>+ sdfree(cbuf);
>+ nexterror();
>+ }
>+ buf = sdmalloc(Uploadsz);
>+ if(buf == nil)
>+ error(Enomem);
>+ if(Firmwarecmp){
>+ cbuf = sdmalloc(Uploadsz);
>+ if(cbuf == nil)
>+ error(Enomem);
>+ }
>+ off = 0;
>+ for(;;){
>+ n = devtab[c->type]->read(c, buf, Uploadsz, off);
>+ if(n <= 0)
>+ break;
>+ if(isconfig){
>+ n = condense(buf, n);
>+ off = ctl->socramsize - n - 4;
>+ }else if(off == 0)
>+ memmove(ctl->resetvec.c, buf, sizeof(ctl->resetvec.c));
>+ while(n&3)
>+ buf[n++] = 0;
>+ sbmem(1, buf, n, ctl->rambase + off);
>+ if(isconfig)
>+ break;
>+ off += n;
>+ }
>+ if(Firmwarecmp){
>+ if(FWDEBUG) print("compare...");
>+ if(!isconfig)
>+ off = 0;
>+ for(;;){
>+ if(!isconfig){
>+ n = devtab[c->type]->read(c, buf, Uploadsz, off);
>+ if(n <= 0)
>+ break;
>+ while(n&3)
>+ buf[n++] = 0;
>+ }
>+ sbmem(0, cbuf, n, ctl->rambase + off);
>+ if(memcmp(buf, cbuf, n) != 0){
>+ print("ether4330: firmware load failed offset %d\n", off);
>+ error(Eio);
>+ }
>+ if(isconfig)
>+ break;
>+ off += n;
>+ }
>+ }
>+ if(FWDEBUG) print("\n");
>+ poperror();
>+ cclose(c);
>+ sdfree(buf);
>+ sdfree(cbuf);
>+ return n;
>+}
>+
>+/*
>+ * Upload regulatory file (.clm) to firmware.
>+ * Packet format is
>+ * [2]flag [2]type [4]len [4]crc [len]data
>+ */
>+static void
>+reguload(Ctlr *ctl, char *file)
>+{
>+ Chan *c;
>+ uchar *buf;
>+ int off, n, flag;
>+ enum {
>+ Reguhdr = 2+2+4+4,
>+ Regusz = 1400,
>+ Regutyp = 2,
>+ Flagclm = 1<<12,
>+ Firstpkt= 1<<1,
>+ Lastpkt = 1<<2,
>+ };
>+
>+ buf = nil;
>+ c = findfirmware(file);
>+ if(waserror()){
>+ cclose(c);
>+ free(buf);
>+ nexterror();
>+ }
>+ buf = malloc(Reguhdr+Regusz+1);
>+ if(buf == nil)
>+ error(Enomem);
>+ put2(buf+2, Regutyp);
>+ put2(buf+8, 0);
>+ off = 0;
>+ flag = Flagclm | Firstpkt;
>+ while((flag&Lastpkt) == 0){
>+ n = devtab[c->type]->read(c, buf+Reguhdr, Regusz+1, off);
>+ if(n <= 0)
>+ break;
>+ if(n == Regusz+1)
>+ --n;
>+ else{
>+ while(n&7)
>+ buf[Reguhdr+n++] = 0;
>+ flag |= Lastpkt;
>+ }
>+ put2(buf+0, flag);
>+ put4(buf+4, n);
>+ wlsetvar(ctl, "clmload", buf, Reguhdr + n);
>+ off += n;
>+ flag &= ~Firstpkt;
>+ }
>+ poperror();
>+ cclose(c);
>+ free(buf);
>+}
>+
>+static void
>+fwload(Ctlr *ctl)
>+{
>+ uchar buf[4];
>+ uint i, n;
>+
>+ i = 0;
>+ while(firmware[i].chipid != ctl->chipid ||
>+ firmware[i].chiprev != ctl->chiprev){
>+ if(++i == nelem(firmware)){
>+ print("ether4330: no firmware for chipid %x (%d) chiprev %d\n",
>+ ctl->chipid, ctl->chipid, ctl->chiprev);
>+ error("no firmware");
>+ }
>+ }
>+ ctl->regufile = firmware[i].regufile;
>+ cfgw(Clkcsr, ReqALP);
>+ while((cfgr(Clkcsr) & ALPavail) == 0)
>+ microdelay(10);
>+ memset(buf, 0, 4);
>+ sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4);
>+ if(FWDEBUG) print("firmware load...");
>+ upload(ctl, firmware[i].fwfile, 0);
>+ if(FWDEBUG) print("config load...");
>+ n = upload(ctl, firmware[i].cfgfile, 1);
>+ n /= 4;
>+ n = (n & 0xFFFF) | (~n << 16);
>+ put4(buf, n);
>+ sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4);
>+ if(ctl->armcore == ARMcr4){
>+ sbwindow(ctl->sdregs);
>+ cfgwritel(Fn1, ctl->sdregs + Intstatus, ~0);
>+ if(ctl->resetvec.i != 0){
>+ if(SBDEBUG) print("%ux\n", ctl->resetvec.i);
>+ sbmem(1, ctl->resetvec.c, sizeof(ctl->resetvec.c), 0);
>+ }
>+ sbreset(ctl->armctl, Cr4Cpuhalt, 0);
>+ }else
>+ sbreset(ctl->armctl, 0, 0);
>+}
>+
>+/*
>+ * Communication of data and control packets
>+ */
>+
>+void
>+intwait(Ctlr *ctlr, int wait)
>+{
>+ ulong ints, mbox;
>+ int i;
>+
>+ if(waserror())
>+ return;
>+ for(;;){
>+ sdiocardintr(wait);
>+ sbwindow(ctlr->sdregs);
>+ i = sdiord(Fn0, Intpend);
>+ if(i == 0){
>+ tsleep(&up->sleep, return0, 0, 10);
>+ continue;
>+ }
>+ ints = cfgreadl(Fn1, ctlr->sdregs + Intstatus);
>+ cfgwritel(Fn1, ctlr->sdregs + Intstatus, ints);
>+ if(0) print("INTS: (%x) %lux -> %lux\n", i, ints, cfgreadl(Fn1, ctlr->sdregs + Intstatus));
>+ if(ints & MailboxInt){
>+ mbox = cfgreadl(Fn1, ctlr->sdregs + Hostmboxdata);
>+ cfgwritel(Fn1, ctlr->sdregs + Sbmbox, 2); /* ack */
>+ if(mbox & 0x8)
>+ print("ether4330: firmware ready\n");
>+ }
>+ if(ints & FrameInt)
>+ break;
>+ }
>+ poperror();
>+}
>+
>+static Block*
>+wlreadpkt(Ctlr *ctl)
>+{
>+ Block *b;
>+ Sdpcm *p;
>+ int len, lenck;
>+
>+ b = allocb(2048);
>+ p = (Sdpcm*)b->wp;
>+ qlock(&ctl->pktlock);
>+ for(;;){
>+ packetrw(0, b->wp, Sdpcmsz);
>+ len = p->len[0] | p->len[1]<<8;
>+ if(len == 0){
>+ freeb(b);
>+ b = nil;
>+ break;
>+ }
>+ lenck = p->lenck[0] | p->lenck[1]<<8;
>+ if(lenck != (len ^ 0xFFFF) ||
>+ len < Sdpcmsz || len > 2048){
>+ print("ether4330: wlreadpkt error len %.4x lenck %.4x\n", len, lenck);
>+ cfgw(Framectl, Rfhalt);
>+ while(cfgr(Rfrmcnt+1))
>+ ;
>+ while(cfgr(Rfrmcnt))
>+ ;
>+ continue;
>+ }
>+ if(len > Sdpcmsz)
>+ packetrw(0, b->wp + Sdpcmsz, len - Sdpcmsz);
>+ b->wp += len;
>+ break;
>+ }
>+ qunlock(&ctl->pktlock);
>+ return b;
>+}
>+
>+static void
>+txstart(Ether *edev)
>+{
>+ Ctlr *ctl;
>+ Sdpcm *p;
>+ Block *b;
>+ int len, off;
>+
>+ ctl = edev->ctlr;
>+ if(!canqlock(&ctl->tlock))
>+ return;
>+ if(waserror()){
>+ qunlock(&ctl->tlock);
>+ return;
>+ }
>+ for(;;){
>+ lock(&ctl->txwinlock);
>+ if(ctl->txseq == ctl->txwindow){
>+ //print("f");
>+ unlock(&ctl->txwinlock);
>+ break;
>+ }
>+ if(ctl->fcmask & 1<<2){
>+ //print("x");
>+ unlock(&ctl->txwinlock);
>+ break;
>+ }
>+ unlock(&ctl->txwinlock);
>+ b = qget(edev->oq);
>+ if(b == nil)
>+ break;
>+ off = ((uintptr)b->rp & 3) + Sdpcmsz;
>+ b = padblock(b, off + 4);
>+ len = BLEN(b);
>+ p = (Sdpcm*)b->rp;
>+ memset(p, 0, off); /* TODO: refactor dup code */
>+ put2(p->len, len);
>+ put2(p->lenck, ~len);
>+ p->chanflg = 2;
>+ p->seq = ctl->txseq;
>+ p->doffset = off;
>+ put4(b->rp + off, 0x20); /* BDC header */
>+ if(iodebug) dump("send", b->rp, len);
>+ qlock(&ctl->pktlock);
>+ if(waserror()){
>+ if(iodebug) print("halt frame %x %x\n", cfgr(Wfrmcnt+1), cfgr(Wfrmcnt+1));
>+ cfgw(Framectl, Wfhalt);
>+ while(cfgr(Wfrmcnt+1))
>+ ;
>+ while(cfgr(Wfrmcnt))
>+ ;
>+ qunlock(&ctl->pktlock);
>+ nexterror();
>+ }
>+ packetrw(1, b->rp, len);
>+ ctl->txseq++;
>+ poperror();
>+ qunlock(&ctl->pktlock);
>+ freeb(b);
>+ }
>+ poperror();
>+ qunlock(&ctl->tlock);
>+}
>+
>+static void
>+rproc(void *a)
>+{
>+ Ether *edev;
>+ Ctlr *ctl;
>+ Block *b;
>+ Sdpcm *p;
>+ Cmd *q;
>+ int flowstart;
>+ int bdc;
>+
>+ edev = a;
>+ ctl = edev->ctlr;
>+ flowstart = 0;
>+ for(;;){
>+ if(flowstart){
>+ //print("F");
>+ flowstart = 0;
>+ txstart(edev);
>+ }
>+ b = wlreadpkt(ctl);
>+ if(b == nil){
>+ intwait(ctl, 1);
>+ continue;
>+ }
>+ p = (Sdpcm*)b->rp;
>+ if(p->window != ctl->txwindow || p->fcmask != ctl->fcmask){
>+ lock(&ctl->txwinlock);
>+ if(p->window != ctl->txwindow){
>+ if(ctl->txseq == ctl->txwindow)
>+ flowstart = 1;
>+ ctl->txwindow = p->window;
>+ }
>+ if(p->fcmask != ctl->fcmask){
>+ if((p->fcmask & 1<<2) == 0)
>+ flowstart = 1;
>+ ctl->fcmask = p->fcmask;
>+ }
>+ unlock(&ctl->txwinlock);
>+ }
>+ switch(p->chanflg & 0xF){
>+ case 0:
>+ if(iodebug) dump("rsp", b->rp, BLEN(b));
>+ if(BLEN(b) < Sdpcmsz + Cmdsz)
>+ break;
>+ q = (Cmd*)(b->rp + Sdpcmsz);
>+ if((q->id[0] | q->id[1]<<8) != ctl->reqid)
>+ break;
>+ ctl->rsp = b;
>+ wakeup(&ctl->cmdr);
>+ continue;
>+ case 1:
>+ if(iodebug) dump("event", b->rp, BLEN(b));
>+ if(BLEN(b) > p->doffset + 4){
>+ bdc = 4 + (b->rp[p->doffset + 3] << 2);
>+ if(BLEN(b) > p->doffset + bdc){
>+ b->rp += p->doffset + bdc; /* skip BDC header */
>+ bcmevent(ctl, b->rp, BLEN(b));
>+ break;
>+ }
>+ }
>+ if(iodebug && BLEN(b) != p->doffset)
>+ print("short event %lld %d\n", BLEN(b), p->doffset);
>+ break;
>+ case 2:
>+ if(iodebug) dump("packet", b->rp, BLEN(b));
>+ if(BLEN(b) > p->doffset + 4){
>+ bdc = 4 + (b->rp[p->doffset + 3] << 2);
>+ if(BLEN(b) >= p->doffset + bdc + ETHERHDRSIZE){
>+ b->rp += p->doffset + bdc; /* skip BDC header */
>+ etheriq(edev, b);
>+ continue;
>+ }
>+ }
>+ break;
>+ default:
>+ dump("ether4330: bad packet", b->rp, BLEN(b));
>+ break;
>+ }
>+ freeb(b);
>+ }
>+}
>+
>+static void
>+linkdown(Ctlr *ctl)
>+{
>+ Ether *edev;
>+ Netfile *f;
>+ int i;
>+
>+ edev = ctl->edev;
>+ if(edev == nil || ctl->status != Connected)
>+ return;
>+ ctl->status = Disconnected;
>+ /* send eof to aux/wpa */
>+ for(i = 0; i < edev->nfile; i++){
>+ f = edev->f[i];
>+ if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e)
>+ continue;
>+ qwrite(f->in, 0, 0);
>+ }
>+}
>+
>+/*
>+ * Command interface between host and firmware
>+ */
>+
>+static char *eventnames[] = {
>+ [0] = "set ssid",
>+ [1] = "join",
>+ [2] = "start",
>+ [3] = "auth",
>+ [4] = "auth ind",
>+ [5] = "deauth",
>+ [6] = "deauth ind",
>+ [7] = "assoc",
>+ [8] = "assoc ind",
>+ [9] = "reassoc",
>+ [10] = "reassoc ind",
>+ [11] = "disassoc",
>+ [12] = "disassoc ind",
>+ [13] = "quiet start",
>+ [14] = "quiet end",
>+ [15] = "beacon rx",
>+ [16] = "link",
>+ [17] = "mic error",
>+ [18] = "ndis link",
>+ [19] = "roam",
>+ [20] = "txfail",
>+ [21] = "pmkid cache",
>+ [22] = "retrograde tsf",
>+ [23] = "prune",
>+ [24] = "autoauth",
>+ [25] = "eapol msg",
>+ [26] = "scan complete",
>+ [27] = "addts ind",
>+ [28] = "delts ind",
>+ [29] = "bcnsent ind",
>+ [30] = "bcnrx msg",
>+ [31] = "bcnlost msg",
>+ [32] = "roam prep",
>+ [33] = "pfn net found",
>+ [34] = "pfn net lost",
>+ [35] = "reset complete",
>+ [36] = "join start",
>+ [37] = "roam start",
>+ [38] = "assoc start",
>+ [39] = "ibss assoc",
>+ [40] = "radio",
>+ [41] = "psm watchdog",
>+ [44] = "probreq msg",
>+ [45] = "scan confirm ind",
>+ [46] = "psk sup",
>+ [47] = "country code changed",
>+ [48] = "exceeded medium time",
>+ [49] = "icv error",
>+ [50] = "unicast decode error",
>+ [51] = "multicast decode error",
>+ [52] = "trace",
>+ [53] = "bta hci event",
>+ [54] = "if",
>+ [55] = "p2p disc listen complete",
>+ [56] = "rssi",
>+ [57] = "pfn scan complete",
>+ [58] = "extlog msg",
>+ [59] = "action frame",
>+ [60] = "action frame complete",
>+ [61] = "pre assoc ind",
>+ [62] = "pre reassoc ind",
>+ [63] = "channel adopted",
>+ [64] = "ap started",
>+ [65] = "dfs ap stop",
>+ [66] = "dfs ap resume",
>+ [67] = "wai sta event",
>+ [68] = "wai msg",
>+ [69] = "escan result",
>+ [70] = "action frame off chan complete",
>+ [71] = "probresp msg",
>+ [72] = "p2p probreq msg",
>+ [73] = "dcs request",
>+ [74] = "fifo credit map",
>+ [75] = "action frame rx",
>+ [76] = "wake event",
>+ [77] = "rm complete",
>+ [78] = "htsfsync",
>+ [79] = "overlay req",
>+ [80] = "csa complete ind",
>+ [81] = "excess pm wake event",
>+ [82] = "pfn scan none",
>+ [83] = "pfn scan allgone",
>+ [84] = "gtk plumbed",
>+ [85] = "assoc ind ndis",
>+ [86] = "reassoc ind ndis",
>+ [87] = "assoc req ie",
>+ [88] = "assoc resp ie",
>+ [89] = "assoc recreated",
>+ [90] = "action frame rx ndis",
>+ [91] = "auth req",
>+ [92] = "tdls peer event",
>+ [127] = "bcmc credit support"
>+};
>+
>+static char*
>+evstring(uint event)
>+{
>+ static char buf[12];
>+
>+ if(event >= nelem(eventnames) || eventnames[event] == 0){
>+ /* not reentrant but only called from one kproc */
>+ snprint(buf, sizeof buf, "%d", event);
>+ return buf;
>+ }
>+ return eventnames[event];
>+}
>+
>+static void
>+bcmevent(Ctlr *ctl, uchar *p, int len)
>+{
>+ int flags;
>+ long event, status, reason;
>+
>+ if(len < ETHERHDRSIZE + 10 + 46)
>+ return;
>+ p += ETHERHDRSIZE + 10; /* skip bcm_ether header */
>+ len -= ETHERHDRSIZE + 10;
>+ flags = nhgets(p + 2);
>+ event = nhgets(p + 6);
>+ status = nhgetl(p + 8);
>+ reason = nhgetl(p + 12);
>+ if(EVENTDEBUG)
>+ print("ether4330: [%s] status %ld flags %#x reason %ld\n",
>+ evstring(event), status, flags, reason);
>+ switch(event){
>+ case 19: /* E_ROAM */
>+ if(status == 0)
>+ break;
>+ /* fall through */
>+ case 0: /* E_SET_SSID */
>+ ctl->joinstatus = 1 + status;
>+ wakeup(&ctl->joinr);
>+ break;
>+ case 16: /* E_LINK */
>+ if(flags&1) /* link up */
>+ break;
>+ /* fall through */
>+ case 5: /* E_DEAUTH */
>+ case 6: /* E_DEAUTH_IND */
>+ case 12: /* E_DISASSOC_IND */
>+ linkdown(ctl);
>+ break;
>+ case 26: /* E_SCAN_COMPLETE */
>+ break;
>+ case 69: /* E_ESCAN_RESULT */
>+ wlscanresult(ctl->edev, p + 48, len - 48);
>+ break;
>+ default:
>+ if(status){
>+ if(!EVENTDEBUG)
>+ print("ether4330: [%s] error status %ld flags %#x reason %ld\n",
>+ evstring(event), status, flags, reason);
>+ dump("event", p, len);
>+ }
>+ }
>+}
>+
>+static int
>+joindone(void *a)
>+{
>+ return ((Ctlr*)a)->joinstatus;
>+}
>+
>+static int
>+waitjoin(Ctlr *ctl)
>+{
>+ int n;
>+
>+ sleep(&ctl->joinr, joindone, ctl);
>+ n = ctl->joinstatus;
>+ ctl->joinstatus = 0;
>+ return n - 1;
>+}
>+
>+static int
>+cmddone(void *a)
>+{
>+ return ((Ctlr*)a)->rsp != nil;
>+}
>+
>+static void
>+wlcmd(Ctlr *ctl, int write, int op, void *data, int dlen, void *res, int rlen)
>+{
>+ Block *b;
>+ Sdpcm *p;
>+ Cmd *q;
>+ int len, tlen;
>+
>+ if(write)
>+ tlen = dlen + rlen;
>+ else
>+ tlen = MAX(dlen, rlen);
>+ len = Sdpcmsz + Cmdsz + tlen;
>+ b = allocb(len);
>+ qlock(&ctl->cmdlock);
>+ if(waserror()){
>+ freeb(b);
>+ qunlock(&ctl->cmdlock);
>+ nexterror();
>+ }
>+ memset(b->wp, 0, len);
>+ qlock(&ctl->pktlock);
>+ p = (Sdpcm*)b->wp;
>+ put2(p->len, len);
>+ put2(p->lenck, ~len);
>+ p->seq = ctl->txseq;
>+ p->doffset = Sdpcmsz;
>+ b->wp += Sdpcmsz;
>+
>+ q = (Cmd*)b->wp;
>+ put4(q->cmd, op);
>+ put4(q->len, tlen);
>+ put2(q->flags, write? 2 : 0);
>+ put2(q->id, ++ctl->reqid);
>+ put4(q->status, 0);
>+ b->wp += Cmdsz;
>+
>+ if(dlen > 0)
>+ memmove(b->wp, data, dlen);
>+ if(write)
>+ memmove(b->wp + dlen, res, rlen);
>+ b->wp += tlen;
>+
>+ if(iodebug) dump("cmd", b->rp, len);
>+ packetrw(1, b->rp, len);
>+ ctl->txseq++;
>+ qunlock(&ctl->pktlock);
>+ freeb(b);
>+ b = nil;
>+ USED(b);
>+ sleep(&ctl->cmdr, cmddone, ctl);
>+ b = ctl->rsp;
>+ ctl->rsp = nil;
>+ assert(b != nil);
>+ p = (Sdpcm*)b->rp;
>+ q = (Cmd*)(b->rp + p->doffset);
>+ if(q->status[0] | q->status[1] | q->status[2] | q->status[3]){
>+ print("ether4330: cmd %d error status %ld\n", op, get4(q->status));
>+ dump("ether4330: cmd error", b->rp, BLEN(b));
>+ error("wlcmd error");
>+ }
>+ if(!write)
>+ memmove(res, q + 1, rlen);
>+ freeb(b);
>+ qunlock(&ctl->cmdlock);
>+ poperror();
>+}
>+
>+static void
>+wlcmdint(Ctlr *ctl, int op, int val)
>+{
>+ uchar buf[4];
>+
>+ put4(buf, val);
>+ wlcmd(ctl, 1, op, buf, 4, nil, 0);
>+}
>+
>+static void
>+wlgetvar(Ctlr *ctl, char *name, void *val, int len)
>+{
>+ wlcmd(ctl, 0, GetVar, name, strlen(name) + 1, val, len);
>+}
>+
>+static void
>+wlsetvar(Ctlr *ctl, char *name, void *val, int len)
>+{
>+ if(VARDEBUG){
>+ char buf[32];
>+ snprint(buf, sizeof buf, "wlsetvar %s:", name);
>+ dump(buf, val, len);
>+ }
>+ wlcmd(ctl, 1, SetVar, name, strlen(name) + 1, val, len);
>+}
>+
>+static void
>+wlsetint(Ctlr *ctl, char *name, int val)
>+{
>+ uchar buf[4];
>+
>+ put4(buf, val);
>+ wlsetvar(ctl, name, buf, 4);
>+}
>+
>+static void
>+wlwepkey(Ctlr *ctl, int i)
>+{
>+ uchar params[164];
>+ uchar *p;
>+
>+ memset(params, 0, sizeof params);
>+ p = params;
>+ p = put4(p, i); /* index */
>+ p = put4(p, ctl->keys[i].len);
>+ memmove(p, ctl->keys[i].dat, ctl->keys[i].len);
>+ p += 32 + 18*4; /* keydata, pad */
>+ if(ctl->keys[i].len == WMinKeyLen)
>+ p = put4(p, 1); /* algo = WEP1 */
>+ else
>+ p = put4(p, 3); /* algo = WEP128 */
>+ put4(p, 2); /* flags = Primarykey */
>+
>+ wlsetvar(ctl, "wsec_key", params, sizeof params);
>+}
>+
>+static void
>+memreverse(char *dst, char *src, int len)
>+{
>+ src += len;
>+ while(len-- > 0)
>+ *dst++ = *--src;
>+}
>+
>+static void
>+wlwpakey(Ctlr *ctl, int id, uvlong iv, uchar *ea)
>+{
>+ uchar params[164];
>+ uchar *p;
>+ int pairwise;
>+
>+ if(id == CMrxkey)
>+ return;
>+ pairwise = (id == CMrxkey || id == CMtxkey);
>+ memset(params, 0, sizeof params);
>+ p = params;
>+ if(pairwise)
>+ p = put4(p, 0);
>+ else
>+ p = put4(p, id - CMrxkey0); /* group key id */
>+ p = put4(p, ctl->keys[0].len);
>+ memmove((char*)p, ctl->keys[0].dat, ctl->keys[0].len);
>+ p += 32 + 18*4; /* keydata, pad */
>+ if(ctl->cryptotype == Wpa)
>+ p = put4(p, 2); /* algo = TKIP */
>+ else
>+ p = put4(p, 4); /* algo = AES_CCM */
>+ if(pairwise)
>+ p = put4(p, 0);
>+ else
>+ p = put4(p, 2); /* flags = Primarykey */
>+ p += 3*4;
>+ p = put4(p, 0); //pairwise); /* iv initialised */
>+ p += 4;
>+ p = put4(p, iv>>16); /* iv high */
>+ p = put2(p, iv&0xFFFF); /* iv low */
>+ p += 2 + 2*4; /* align, pad */
>+ if(pairwise)
>+ memmove(p, ea, Eaddrlen);
>+
>+ wlsetvar(ctl, "wsec_key", params, sizeof params);
>+}
>+
>+static void
>+wljoin(Ctlr *ctl, char *ssid, int chan)
>+{
>+ uchar params[72];
>+ uchar *p;
>+ int n;
>+
>+ if(chan != 0)
>+ chan |= 0x2b00; /* 20Mhz channel width */
>+ p = params;
>+ n = strlen(ssid);
>+ n = MIN(n, 32);
>+ p = put4(p, n);
>+ memmove(p, ssid, n);
>+ memset(p + n, 0, 32 - n);
>+ p += 32;
>+ p = put4(p, 0xff); /* scan type */
>+ if(chan != 0){
>+ p = put4(p, 2); /* num probes */
>+ p = put4(p, 120); /* active time */
>+ p = put4(p, 390); /* passive time */
>+ }else{
>+ p = put4(p, -1); /* num probes */
>+ p = put4(p, -1); /* active time */
>+ p = put4(p, -1); /* passive time */
>+ }
>+ p = put4(p, -1); /* home time */
>+ memset(p, 0xFF, Eaddrlen); /* bssid */
>+ p += Eaddrlen;
>+ p = put2(p, 0); /* pad */
>+ if(chan != 0){
>+ p = put4(p, 1); /* num chans */
>+ p = put2(p, chan); /* chan spec */
>+ p = put2(p, 0); /* pad */
>+ assert(p == params + sizeof(params));
>+ }else{
>+ p = put4(p, 0); /* num chans */
>+ assert(p == params + sizeof(params) - 4);
>+ }
>+
>+ wlsetvar(ctl, "join", params, chan? sizeof params : sizeof params - 4);
>+ ctl->status = Connecting;
>+ switch(waitjoin(ctl)){
>+ case 0:
>+ ctl->status = Connected;
>+ break;
>+ case 3:
>+ ctl->status = Disconnected;
>+ error("wifi join: network not found");
>+ case 1:
>+ ctl->status = Disconnected;
>+ error("wifi join: failed");
>+ default:
>+ ctl->status = Disconnected;
>+ error("wifi join: error");
>+ }
>+}
>+
>+static void
>+wlscanstart(Ctlr *ctl)
>+{
>+ /* version[4] action[2] sync_id[2] ssidlen[4] ssid[32] bssid[6] bss_type[1]
>+ scan_type[1] nprobes[4] active_time[4] passive_time[4] home_time[4]
>+ nchans[2] nssids[2] chans[nchans][2] ssids[nssids][32] */
>+ /* hack - this is only correct on a little-endian cpu */
>+ static uchar params[4+2+2+4+32+6+1+1+4*4+2+2+14*2+32+4] = {
>+ 1,0,0,0,
>+ 1,0,
>+ 0x34,0x12,
>+ 0,0,0,0,
>+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>+ 0xff,0xff,0xff,0xff,0xff,0xff,
>+ 2,
>+ 0,
>+ 0xff,0xff,0xff,0xff,
>+ 0xff,0xff,0xff,0xff,
>+ 0xff,0xff,0xff,0xff,
>+ 0xff,0xff,0xff,0xff,
>+ 14,0,
>+ 1,0,
>+ 0x01,0x2b,0x02,0x2b,0x03,0x2b,0x04,0x2b,0x05,0x2e,0x06,0x2e,0x07,0x2e,
>+ 0x08,0x2b,0x09,0x2b,0x0a,0x2b,0x0b,0x2b,0x0c,0x2b,0x0d,0x2b,0x0e,0x2b,
>+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>+ };
>+
>+ wlcmdint(ctl, 49, 0); /* PASSIVE_SCAN */
>+ wlsetvar(ctl, "escan", params, sizeof params);
>+}
>+
>+static uchar*
>+gettlv(uchar *p, uchar *ep, int tag)
>+{
>+ int len;
>+
>+ while(p + 1 < ep){
>+ len = p[1];
>+ if(p + 2 + len > ep)
>+ return nil;
>+ if(p[0] == tag)
>+ return p;
>+ p += 2 + len;
>+ }
>+ return nil;
>+}
>+
>+static void
>+addscan(Block *bp, uchar *p, int len)
>+{
>+ char bssid[24];
>+ char *auth, *auth2;
>+ uchar *t, *et;
>+ int ielen;
>+ static uchar wpaie1[4] = { 0x00, 0x50, 0xf2, 0x01 };
>+
>+ snprint(bssid, sizeof bssid, ";bssid=%E", p + 8);
>+ if(strstr((char*)bp->rp, bssid) != nil)
>+ return;
>+ bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
>+ "ssid=%.*s%s;signal=%d;noise=%d;chan=%d",
>+ p[18], (char*)p+19, bssid,
>+ (short)get2(p+78), (signed char)p[80],
>+ get2(p+72) & 0xF);
>+ auth = auth2 = "";
>+ if(get2(p + 16) & 0x10)
>+ auth = ";wep";
>+ ielen = get4(p + 0x78);
>+ if(ielen > 0){
>+ t = p + get4(p + 0x74);
>+ et = t + ielen;
>+ if(et > p + len)
>+ return;
>+ if(gettlv(t, et, 0x30) != nil){
>+ auth = "";
>+ auth2 = ";wpa2";
>+ }
>+ while((t = gettlv(t, et, 0xdd)) != nil){
>+ if(t[1] > 4 && memcmp(t+2, wpaie1, 4) == 0){
>+ auth = ";wpa";
>+ break;
>+ }
>+ t += 2 + t[1];
>+ }
>+ }
>+ bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
>+ "%s%s\n", auth, auth2);
>+}
>+
>+
>+static void
>+wlscanresult(Ether *edev, uchar *p, int len)
>+{
>+ Ctlr *ctlr;
>+ Netfile **ep, *f, **fp;
>+ Block *bp;
>+ int nbss, i;
>+
>+ ctlr = edev->ctlr;
>+ if(get4(p) > len)
>+ return;
>+ /* TODO: more syntax checking */
>+ bp = ctlr->scanb;
>+ if(bp == nil)
>+ ctlr->scanb = bp = allocb(8192);
>+ nbss = get2(p+10);
>+ p += 12;
>+ len -= 12;
>+ if(0) dump("SCAN", p, len);
>+ if(nbss){
>+ addscan(bp, p, len);
>+ return;
>+ }
>+ i = edev->scan;
>+ ep = &edev->f[Ntypes];
>+ for(fp = edev->f; fp < ep && i > 0; fp++){
>+ f = *fp;
>+ if(f == nil || f->scan == 0)
>+ continue;
>+ if(i == 1)
>+ qpass(f->in, bp);
>+ else
>+ qpass(f->in, copyblock(bp, BLEN(bp)));
>+ i--;
>+ }
>+ if(i)
>+ freeb(bp);
>+ ctlr->scanb = nil;
>+}
>+
>+static void
>+lproc(void *a)
>+{
>+ Ether *edev;
>+ Ctlr *ctlr;
>+ int secs;
>+
>+ edev = a;
>+ ctlr = edev->ctlr;
>+ secs = 0;
>+ for(;;){
>+ tsleep(&up->sleep, return0, 0, 1000);
>+ if(ctlr->scansecs){
>+ if(secs == 0){
>+ if(waserror())
>+ ctlr->scansecs = 0;
>+ else{
>+ wlscanstart(ctlr);
>+ poperror();
>+ }
>+ secs = ctlr->scansecs;
>+ }
>+ --secs;
>+ }else
>+ secs = 0;
>+ }
>+}
>+
>+static void
>+wlinit(Ether *edev, Ctlr *ctlr)
>+{
>+ uchar ea[Eaddrlen];
>+ uchar eventmask[16];
>+ char version[128];
>+ char *p;
>+ static uchar keepalive[12] = {1, 0, 11, 0, 0xd8, 0xd6, 0, 0, 0, 0, 0, 0};
>+
>+ wlgetvar(ctlr, "cur_etheraddr", ea, Eaddrlen);
>+ memmove(edev->ea, ea, Eaddrlen);
>+ memmove(edev->addr, ea, Eaddrlen);
>+ print("ether4330: addr %E\n", edev->ea);
>+ wlsetint(ctlr, "assoc_listen", 10);
>+ if(ctlr->chipid == 43430 || ctlr->chipid == 0x4345)
>+ wlcmdint(ctlr, 0x56, 0); /* powersave off */
>+ else
>+ wlcmdint(ctlr, 0x56, 2); /* powersave FAST */
>+ wlsetint(ctlr, "bus:txglom", 0);
>+ wlsetint(ctlr, "bcn_timeout", 10);
>+ wlsetint(ctlr, "assoc_retry_max", 3);
>+ if(ctlr->chipid == 0x4330){
>+ wlsetint(ctlr, "btc_wire", 4);
>+ wlsetint(ctlr, "btc_mode", 1);
>+ wlsetvar(ctlr, "mkeep_alive", keepalive, 11);
>+ }
>+ memset(eventmask, 0xFF, sizeof eventmask);
>+#define ENABLE(n) eventmask[n/8] |= 1<<(n%8)
>+#define DISABLE(n) eventmask[n/8] &= ~(1<<(n%8))
>+ DISABLE(40); /* E_RADIO */
>+ DISABLE(44); /* E_PROBREQ_MSG */
>+ DISABLE(54); /* E_IF */
>+ DISABLE(71); /* E_PROBRESP_MSG */
>+ DISABLE(20); /* E_TXFAIL */
>+ DISABLE(124); /* ? */
>+ wlsetvar(ctlr, "event_msgs", eventmask, sizeof eventmask);
>+ wlcmdint(ctlr, 0xb9, 0x28); /* SET_SCAN_CHANNEL_TIME */
>+ wlcmdint(ctlr, 0xbb, 0x28); /* SET_SCAN_UNASSOC_TIME */
>+ wlcmdint(ctlr, 0x102, 0x82); /* SET_SCAN_PASSIVE_TIME */
>+ wlcmdint(ctlr, 2, 0); /* UP */
>+ memset(version, 0, sizeof version);
>+ wlgetvar(ctlr, "ver", version, sizeof version - 1);
>+ if((p = strchr(version, '\n')) != nil)
>+ *p = '\0';
>+ if(0) print("ether4330: %s\n", version);
>+ wlsetint(ctlr, "roam_off", 1);
>+ wlcmdint(ctlr, 0x14, 1); /* SET_INFRA 1 */
>+ wlcmdint(ctlr, 10, 0); /* SET_PROMISC */
>+ //wlcmdint(ctlr, 0x8e, 0); /* SET_BAND 0 */
>+ //wlsetint(ctlr, "wsec", 1);
>+ wlcmdint(ctlr, 2, 1); /* UP */
>+ ctlr->keys[0].len = WMinKeyLen;
>+ //wlwepkey(ctlr, 0);
>+}
>+
>+/*
>+ * Plan 9 driver interface
>+ */
>+
>+static long
>+etherbcmifstat(Ether* edev, void* a, long n, ulong offset)
>+{
>+ Ctlr *ctlr;
>+ char *p;
>+ int l;
>+ static char *cryptoname[4] = {
>+ [0] "off",
>+ [Wep] "wep",
>+ [Wpa] "wpa",
>+ [Wpa2] "wpa2",
>+ };
>+ /* these strings are known by aux/wpa */
>+ static char* connectstate[] = {
>+ [Disconnected] = "unassociated",
>+ [Connecting] = "connecting",
>+ [Connected] = "associated",
>+ };
>+
>+ ctlr = edev->ctlr;
>+ if(ctlr == nil)
>+ return 0;
>+ p = malloc(READSTR);
>+ l = 0;
>+
>+ l += snprint(p+l, READSTR-l, "channel: %d\n", ctlr->chanid);
>+ l += snprint(p+l, READSTR-l, "essid: %s\n", ctlr->essid);
>+ l += snprint(p+l, READSTR-l, "crypt: %s\n", cryptoname[ctlr->cryptotype]);
>+ l += snprint(p+l, READSTR-l, "oq: %d\n", qlen(edev->oq));
>+ l += snprint(p+l, READSTR-l, "txwin: %d\n", ctlr->txwindow);
>+ l += snprint(p+l, READSTR-l, "txseq: %d\n", ctlr->txseq);
>+ l += snprint(p+l, READSTR-l, "status: %s\n", connectstate[ctlr->status]);
>+ USED(l);
>+ n = readstr(offset, a, n, p);
>+ free(p);
>+ return n;
>+}
>+
>+static void
>+etherbcmtransmit(Ether *edev)
>+{
>+ Ctlr *ctlr;
>+
>+ ctlr = edev->ctlr;
>+ if(ctlr == nil)
>+ return;
>+ txstart(edev);
>+}
>+
>+static int
>+parsehex(char *buf, int buflen, char *a)
>+{
>+ int i, k, n;
>+
>+ k = 0;
>+ for(i = 0;k < buflen && *a; i++){
>+ if(*a >= '0' && *a <= '9')
>+ n = *a++ - '0';
>+ else if(*a >= 'a' && *a <= 'f')
>+ n = *a++ - 'a' + 10;
>+ else if(*a >= 'A' && *a <= 'F')
>+ n = *a++ - 'A' + 10;
>+ else
>+ break;
>+
>+ if(i & 1){
>+ buf[k] |= n;
>+ k++;
>+ }
>+ else
>+ buf[k] = n<<4;
>+ }
>+ if(i & 1)
>+ return -1;
>+ return k;
>+}
>+
>+static int
>+wepparsekey(WKey* key, char* a)
>+{
>+ int i, k, len, n;
>+ char buf[WMaxKeyLen];
>+
>+ len = strlen(a);
>+ if(len == WMinKeyLen || len == WMaxKeyLen){
>+ memset(key->dat, 0, sizeof(key->dat));
>+ memmove(key->dat, a, len);
>+ key->len = len;
>+
>+ return 0;
>+ }
>+ else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
>+ k = 0;
>+ for(i = 0; i < len; i++){
>+ if(*a >= '0' && *a <= '9')
>+ n = *a++ - '0';
>+ else if(*a >= 'a' && *a <= 'f')
>+ n = *a++ - 'a' + 10;
>+ else if(*a >= 'A' && *a <= 'F')
>+ n = *a++ - 'A' + 10;
>+ else
>+ return -1;
>+
>+ if(i & 1){
>+ buf[k] |= n;
>+ k++;
>+ }
>+ else
>+ buf[k] = n<<4;
>+ }
>+
>+ memset(key->dat, 0, sizeof(key->dat));
>+ memmove(key->dat, buf, k);
>+ key->len = k;
>+
>+ return 0;
>+ }
>+
>+ return -1;
>+}
>+
>+static int
>+wpaparsekey(WKey *key, uvlong *ivp, char *a)
>+{
>+ int len;
>+ char *e;
>+
>+ if(cistrncmp(a, "tkip:", 5) == 0 || cistrncmp(a, "ccmp:", 5) == 0)
>+ a += 5;
>+ else
>+ return 1;
>+ len = parsehex(key->dat, sizeof(key->dat), a);
>+ if(len <= 0)
>+ return 1;
>+ key->len = len;
>+ a += 2*len;
>+ if(*a++ != '@')
>+ return 1;
>+ *ivp = strtoull(a, &e, 16);
>+ if(e == a)
>+ return -1;
>+ return 0;
>+}
>+
>+static void
>+setauth(Ctlr *ctlr, Cmdbuf *cb, char *a)
>+{
>+ uchar wpaie[32];
>+ int i;
>+
>+ i = parsehex((char*)wpaie, sizeof wpaie, a);
>+ if(i < 2 || i != wpaie[1] + 2)
>+ cmderror(cb, "bad wpa ie syntax");
>+ if(wpaie[0] == 0xdd)
>+ ctlr->cryptotype = Wpa;
>+ else if(wpaie[0] == 0x30)
>+ ctlr->cryptotype = Wpa2;
>+ else
>+ cmderror(cb, "bad wpa ie");
>+ wlsetvar(ctlr, "wpaie", wpaie, i);
>+ if(ctlr->cryptotype == Wpa){
>+ wlsetint(ctlr, "wpa_auth", 4|2); /* auth_psk | auth_unspecified */
>+ wlsetint(ctlr, "auth", 0);
>+ wlsetint(ctlr, "wsec", 2); /* tkip */
>+ wlsetint(ctlr, "wpa_auth", 4); /* auth_psk */
>+ }else{
>+ wlsetint(ctlr, "wpa_auth", 0x80|0x40); /* auth_psk | auth_unspecified */
>+ wlsetint(ctlr, "auth", 0);
>+ wlsetint(ctlr, "wsec", 4); /* aes */
>+ wlsetint(ctlr, "wpa_auth", 0x80); /* auth_psk */
>+ }
>+}
>+
>+static int
>+setcrypt(Ctlr *ctlr, Cmdbuf*, char *a)
>+{
>+ if(cistrcmp(a, "wep") == 0 || cistrcmp(a, "on") == 0)
>+ ctlr->cryptotype = Wep;
>+ else if(cistrcmp(a, "off") == 0 || cistrcmp(a, "none") == 0)
>+ ctlr->cryptotype = 0;
>+ else
>+ return 0;
>+ wlsetint(ctlr, "auth", ctlr->cryptotype);
>+ return 1;
>+}
>+
>+static long
>+etherbcmctl(Ether* edev, void* buf, long n)
>+{
>+ Ctlr *ctlr;
>+ Cmdbuf *cb;
>+ Cmdtab *ct;
>+ uchar ea[Eaddrlen];
>+ uvlong iv;
>+ int i;
>+
>+ if((ctlr = edev->ctlr) == nil)
>+ error(Enonexist);
>+ USED(ctlr);
>+
>+ cb = parsecmd(buf, n);
>+ if(waserror()){
>+ free(cb);
>+ nexterror();
>+ }
>+ ct = lookupcmd(cb, cmds, nelem(cmds));
>+ switch(ct->index){
>+ case CMauth:
>+ setauth(ctlr, cb, cb->f[1]);
>+ if(ctlr->essid[0])
>+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
>+ break;
>+ case CMchannel:
>+ if((i = atoi(cb->f[1])) < 0 || i > 16)
>+ cmderror(cb, "bad channel number");
>+ //wlcmdint(ctlr, 30, i); /* SET_CHANNEL */
>+ ctlr->chanid = i;
>+ break;
>+ case CMcrypt:
>+ if(setcrypt(ctlr, cb, cb->f[1])){
>+ if(ctlr->essid[0])
>+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
>+ }else
>+ cmderror(cb, "bad crypt type");
>+ break;
>+ case CMessid:
>+ if(cistrcmp(cb->f[1], "default") == 0)
>+ memset(ctlr->essid, 0, sizeof(ctlr->essid));
>+ else{
>+ strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid) - 1);
>+ ctlr->essid[sizeof(ctlr->essid) - 1] = '\0';
>+ }
>+ if(!waserror()){
>+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
>+ poperror();
>+ }
>+ break;
>+ case CMjoin: /* join essid channel wep|on|off|wpakey */
>+ if(strcmp(cb->f[1], "") != 0){ /* empty string for no change */
>+ if(cistrcmp(cb->f[1], "default") != 0){
>+ strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid)-1);
>+ ctlr->essid[sizeof(ctlr->essid)-1] = 0;
>+ }else
>+ memset(ctlr->essid, 0, sizeof(ctlr->essid));
>+ }else if(ctlr->essid[0] == 0)
>+ cmderror(cb, "essid not set");
>+ if((i = atoi(cb->f[2])) >= 0 && i <= 16)
>+ ctlr->chanid = i;
>+ else
>+ cmderror(cb, "bad channel number");
>+ if(!setcrypt(ctlr, cb, cb->f[3]))
>+ setauth(ctlr, cb, cb->f[3]);
>+ if(ctlr->essid[0])
>+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
>+ break;
>+ case CMkey1:
>+ case CMkey2:
>+ case CMkey3:
>+ case CMkey4:
>+ i = ct->index - CMkey1;
>+ if(wepparsekey(&ctlr->keys[i], cb->f[1]))
>+ cmderror(cb, "bad WEP key syntax");
>+ wlsetint(ctlr, "wsec", 1); /* wep enabled */
>+ wlwepkey(ctlr, i);
>+ break;
>+ case CMrxkey:
>+ case CMrxkey0:
>+ case CMrxkey1:
>+ case CMrxkey2:
>+ case CMrxkey3:
>+ case CMtxkey:
>+ if(parseether(ea, cb->f[1]) < 0)
>+ cmderror(cb, "bad ether addr");
>+ if(wpaparsekey(&ctlr->keys[0], &iv, cb->f[2]))
>+ cmderror(cb, "bad wpa key");
>+ wlwpakey(ctlr, ct->index, iv, ea);
>+ break;
>+ case CMdebug:
>+ iodebug = atoi(cb->f[1]);
>+ break;
>+ }
>+ poperror();
>+ free(cb);
>+ return n;
>+}
>+
>+static void
>+etherbcmscan(void *a, uint secs)
>+{
>+ Ether* edev;
>+ Ctlr* ctlr;
>+
>+ edev = a;
>+ ctlr = edev->ctlr;
>+ ctlr->scansecs = secs;
>+}
>+
>+static void
>+etherbcmattach(Ether* edev)
>+{
>+ Ctlr *ctlr;
>+
>+ ctlr = edev->ctlr;
>+ qlock(&ctlr->alock);
>+ if(waserror()){
>+ //print("ether4330: attach failed: %s\n", up->errstr);
>+ qunlock(&ctlr->alock);
>+ nexterror();
>+ }
>+ if(ctlr->edev == nil){
>+ if(ctlr->chipid == 0){
>+ sdioinit();
>+ sbinit(ctlr);
>+ }
>+ fwload(ctlr);
>+ sbenable(ctlr);
>+ kproc("wifireader", rproc, edev);
>+ kproc("wifitimer", lproc, edev);
>+ if(ctlr->regufile)
>+ reguload(ctlr, ctlr->regufile);
>+ wlinit(edev, ctlr);
>+ ctlr->edev = edev;
>+ }
>+ qunlock(&ctlr->alock);
>+ poperror();
>+}
>+
>+static void
>+etherbcmshutdown(Ether*)
>+{
>+ sdioreset();
>+}
>+
>+
>+static int
>+etherbcmpnp(Ether* edev)
>+{
>+ static Ctlr *ctlr;
>+
>+ if(ctlr != nil)
>+ return -1;
>+
>+ ctlr = malloc(sizeof(Ctlr));
>+ ctlr->chanid = Wifichan;
>+ edev->ctlr = ctlr;
>+ edev->attach = etherbcmattach;
>+ edev->transmit = etherbcmtransmit;
>+ edev->ifstat = etherbcmifstat;
>+ edev->ctl = etherbcmctl;
>+ edev->scanbs = etherbcmscan;
>+ edev->shutdown = etherbcmshutdown;
>+ edev->arg = edev;
>+
>+ return 0;
>+}
>+
>+void
>+ether4330link(void)
>+{
>+ addethercard("4330", etherbcmpnp);
>+}
>--- a/sys/src/9/bcm64/pi4 Sun Oct 22 09:21:23 2023
>+++ b/sys/src/9/bcm64/pi4 Sun Oct 22 10:53:02 2023
>@@ -32,6 +32,7 @@
> archbcm4 pci
> usbxhcipci pci usbxhci archbcm4
> ethergenet ethermii
>+ ether4330 emmc
> ethermedium
> loopbackmedium
> netdevmedium
>--- a/sys/src/9/port/sd.h Sun Oct 22 09:21:23 2023
>+++ b/sys/src/9/port/sd.h Sun Oct 22 10:53:02 2023
>@@ -164,6 +164,31 @@
> char *name;
> };
>
>+/* Commands */
>+extern SDiocmd GO_IDLE_STATE;
>+extern SDiocmd SEND_OP_COND;
>+extern SDiocmd ALL_SEND_CID;
>+extern SDiocmd SET_RELATIVE_ADDR;
>+extern SDiocmd SEND_RELATIVE_ADDR;
>+extern SDiocmd SWITCH;
>+extern SDiocmd SWITCH_FUNC;
>+extern SDiocmd SELECT_CARD;
>+extern SDiocmd SEND_EXT_CSD;
>+extern SDiocmd SD_SEND_IF_COND;
>+extern SDiocmd SEND_CSD;
>+extern SDiocmd STOP_TRANSMISSION;
>+extern SDiocmd SEND_STATUS;
>+extern SDiocmd SET_BLOCKLEN;
>+extern SDiocmd READ_SINGLE_BLOCK;
>+extern SDiocmd READ_MULTIPLE_BLOCK;
>+extern SDiocmd WRITE_SINGLE_BLOCK;
>+extern SDiocmd WRITE_MULTIPLE_BLOCK;
>+
>+/* prefix for following app-specific commands */
>+extern SDiocmd APP_CMD;
>+extern SDiocmd SD_SET_BUS_WIDTH;
>+extern SDiocmd SD_SEND_OP_COND;
>+
> struct SDio {
> char *name;
> int (*init)(SDio*);
>
>
let me rephrase: is this known to actually work, and which hardware does it actually work with. asking for a fqa.
sl
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [9front] Re: Fwd: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org)
2023-10-22 18:45 ` [9front] " Stanley Lieber
@ 2023-10-22 19:01 ` adventures in9
0 siblings, 0 replies; 3+ messages in thread
From: adventures in9 @ 2023-10-22 19:01 UTC (permalink / raw)
To: 9front
kws says it works on the rpi4 right now.
I'm testing pi3b, pi3b+ and piw0,
but I need to track down which firmware to use first
On Sun, Oct 22, 2023 at 11:47 AM Stanley Lieber <sl@stanleylieber.com> wrote:
>
> On October 22, 2023 2:43:42 PM EDT, Stanley Lieber <sl@stanleylieber.com> wrote:
> >this is for rpi, yes? is there a list of specific hardware this is known to work with?
> >
> >sl
> >
> >
> >-------- Original Message --------
> >From: commits@git.9front.org
> >Sent: October 22, 2023 10:53:02 AM EDT
> >To: 9front-commits@9front.org
> >Subject: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org)
> >
> >
> >Thanks Richard Miller
> >---
> >diff f2cfee358f329519e913a20142d96b1e0029633c fb96a050f8a9d5f23da3557ba89251ffb73889bf
> >--- a/sys/src/9/bcm/emmc.c Sun Oct 22 09:21:23 2023
> >+++ b/sys/src/9/bcm/emmc.c Sun Oct 22 10:53:02 2023
> >@@ -129,6 +129,7 @@
> >
> > typedef struct Ctlr Ctlr;
> > struct Ctlr {
> >+ Rendez cardr;
> > Rendez r;
> > int fastclock;
> > ulong extclk;
> >@@ -266,6 +267,23 @@
> > intrenable(IRQmmc, emmcinterrupt, nil, BUSUNKNOWN, io->name);
> > }
> >
> >+int
> >+sdiocardintr(int wait)
> >+{
> >+ u32int *r = (u32int*)EMMCREGS;
> >+ int i;
> >+
> >+ WR(Interrupt, Cardintr);
> >+ while(((i = r[Interrupt]) & Cardintr) == 0){
> >+ if(!wait)
> >+ return 0;
> >+ WR(Irpten, r[Irpten] | Cardintr);
> >+ sleep(&emmc.cardr, cardintready, 0);
> >+ }
> >+ WR(Interrupt, Cardintr);
> >+ return i;
> >+}
> >+
> > static int
> > emmccmd(SDio*, SDiocmd *cmd, u32int arg, u32int *resp)
> > {
> >@@ -434,21 +452,25 @@
> > i = r[Interrupt];
> > if(i&(Datadone|Err))
> > wakeup(&emmc.r);
> >+ if(i&Cardintr)
> >+ wakeup(&emmc.cardr);
> > WR(Irpten, r[Irpten] & ~i);
> > }
> >
> >+
> >+SDio sdio = {
> >+ "emmc",
> >+ emmcinit,
> >+ emmcenable,
> >+ emmcinquiry,
> >+ emmccmd,
> >+ emmciosetup,
> >+ emmcio,
> >+ emmcbus,
> >+};
> >+
> > void
> > emmclink(void)
> > {
> >- static SDio io = {
> >- "emmc",
> >- emmcinit,
> >- emmcenable,
> >- emmcinquiry,
> >- emmccmd,
> >- emmciosetup,
> >- emmcio,
> >- emmcbus,
> >- };
> >- addmmcio(&io);
> >+ addmmcio(&sdio);
> > }
> >--- /dev/null Sun Aug 27 23:09:05 2023
> >+++ b/sys/src/9/bcm/ether4330.c Sun Oct 22 10:53:02 2023
> >@@ -0,0 +1,2411 @@
> >+/*
> >+ * Broadcom bcm4330 wifi (sdio interface)
> >+ */
> >+
> >+#include "u.h"
> >+#include "../port/lib.h"
> >+#include "mem.h"
> >+#include "dat.h"
> >+#include "fns.h"
> >+#include "io.h"
> >+#include "../port/error.h"
> >+#include "../port/netif.h"
> >+#include "../port/sd.h"
> >+#include "../port/etherif.h"
> >+
> >+#define CACHELINESZ 64 /* temp */
> >+
> >+extern SDio sdio;
> >+extern int sdiocardintr(int);
> >+
> >+SDiocmd IO_SEND_OP_COND = { 5, 3, 0, 0, "IO_SEND_OP_COND" };
> >+SDiocmd IO_RW_DIRECT = { 52, 1, 0, 0, "IO_RW_DIRECT" };
> >+
> >+enum{
> >+ SDIODEBUG = 0,
> >+ SBDEBUG = 0,
> >+ EVENTDEBUG = 0,
> >+ VARDEBUG = 0,
> >+ FWDEBUG = 0,
> >+
> >+ Corescansz = 512,
> >+ Uploadsz = 2048,
> >+ Sdpcmsz = 12, /* sizeof(Sdpcmsz) */
> >+ Cmdsz = 16, /* sizeof(Cmd) */
> >+
> >+ SDfreq = 25*Mhz, /* standard SD frequency */
> >+ SDfreqhs = 50*Mhz, /* high speed frequency */
> >+
> >+ Wifichan = 0, /* default channel */
> >+ Firmwarecmp = 1,
> >+
> >+ ARMcm3 = 0x82A,
> >+ ARM7tdmi = 0x825,
> >+ ARMcr4 = 0x83E,
> >+
> >+ Fn0 = 0,
> >+ Fn1 = 1,
> >+ Fn2 = 2,
> >+ Fbr1 = 0x100,
> >+ Fbr2 = 0x200,
> >+
> >+ /* CCCR */
> >+ Ioenable = 0x02,
> >+ Ioready = 0x03,
> >+ Intenable = 0x04,
> >+ Intpend = 0x05,
> >+ Ioabort = 0x06,
> >+ Busifc = 0x07,
> >+ Capability = 0x08,
> >+ Blksize = 0x10,
> >+ Highspeed = 0x13,
> >+
> >+ /* SELECT_CARD args */
> >+ Rcashift = 16,
> >+
> >+ /* SEND_OP_COND args */
> >+ Hcs = 1<<30, /* host supports SDHC & SDXC */
> >+ V3_3 = 3<<20, /* 3.2-3.4 volts */
> >+ V2_8 = 3<<15, /* 2.7-2.9 volts */
> >+ V2_0 = 1<<8, /* 2.0-2.1 volts */
> >+ S18R = 1<<24, /* switch to 1.8V request */
> >+
> >+ /* Sonics Silicon Backplane (access to cores on chip) */
> >+ Sbwsize = 0x8000,
> >+ Sb32bit = 0x8000,
> >+ Sbaddr = 0x1000a,
> >+ Enumbase = 0x18000000,
> >+ Framectl= 0x1000d,
> >+ Rfhalt = 0x01,
> >+ Wfhalt = 0x02,
> >+ Clkcsr = 0x1000e,
> >+ ForceALP = 0x01, /* active low-power clock */
> >+ ForceHT = 0x02, /* high throughput clock */
> >+ ForceILP = 0x04, /* idle low-power clock */
> >+ ReqALP = 0x08,
> >+ ReqHT = 0x10,
> >+ Nohwreq = 0x20,
> >+ ALPavail = 0x40,
> >+ HTavail = 0x80,
> >+ Pullups = 0x1000f,
> >+ Wfrmcnt = 0x10019,
> >+ Rfrmcnt = 0x1001b,
> >+
> >+ /* core control regs */
> >+ Ioctrl = 0x408,
> >+ Resetctrl = 0x800,
> >+
> >+ /* socram regs */
> >+ Coreinfo = 0x00,
> >+ Bankidx = 0x10,
> >+ Bankinfo = 0x40,
> >+ Bankpda = 0x44,
> >+
> >+ /* armcr4 regs */
> >+ Cr4Cap = 0x04,
> >+ Cr4Bankidx = 0x40,
> >+ Cr4Bankinfo = 0x44,
> >+ Cr4Cpuhalt = 0x20,
> >+
> >+ /* chipcommon regs */
> >+ Gpiopullup = 0x58,
> >+ Gpiopulldown = 0x5c,
> >+ Chipctladdr = 0x650,
> >+ Chipctldata = 0x654,
> >+
> >+ /* sdio core regs */
> >+ Intstatus = 0x20,
> >+ Fcstate = 1<<4,
> >+ Fcchange = 1<<5,
> >+ FrameInt = 1<<6,
> >+ MailboxInt = 1<<7,
> >+ Intmask = 0x24,
> >+ Sbmbox = 0x40,
> >+ Sbmboxdata = 0x48,
> >+ Hostmboxdata= 0x4c,
> >+ Fwready = 0x80,
> >+
> >+ /* wifi control commands */
> >+ GetVar = 262,
> >+ SetVar = 263,
> >+
> >+ /* status */
> >+ Disconnected= 0,
> >+ Connecting,
> >+ Connected,
> >+};
> >+
> >+typedef struct Ctlr Ctlr;
> >+
> >+enum{
> >+ Wpa = 1,
> >+ Wep = 2,
> >+ Wpa2 = 3,
> >+ WNameLen = 32,
> >+ WNKeys = 4,
> >+ WKeyLen = 32,
> >+ WMinKeyLen = 5,
> >+ WMaxKeyLen = 13,
> >+};
> >+
> >+typedef struct WKey WKey;
> >+struct WKey
> >+{
> >+ ushort len;
> >+ char dat[WKeyLen];
> >+};
> >+
> >+struct Ctlr {
> >+ Ether* edev;
> >+ QLock cmdlock;
> >+ QLock pktlock;
> >+ QLock tlock;
> >+ QLock alock;
> >+ Lock txwinlock;
> >+ Rendez cmdr;
> >+ Rendez joinr;
> >+ int joinstatus;
> >+ int cryptotype;
> >+ int chanid;
> >+ char essid[WNameLen + 1];
> >+ WKey keys[WNKeys];
> >+ Block *rsp;
> >+ Block *scanb;
> >+ int scansecs;
> >+ int status;
> >+ int chipid;
> >+ int chiprev;
> >+ int armcore;
> >+ char *regufile;
> >+ union {
> >+ u32int i;
> >+ uchar c[4];
> >+ } resetvec;
> >+ ulong chipcommon;
> >+ ulong armctl;
> >+ ulong armregs;
> >+ ulong d11ctl;
> >+ ulong socramregs;
> >+ ulong socramctl;
> >+ ulong sdregs;
> >+ int sdiorev;
> >+ int socramrev;
> >+ ulong socramsize;
> >+ ulong rambase;
> >+ short reqid;
> >+ uchar fcmask;
> >+ uchar txwindow;
> >+ uchar txseq;
> >+ uchar rxseq;
> >+};
> >+
> >+enum{
> >+ CMauth,
> >+ CMchannel,
> >+ CMcrypt,
> >+ CMessid,
> >+ CMkey1,
> >+ CMkey2,
> >+ CMkey3,
> >+ CMkey4,
> >+ CMrxkey,
> >+ CMrxkey0,
> >+ CMrxkey1,
> >+ CMrxkey2,
> >+ CMrxkey3,
> >+ CMtxkey,
> >+ CMdebug,
> >+ CMjoin,
> >+};
> >+
> >+static Cmdtab cmds[] = {
> >+ {CMauth, "auth", 2},
> >+ {CMchannel, "channel", 2},
> >+ {CMcrypt, "crypt", 2},
> >+ {CMessid, "essid", 2},
> >+ {CMkey1, "key1", 2},
> >+ {CMkey2, "key1", 2},
> >+ {CMkey3, "key1", 2},
> >+ {CMkey4, "key1", 2},
> >+ {CMrxkey, "rxkey", 3},
> >+ {CMrxkey0, "rxkey0", 3},
> >+ {CMrxkey1, "rxkey1", 3},
> >+ {CMrxkey2, "rxkey2", 3},
> >+ {CMrxkey3, "rxkey3", 3},
> >+ {CMtxkey, "txkey", 3},
> >+ {CMdebug, "debug", 2},
> >+ {CMjoin, "join", 5},
> >+};
> >+
> >+typedef struct Sdpcm Sdpcm;
> >+typedef struct Cmd Cmd;
> >+struct Sdpcm {
> >+ uchar len[2];
> >+ uchar lenck[2];
> >+ uchar seq;
> >+ uchar chanflg;
> >+ uchar nextlen;
> >+ uchar doffset;
> >+ uchar fcmask;
> >+ uchar window;
> >+ uchar version;
> >+ uchar pad;
> >+};
> >+
> >+struct Cmd {
> >+ uchar cmd[4];
> >+ uchar len[4];
> >+ uchar flags[2];
> >+ uchar id[2];
> >+ uchar status[4];
> >+};
> >+
> >+static char config40181[] = "bcmdhd.cal.40181";
> >+static char config40183[] = "bcmdhd.cal.40183.26MHz";
> >+
> >+struct {
> >+ int chipid;
> >+ int chiprev;
> >+ char *fwfile;
> >+ char *cfgfile;
> >+ char *regufile;
> >+} firmware[] = {
> >+ { 0x4330, 3, "fw_bcm40183b1.bin", config40183, 0 },
> >+ { 0x4330, 4, "fw_bcm40183b2.bin", config40183, 0 },
> >+ { 43362, 0, "fw_bcm40181a0.bin", config40181, 0 },
> >+ { 43362, 1, "fw_bcm40181a2.bin", config40181, 0 },
> >+ { 43430, 1, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt", 0 },
> >+ { 43430, 2, "brcmfmac43436-sdio.bin", "brcmfmac43436-sdio.txt", "brcmfmac43436-sdio.clm_blob" },
> >+ { 0x4345, 6, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt", "brcmfmac43455-sdio.clm_blob" },
> >+ { 0x4345, 9, "brcmfmac43456-sdio.bin", "brcmfmac43456-sdio.txt", "brcmfmac43456-sdio.clm_blob" },
> >+};
> >+
> >+static QLock sdiolock;
> >+static int iodebug;
> >+
> >+static void etherbcmintr(void *);
> >+static void bcmevent(Ctlr*, uchar*, int);
> >+static void wlscanresult(Ether*, uchar*, int);
> >+static void wlsetvar(Ctlr*, char*, void*, int);
> >+
> >+static uchar*
> >+put2(uchar *p, short v)
> >+{
> >+ p[0] = v;
> >+ p[1] = v >> 8;
> >+ return p + 2;
> >+}
> >+
> >+static uchar*
> >+put4(uchar *p, long v)
> >+{
> >+ p[0] = v;
> >+ p[1] = v >> 8;
> >+ p[2] = v >> 16;
> >+ p[3] = v >> 24;
> >+ return p + 4;
> >+}
> >+
> >+static ushort
> >+get2(uchar *p)
> >+{
> >+ return p[0] | p[1]<<8;
> >+}
> >+
> >+static ulong
> >+get4(uchar *p)
> >+{
> >+ return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
> >+}
> >+
> >+static void
> >+dump(char *s, void *a, int n)
> >+{
> >+ int i;
> >+ uchar *p;
> >+
> >+ p = a;
> >+ print("%s:", s);
> >+ for(i = 0; i < n; i++)
> >+ print("%c%2.2x", i&15? ' ' : '\n', *p++);
> >+ print("\n");
> >+}
> >+
> >+/*
> >+ * SDIO communication with dongle
> >+ */
> >+static ulong
> >+sdiocmd_locked(SDiocmd *cmd, ulong arg)
> >+{
> >+ u32int resp[4];
> >+
> >+ sdio.cmd(&sdio, cmd, arg, resp);
> >+ return resp[0];
> >+}
> >+
> >+static ulong
> >+sdiocmd(SDiocmd *cmd, ulong arg)
> >+{
> >+ ulong r;
> >+
> >+ qlock(&sdiolock);
> >+ if(waserror()){
> >+ if(SDIODEBUG) print("sdiocmd error: cmd %s arg %lux\n", cmd->name, arg);
> >+ qunlock(&sdiolock);
> >+ nexterror();
> >+ }
> >+ r = sdiocmd_locked(cmd, arg);
> >+ qunlock(&sdiolock);
> >+ poperror();
> >+ return r;
> >+
> >+}
> >+
> >+static ulong
> >+trysdiocmd(SDiocmd *cmd, ulong arg)
> >+{
> >+ ulong r;
> >+
> >+ if(waserror())
> >+ return 0;
> >+ r = sdiocmd(cmd, arg);
> >+ poperror();
> >+ return r;
> >+}
> >+
> >+static int
> >+sdiord(int fn, int addr)
> >+{
> >+ int r;
> >+
> >+ r = sdiocmd(&IO_RW_DIRECT, (0<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9));
> >+ if(r & 0xCF00){
> >+ print("ether4330: sdiord(%x, %x) fail: %2.2ux %2.2ux\n", fn, addr, (r>>8)&0xFF, r&0xFF);
> >+ error(Eio);
> >+ }
> >+ return r & 0xFF;
> >+}
> >+
> >+static void
> >+sdiowr(int fn, int addr, int data)
> >+{
> >+ int r;
> >+ int retry;
> >+ ulong arg;
> >+
> >+ r = 0;
> >+ for(retry = 0; retry < 10; retry++){
> >+ arg = (1<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)|(data&0xFF);
> >+ if((arg & ~0xFF) == (1<<31|0<<28|7<<9)){
> >+ switch(arg&3){
> >+ case 0:
> >+ sdio.bus(&sdio, 1, 0);
> >+ break;
> >+ case 2:
> >+ sdio.bus(&sdio, 4, 0);
> >+ break;
> >+ }
> >+ }
> >+ r = sdiocmd(&IO_RW_DIRECT, arg);
> >+ if((r & 0xCF00) == 0)
> >+ return;
> >+ }
> >+ print("ether4330: sdiowr(%x, %x, %x) fail: %2.2ux %2.2ux\n", fn, addr, data, (r>>8)&0xFF, r&0xFF);
> >+ error(Eio);
> >+}
> >+
> >+static void
> >+sdiorwext(int fn, int write, void *a, int len, int addr, int incr)
> >+{
> >+ int bsize, blk, bcount, m;
> >+ SDiocmd cmd = { 53, 1, 0, 0, "IO_RW_EXTENDED" };
> >+
> >+ bsize = fn == Fn2? 512 : 64;
> >+ while(len > 0){
> >+ if(len >= 511*bsize){
> >+ blk = 1;
> >+ bcount = 511;
> >+ m = bcount*bsize;
> >+ }else if(len > bsize){
> >+ blk = 1;
> >+ bcount = len/bsize;
> >+ m = bcount*bsize;
> >+ }else{
> >+ blk = 0;
> >+ bcount = len;
> >+ m = bcount;
> >+ }
> >+ qlock(&sdiolock);
> >+ if(waserror()){
> >+ print("ether4330: sdiorwext fail: %s\n", up->errstr);
> >+ qunlock(&sdiolock);
> >+ nexterror();
> >+ }
> >+ cmd.data = write? 2 : 1; /* Host2card : Card2host */
> >+ if(blk){
> >+ cmd.data += 2; /* Multiblock | Blkcnten */
> >+ sdio.iosetup(&sdio, write, a, bsize, bcount);
> >+ }else
> >+ sdio.iosetup(&sdio, write, a, bcount, 1);
> >+ sdiocmd_locked(&cmd, write<<31 | (fn&7)<<28 | blk<<27 | incr<<26 | (addr&0x1FFFF)<<9 | (bcount&0x1FF));
> >+ sdio.io(&sdio, write, a, m);
> >+ qunlock(&sdiolock);
> >+ poperror();
> >+ len -= m;
> >+ a = (char*)a + m;
> >+ if(incr)
> >+ addr += m;
> >+ }
> >+}
> >+
> >+static void
> >+sdioset(int fn, int addr, int bits)
> >+{
> >+ sdiowr(fn, addr, sdiord(fn, addr) | bits);
> >+}
> >+
> >+static void
> >+sdioinit(void)
> >+{
> >+ ulong ocr, rca;
> >+ int i;
> >+
> >+ /* disconnect emmc from SD card (connect sdhost instead) */
> >+ for(i = 48; i <= 53; i++)
> >+ gpiosel(i, Alt0);
> >+ /* connect emmc to wifi */
> >+ for(i = 34; i <= 39; i++){
> >+ gpiosel(i, Alt3);
> >+ if(i == 34)
> >+ gpiopulloff(i);
> >+ else
> >+ gpiopullup(i);
> >+ }
> >+ sdio.init(&sdio);
> >+ sdio.enable(&sdio);
> >+ sdiocmd(&GO_IDLE_STATE, 0);
> >+ ocr = trysdiocmd(&IO_SEND_OP_COND, 0);
> >+ i = 0;
> >+ while((ocr & (1<<31)) == 0){
> >+ if(++i > 5){
> >+ print("ether4330: no response to sdio access: ocr = %lux\n", ocr);
> >+ error(Eio);
> >+ }
> >+ ocr = trysdiocmd(&IO_SEND_OP_COND, V3_3);
> >+ tsleep(&up->sleep, return0, nil, 100);
> >+ }
> >+ rca = sdiocmd(&SEND_RELATIVE_ADDR, 0) >> Rcashift;
> >+ sdiocmd(&SELECT_CARD, rca << Rcashift);
> >+ sdio.bus(&sdio, 0, SDfreq);
> >+ sdioset(Fn0, Highspeed, 2);
> >+ sdioset(Fn0, Busifc, 2); /* bus width 4 */
> >+ sdiowr(Fn0, Fbr1+Blksize, 64);
> >+ sdiowr(Fn0, Fbr1+Blksize+1, 64>>8);
> >+ sdiowr(Fn0, Fbr2+Blksize, 512);
> >+ sdiowr(Fn0, Fbr2+Blksize+1, 512>>8);
> >+ sdioset(Fn0, Ioenable, 1<<Fn1);
> >+ sdiowr(Fn0, Intenable, 0);
> >+ for(i = 0; !(sdiord(Fn0, Ioready) & 1<<Fn1); i++){
> >+ if(i == 10){
> >+ print("ether4330: can't enable SDIO function\n");
> >+ error(Eio);
> >+ }
> >+ tsleep(&up->sleep, return0, nil, 100);
> >+ }
> >+}
> >+
> >+static void
> >+sdioreset(void)
> >+{
> >+ sdiowr(Fn0, Ioabort, 1<<3); /* reset */
> >+}
> >+
> >+static void
> >+sdioabort(int fn)
> >+{
> >+ sdiowr(Fn0, Ioabort, fn);
> >+}
> >+
> >+/*
> >+ * Chip register and memory access via SDIO
> >+ */
> >+
> >+static void
> >+cfgw(ulong off, int val)
> >+{
> >+ sdiowr(Fn1, off, val);
> >+}
> >+
> >+static int
> >+cfgr(ulong off)
> >+{
> >+ return sdiord(Fn1, off);
> >+}
> >+
> >+static ulong
> >+cfgreadl(int fn, ulong off)
> >+{
> >+ uchar cbuf[2*CACHELINESZ];
> >+ uchar *p;
> >+
> >+ p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ);
> >+ memset(p, 0, 4);
> >+ sdiorwext(fn, 0, p, 4, off|Sb32bit, 1);
> >+ if(SDIODEBUG) print("cfgreadl %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]);
> >+ return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
> >+}
> >+
> >+static void
> >+cfgwritel(int fn, ulong off, u32int data)
> >+{
> >+ uchar cbuf[2*CACHELINESZ];
> >+ uchar *p;
> >+ int retry;
> >+
> >+ p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ);
> >+ put4(p, data);
> >+ if(SDIODEBUG) print("cfgwritel %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]);
> >+ retry = 0;
> >+ while(waserror()){
> >+ print("ether4330: cfgwritel retry %lux %ux\n", off, data);
> >+ sdioabort(fn);
> >+ if(++retry == 3)
> >+ nexterror();
> >+ }
> >+ sdiorwext(fn, 1, p, 4, off|Sb32bit, 1);
> >+ poperror();
> >+}
> >+
> >+static void
> >+sbwindow(ulong addr)
> >+{
> >+ addr &= ~(Sbwsize-1);
> >+ cfgw(Sbaddr, addr>>8);
> >+ cfgw(Sbaddr+1, addr>>16);
> >+ cfgw(Sbaddr+2, addr>>24);
> >+}
> >+
> >+static void
> >+sbrw(int fn, int write, uchar *buf, int len, ulong off)
> >+{
> >+ int n;
> >+ USED(fn);
> >+
> >+ if(waserror()){
> >+ print("ether4330: sbrw err off %lux len %ud\n", off, len);
> >+ nexterror();
> >+ }
> >+ if(write){
> >+ if(len >= 4){
> >+ n = len;
> >+ n &= ~3;
> >+ sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1);
> >+ off += n;
> >+ buf += n;
> >+ len -= n;
> >+ }
> >+ while(len > 0){
> >+ sdiowr(Fn1, off|Sb32bit, *buf);
> >+ off++;
> >+ buf++;
> >+ len--;
> >+ }
> >+ }else{
> >+ if(len >= 4){
> >+ n = len;
> >+ n &= ~3;
> >+ sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1);
> >+ off += n;
> >+ buf += n;
> >+ len -= n;
> >+ }
> >+ while(len > 0){
> >+ *buf = sdiord(Fn1, off|Sb32bit);
> >+ off++;
> >+ buf++;
> >+ len--;
> >+ }
> >+ }
> >+ poperror();
> >+}
> >+
> >+static void
> >+sbmem(int write, uchar *buf, int len, ulong off)
> >+{
> >+ ulong n;
> >+
> >+ n = ROUNDUP(off, Sbwsize) - off;
> >+ if(n == 0)
> >+ n = Sbwsize;
> >+ while(len > 0){
> >+ if(n > len)
> >+ n = len;
> >+ sbwindow(off);
> >+ sbrw(Fn1, write, buf, n, off & (Sbwsize-1));
> >+ off += n;
> >+ buf += n;
> >+ len -= n;
> >+ n = Sbwsize;
> >+ }
> >+}
> >+
> >+static void
> >+packetrw(int write, uchar *buf, int len)
> >+{
> >+ int n;
> >+ int retry;
> >+
> >+ n = 2048;
> >+ while(len > 0){
> >+ if(n > len)
> >+ n = ROUND(len, 4);
> >+ retry = 0;
> >+ while(waserror()){
> >+ sdioabort(Fn2);
> >+ if(++retry == 3)
> >+ nexterror();
> >+ }
> >+ sdiorwext(Fn2, write, buf, n, Enumbase, 0);
> >+ poperror();
> >+ buf += n;
> >+ len -= n;
> >+ }
> >+}
> >+
> >+/*
> >+ * Configuration and control of chip cores via Silicon Backplane
> >+ */
> >+
> >+static void
> >+sbdisable(ulong regs, int pre, int ioctl)
> >+{
> >+ sbwindow(regs);
> >+ if((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){
> >+ cfgwritel(Fn1, regs + Ioctrl, 3|ioctl);
> >+ cfgreadl(Fn1, regs + Ioctrl);
> >+ return;
> >+ }
> >+ cfgwritel(Fn1, regs + Ioctrl, 3|pre);
> >+ cfgreadl(Fn1, regs + Ioctrl);
> >+ cfgwritel(Fn1, regs + Resetctrl, 1);
> >+ microdelay(10);
> >+ while((cfgreadl(Fn1, regs + Resetctrl) & 1) == 0)
> >+ ;
> >+ cfgwritel(Fn1, regs + Ioctrl, 3|ioctl);
> >+ cfgreadl(Fn1, regs + Ioctrl);
> >+}
> >+
> >+static void
> >+sbreset(ulong regs, int pre, int ioctl)
> >+{
> >+ sbdisable(regs, pre, ioctl);
> >+ sbwindow(regs);
> >+ if(SBDEBUG) print("sbreset %#lux %#lux %#lux ->", regs,
> >+ cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl));
> >+ while((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){
> >+ cfgwritel(Fn1, regs + Resetctrl, 0);
> >+ microdelay(40);
> >+ }
> >+ cfgwritel(Fn1, regs + Ioctrl, 1|ioctl);
> >+ cfgreadl(Fn1, regs + Ioctrl);
> >+ if(SBDEBUG) print("%#lux %#lux\n",
> >+ cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl));
> >+}
> >+
> >+static void
> >+corescan(Ctlr *ctl, ulong r)
> >+{
> >+ uchar *buf;
> >+ int i, coreid, corerev;
> >+ ulong addr;
> >+
> >+ buf = sdmalloc(Corescansz);
> >+ if(buf == nil)
> >+ error(Enomem);
> >+ sbmem(0, buf, Corescansz, r);
> >+ coreid = 0;
> >+ corerev = 0;
> >+ for(i = 0; i < Corescansz; i += 4){
> >+ switch(buf[i]&0xF){
> >+ case 0xF: /* end */
> >+ sdfree(buf);
> >+ return;
> >+ case 0x1: /* core info */
> >+ if((buf[i+4]&0xF) != 0x1)
> >+ break;
> >+ coreid = (buf[i+1] | buf[i+2]<<8) & 0xFFF;
> >+ i += 4;
> >+ corerev = buf[i+3];
> >+ break;
> >+ case 0x05: /* address */
> >+ addr = buf[i+1]<<8 | buf[i+2]<<16 | buf[i+3]<<24;
> >+ addr &= ~0xFFF;
> >+ if(SBDEBUG) print("core %x %s %#lux\n", coreid, buf[i]&0xC0? "ctl" : "mem", addr);
> >+ switch(coreid){
> >+ case 0x800:
> >+ if((buf[i] & 0xC0) == 0)
> >+ ctl->chipcommon = addr;
> >+ break;
> >+ case ARMcm3:
> >+ case ARM7tdmi:
> >+ case ARMcr4:
> >+ ctl->armcore = coreid;
> >+ if(buf[i] & 0xC0){
> >+ if(ctl->armctl == 0)
> >+ ctl->armctl = addr;
> >+ }else{
> >+ if(ctl->armregs == 0)
> >+ ctl->armregs = addr;
> >+ }
> >+ break;
> >+ case 0x80E:
> >+ if(buf[i] & 0xC0)
> >+ ctl->socramctl = addr;
> >+ else if(ctl->socramregs == 0)
> >+ ctl->socramregs = addr;
> >+ ctl->socramrev = corerev;
> >+ break;
> >+ case 0x829:
> >+ if((buf[i] & 0xC0) == 0)
> >+ ctl->sdregs = addr;
> >+ ctl->sdiorev = corerev;
> >+ break;
> >+ case 0x812:
> >+ if(buf[i] & 0xC0)
> >+ ctl->d11ctl = addr;
> >+ break;
> >+ }
> >+ }
> >+ }
> >+ sdfree(buf);
> >+}
> >+
> >+static void
> >+ramscan(Ctlr *ctl)
> >+{
> >+ ulong r, n, size;
> >+ int banks, i;
> >+
> >+ if(ctl->armcore == ARMcr4){
> >+ r = ctl->armregs;
> >+ sbwindow(r);
> >+ n = cfgreadl(Fn1, r + Cr4Cap);
> >+ if(SBDEBUG) print("cr4 banks %lux\n", n);
> >+ banks = ((n>>4) & 0xF) + (n & 0xF);
> >+ size = 0;
> >+ for(i = 0; i < banks; i++){
> >+ cfgwritel(Fn1, r + Cr4Bankidx, i);
> >+ n = cfgreadl(Fn1, r + Cr4Bankinfo);
> >+ if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1));
> >+ size += 8192 * ((n & 0x3F) + 1);
> >+ }
> >+ ctl->socramsize = size;
> >+ ctl->rambase = 0x198000;
> >+ return;
> >+ }
> >+ if(ctl->socramrev <= 7 || ctl->socramrev == 12){
> >+ print("ether4330: SOCRAM rev %d not supported\n", ctl->socramrev);
> >+ error(Eio);
> >+ }
> >+ sbreset(ctl->socramctl, 0, 0);
> >+ r = ctl->socramregs;
> >+ sbwindow(r);
> >+ n = cfgreadl(Fn1, r + Coreinfo);
> >+ if(SBDEBUG) print("socramrev %d coreinfo %lux\n", ctl->socramrev, n);
> >+ banks = (n>>4) & 0xF;
> >+ size = 0;
> >+ for(i = 0; i < banks; i++){
> >+ cfgwritel(Fn1, r + Bankidx, i);
> >+ n = cfgreadl(Fn1, r + Bankinfo);
> >+ if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1));
> >+ size += 8192 * ((n & 0x3F) + 1);
> >+ }
> >+ ctl->socramsize = size;
> >+ ctl->rambase = 0;
> >+ if(ctl->chipid == 43430){
> >+ cfgwritel(Fn1, r + Bankidx, 3);
> >+ cfgwritel(Fn1, r + Bankpda, 0);
> >+ }
> >+}
> >+
> >+static void
> >+sbinit(Ctlr *ctl)
> >+{
> >+ ulong r;
> >+ int chipid;
> >+ char buf[16];
> >+
> >+ sbwindow(Enumbase);
> >+ r = cfgreadl(Fn1, Enumbase);
> >+ chipid = r & 0xFFFF;
> >+ sprint(buf, chipid > 43000 ? "%d" : "%#x", chipid);
> >+ print("ether4330: chip %s rev %ld type %ld\n", buf, (r>>16)&0xF, (r>>28)&0xF);
> >+ switch(chipid){
> >+ case 0x4330:
> >+ case 43362:
> >+ case 43430:
> >+ case 0x4345:
> >+ ctl->chipid = chipid;
> >+ ctl->chiprev = (r>>16)&0xF;
> >+ break;
> >+ default:
> >+ print("ether4330: chipid %#x (%d) not supported\n", chipid, chipid);
> >+ error(Eio);
> >+ }
> >+ r = cfgreadl(Fn1, Enumbase + 63*4);
> >+ corescan(ctl, r);
> >+ if(ctl->armctl == 0 || ctl->d11ctl == 0 ||
> >+ (ctl->armcore == ARMcm3 && (ctl->socramctl == 0 || ctl->socramregs == 0)))
> >+ error("corescan didn't find essential cores\n");
> >+ if(ctl->armcore == ARMcr4)
> >+ sbreset(ctl->armctl, Cr4Cpuhalt, Cr4Cpuhalt);
> >+ else
> >+ sbdisable(ctl->armctl, 0, 0);
> >+ sbreset(ctl->d11ctl, 8|4, 4);
> >+ ramscan(ctl);
> >+ if(SBDEBUG) print("ARM %#lux D11 %#lux SOCRAM %#lux,%#lux %lud bytes @ %#lux\n",
> >+ ctl->armctl, ctl->d11ctl, ctl->socramctl, ctl->socramregs, ctl->socramsize, ctl->rambase);
> >+ cfgw(Clkcsr, 0);
> >+ microdelay(10);
> >+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
> >+ cfgw(Clkcsr, Nohwreq | ReqALP);
> >+ while((cfgr(Clkcsr) & (HTavail|ALPavail)) == 0)
> >+ microdelay(10);
> >+ cfgw(Clkcsr, Nohwreq | ForceALP);
> >+ microdelay(65);
> >+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
> >+ cfgw(Pullups, 0);
> >+ sbwindow(ctl->chipcommon);
> >+ cfgwritel(Fn1, ctl->chipcommon + Gpiopullup, 0);
> >+ cfgwritel(Fn1, ctl->chipcommon + Gpiopulldown, 0);
> >+ if(ctl->chipid != 0x4330 && ctl->chipid != 43362)
> >+ return;
> >+ cfgwritel(Fn1, ctl->chipcommon + Chipctladdr, 1);
> >+ if(cfgreadl(Fn1, ctl->chipcommon + Chipctladdr) != 1)
> >+ print("ether4330: can't set Chipctladdr\n");
> >+ else{
> >+ r = cfgreadl(Fn1, ctl->chipcommon + Chipctldata);
> >+ if(SBDEBUG) print("chipcommon PMU (%lux) %lux", cfgreadl(Fn1, ctl->chipcommon + Chipctladdr), r);
> >+ /* set SDIO drive strength >= 6mA */
> >+ r &= ~0x3800;
> >+ if(ctl->chipid == 0x4330)
> >+ r |= 3<<11;
> >+ else
> >+ r |= 7<<11;
> >+ cfgwritel(Fn1, ctl->chipcommon + Chipctldata, r);
> >+ if(SBDEBUG) print("-> %lux (= %lux)\n", r, cfgreadl(Fn1, ctl->chipcommon + Chipctldata));
> >+ }
> >+}
> >+
> >+static void
> >+sbenable(Ctlr *ctl)
> >+{
> >+ int i;
> >+
> >+ if(SBDEBUG) print("enabling HT clock...");
> >+ cfgw(Clkcsr, 0);
> >+ delay(1);
> >+ cfgw(Clkcsr, ReqHT);
> >+ for(i = 0; (cfgr(Clkcsr) & HTavail) == 0; i++){
> >+ if(i == 50){
> >+ print("ether4330: can't enable HT clock: csr %x\n", cfgr(Clkcsr));
> >+ error(Eio);
> >+ }
> >+ tsleep(&up->sleep, return0, nil, 100);
> >+ }
> >+ cfgw(Clkcsr, cfgr(Clkcsr) | ForceHT);
> >+ delay(10);
> >+ if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
> >+ sbwindow(ctl->sdregs);
> >+ cfgwritel(Fn1, ctl->sdregs + Sbmboxdata, 4 << 16); /* protocol version */
> >+ cfgwritel(Fn1, ctl->sdregs + Intmask, FrameInt | MailboxInt | Fcchange);
> >+ sdioset(Fn0, Ioenable, 1<<Fn2);
> >+ for(i = 0; !(sdiord(Fn0, Ioready) & 1<<Fn2); i++){
> >+ if(i == 10){
> >+ print("ether4330: can't enable SDIO function 2 - ioready %x\n", sdiord(Fn0, Ioready));
> >+ error(Eio);
> >+ }
> >+ tsleep(&up->sleep, return0, nil, 100);
> >+ }
> >+ sdiowr(Fn0, Intenable, (1<<Fn1) | (1<<Fn2) | 1);
> >+}
> >+
> >+
> >+/*
> >+ * Firmware and config file uploading
> >+ */
> >+
> >+/*
> >+ * Condense config file contents (in buffer buf with length n)
> >+ * to 'var=value\0' list for firmware:
> >+ * - remove comments (starting with '#') and blank lines
> >+ * - remove carriage returns
> >+ * - convert newlines to nulls
> >+ * - mark end with two nulls
> >+ * - pad with nulls to multiple of 4 bytes total length
> >+ */
> >+static int
> >+condense(uchar *buf, int n)
> >+{
> >+ uchar *p, *ep, *lp, *op;
> >+ int c, skipping;
> >+
> >+ skipping = 0; /* true if in a comment */
> >+ ep = buf + n; /* end of input */
> >+ op = buf; /* end of output */
> >+ lp = buf; /* start of current output line */
> >+ for(p = buf; p < ep; p++){
> >+ switch(c = *p){
> >+ case '#':
> >+ skipping = 1;
> >+ break;
> >+ case '\0':
> >+ case '\n':
> >+ skipping = 0;
> >+ if(op != lp){
> >+ *op++ = '\0';
> >+ lp = op;
> >+ }
> >+ break;
> >+ case '\r':
> >+ break;
> >+ default:
> >+ if(!skipping)
> >+ *op++ = c;
> >+ break;
> >+ }
> >+ }
> >+ if(!skipping && op != lp)
> >+ *op++ = '\0';
> >+ *op++ = '\0';
> >+ for(n = op - buf; n & 03; n++)
> >+ *op++ = '\0';
> >+ return n;
> >+}
> >+
> >+/*
> >+ * Try to find firmware file in /boot or in /lib/firmware.
> >+ * Throw an error if not found.
> >+ */
> >+static Chan*
> >+findfirmware(char *file)
> >+{
> >+ char nbuf[64];
> >+ Chan *c;
> >+
> >+ if(!waserror()){
> >+ snprint(nbuf, sizeof nbuf, "/boot/%s", file);
> >+ c = namec(nbuf, Aopen, OREAD, 0);
> >+ poperror();
> >+ }else if(!waserror()){
> >+ snprint(nbuf, sizeof nbuf, "/lib/firmware/%s", file);
> >+ c = namec(nbuf, Aopen, OREAD, 0);
> >+ poperror();
> >+ }else{
> >+ c = nil;
> >+ snprint(up->genbuf, sizeof up->genbuf, "can't find %s in /boot or /lib/firmware", file);
> >+ error(up->genbuf);
> >+ }
> >+ return c;
> >+}
> >+
> >+static int
> >+upload(Ctlr *ctl, char *file, int isconfig)
> >+{
> >+ Chan *c;
> >+ uchar *buf;
> >+ uchar *cbuf;
> >+ int off, n;
> >+
> >+ buf = cbuf = nil;
> >+ c = findfirmware(file);
> >+ if(waserror()){
> >+ cclose(c);
> >+ sdfree(buf);
> >+ sdfree(cbuf);
> >+ nexterror();
> >+ }
> >+ buf = sdmalloc(Uploadsz);
> >+ if(buf == nil)
> >+ error(Enomem);
> >+ if(Firmwarecmp){
> >+ cbuf = sdmalloc(Uploadsz);
> >+ if(cbuf == nil)
> >+ error(Enomem);
> >+ }
> >+ off = 0;
> >+ for(;;){
> >+ n = devtab[c->type]->read(c, buf, Uploadsz, off);
> >+ if(n <= 0)
> >+ break;
> >+ if(isconfig){
> >+ n = condense(buf, n);
> >+ off = ctl->socramsize - n - 4;
> >+ }else if(off == 0)
> >+ memmove(ctl->resetvec.c, buf, sizeof(ctl->resetvec.c));
> >+ while(n&3)
> >+ buf[n++] = 0;
> >+ sbmem(1, buf, n, ctl->rambase + off);
> >+ if(isconfig)
> >+ break;
> >+ off += n;
> >+ }
> >+ if(Firmwarecmp){
> >+ if(FWDEBUG) print("compare...");
> >+ if(!isconfig)
> >+ off = 0;
> >+ for(;;){
> >+ if(!isconfig){
> >+ n = devtab[c->type]->read(c, buf, Uploadsz, off);
> >+ if(n <= 0)
> >+ break;
> >+ while(n&3)
> >+ buf[n++] = 0;
> >+ }
> >+ sbmem(0, cbuf, n, ctl->rambase + off);
> >+ if(memcmp(buf, cbuf, n) != 0){
> >+ print("ether4330: firmware load failed offset %d\n", off);
> >+ error(Eio);
> >+ }
> >+ if(isconfig)
> >+ break;
> >+ off += n;
> >+ }
> >+ }
> >+ if(FWDEBUG) print("\n");
> >+ poperror();
> >+ cclose(c);
> >+ sdfree(buf);
> >+ sdfree(cbuf);
> >+ return n;
> >+}
> >+
> >+/*
> >+ * Upload regulatory file (.clm) to firmware.
> >+ * Packet format is
> >+ * [2]flag [2]type [4]len [4]crc [len]data
> >+ */
> >+static void
> >+reguload(Ctlr *ctl, char *file)
> >+{
> >+ Chan *c;
> >+ uchar *buf;
> >+ int off, n, flag;
> >+ enum {
> >+ Reguhdr = 2+2+4+4,
> >+ Regusz = 1400,
> >+ Regutyp = 2,
> >+ Flagclm = 1<<12,
> >+ Firstpkt= 1<<1,
> >+ Lastpkt = 1<<2,
> >+ };
> >+
> >+ buf = nil;
> >+ c = findfirmware(file);
> >+ if(waserror()){
> >+ cclose(c);
> >+ free(buf);
> >+ nexterror();
> >+ }
> >+ buf = malloc(Reguhdr+Regusz+1);
> >+ if(buf == nil)
> >+ error(Enomem);
> >+ put2(buf+2, Regutyp);
> >+ put2(buf+8, 0);
> >+ off = 0;
> >+ flag = Flagclm | Firstpkt;
> >+ while((flag&Lastpkt) == 0){
> >+ n = devtab[c->type]->read(c, buf+Reguhdr, Regusz+1, off);
> >+ if(n <= 0)
> >+ break;
> >+ if(n == Regusz+1)
> >+ --n;
> >+ else{
> >+ while(n&7)
> >+ buf[Reguhdr+n++] = 0;
> >+ flag |= Lastpkt;
> >+ }
> >+ put2(buf+0, flag);
> >+ put4(buf+4, n);
> >+ wlsetvar(ctl, "clmload", buf, Reguhdr + n);
> >+ off += n;
> >+ flag &= ~Firstpkt;
> >+ }
> >+ poperror();
> >+ cclose(c);
> >+ free(buf);
> >+}
> >+
> >+static void
> >+fwload(Ctlr *ctl)
> >+{
> >+ uchar buf[4];
> >+ uint i, n;
> >+
> >+ i = 0;
> >+ while(firmware[i].chipid != ctl->chipid ||
> >+ firmware[i].chiprev != ctl->chiprev){
> >+ if(++i == nelem(firmware)){
> >+ print("ether4330: no firmware for chipid %x (%d) chiprev %d\n",
> >+ ctl->chipid, ctl->chipid, ctl->chiprev);
> >+ error("no firmware");
> >+ }
> >+ }
> >+ ctl->regufile = firmware[i].regufile;
> >+ cfgw(Clkcsr, ReqALP);
> >+ while((cfgr(Clkcsr) & ALPavail) == 0)
> >+ microdelay(10);
> >+ memset(buf, 0, 4);
> >+ sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4);
> >+ if(FWDEBUG) print("firmware load...");
> >+ upload(ctl, firmware[i].fwfile, 0);
> >+ if(FWDEBUG) print("config load...");
> >+ n = upload(ctl, firmware[i].cfgfile, 1);
> >+ n /= 4;
> >+ n = (n & 0xFFFF) | (~n << 16);
> >+ put4(buf, n);
> >+ sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4);
> >+ if(ctl->armcore == ARMcr4){
> >+ sbwindow(ctl->sdregs);
> >+ cfgwritel(Fn1, ctl->sdregs + Intstatus, ~0);
> >+ if(ctl->resetvec.i != 0){
> >+ if(SBDEBUG) print("%ux\n", ctl->resetvec.i);
> >+ sbmem(1, ctl->resetvec.c, sizeof(ctl->resetvec.c), 0);
> >+ }
> >+ sbreset(ctl->armctl, Cr4Cpuhalt, 0);
> >+ }else
> >+ sbreset(ctl->armctl, 0, 0);
> >+}
> >+
> >+/*
> >+ * Communication of data and control packets
> >+ */
> >+
> >+void
> >+intwait(Ctlr *ctlr, int wait)
> >+{
> >+ ulong ints, mbox;
> >+ int i;
> >+
> >+ if(waserror())
> >+ return;
> >+ for(;;){
> >+ sdiocardintr(wait);
> >+ sbwindow(ctlr->sdregs);
> >+ i = sdiord(Fn0, Intpend);
> >+ if(i == 0){
> >+ tsleep(&up->sleep, return0, 0, 10);
> >+ continue;
> >+ }
> >+ ints = cfgreadl(Fn1, ctlr->sdregs + Intstatus);
> >+ cfgwritel(Fn1, ctlr->sdregs + Intstatus, ints);
> >+ if(0) print("INTS: (%x) %lux -> %lux\n", i, ints, cfgreadl(Fn1, ctlr->sdregs + Intstatus));
> >+ if(ints & MailboxInt){
> >+ mbox = cfgreadl(Fn1, ctlr->sdregs + Hostmboxdata);
> >+ cfgwritel(Fn1, ctlr->sdregs + Sbmbox, 2); /* ack */
> >+ if(mbox & 0x8)
> >+ print("ether4330: firmware ready\n");
> >+ }
> >+ if(ints & FrameInt)
> >+ break;
> >+ }
> >+ poperror();
> >+}
> >+
> >+static Block*
> >+wlreadpkt(Ctlr *ctl)
> >+{
> >+ Block *b;
> >+ Sdpcm *p;
> >+ int len, lenck;
> >+
> >+ b = allocb(2048);
> >+ p = (Sdpcm*)b->wp;
> >+ qlock(&ctl->pktlock);
> >+ for(;;){
> >+ packetrw(0, b->wp, Sdpcmsz);
> >+ len = p->len[0] | p->len[1]<<8;
> >+ if(len == 0){
> >+ freeb(b);
> >+ b = nil;
> >+ break;
> >+ }
> >+ lenck = p->lenck[0] | p->lenck[1]<<8;
> >+ if(lenck != (len ^ 0xFFFF) ||
> >+ len < Sdpcmsz || len > 2048){
> >+ print("ether4330: wlreadpkt error len %.4x lenck %.4x\n", len, lenck);
> >+ cfgw(Framectl, Rfhalt);
> >+ while(cfgr(Rfrmcnt+1))
> >+ ;
> >+ while(cfgr(Rfrmcnt))
> >+ ;
> >+ continue;
> >+ }
> >+ if(len > Sdpcmsz)
> >+ packetrw(0, b->wp + Sdpcmsz, len - Sdpcmsz);
> >+ b->wp += len;
> >+ break;
> >+ }
> >+ qunlock(&ctl->pktlock);
> >+ return b;
> >+}
> >+
> >+static void
> >+txstart(Ether *edev)
> >+{
> >+ Ctlr *ctl;
> >+ Sdpcm *p;
> >+ Block *b;
> >+ int len, off;
> >+
> >+ ctl = edev->ctlr;
> >+ if(!canqlock(&ctl->tlock))
> >+ return;
> >+ if(waserror()){
> >+ qunlock(&ctl->tlock);
> >+ return;
> >+ }
> >+ for(;;){
> >+ lock(&ctl->txwinlock);
> >+ if(ctl->txseq == ctl->txwindow){
> >+ //print("f");
> >+ unlock(&ctl->txwinlock);
> >+ break;
> >+ }
> >+ if(ctl->fcmask & 1<<2){
> >+ //print("x");
> >+ unlock(&ctl->txwinlock);
> >+ break;
> >+ }
> >+ unlock(&ctl->txwinlock);
> >+ b = qget(edev->oq);
> >+ if(b == nil)
> >+ break;
> >+ off = ((uintptr)b->rp & 3) + Sdpcmsz;
> >+ b = padblock(b, off + 4);
> >+ len = BLEN(b);
> >+ p = (Sdpcm*)b->rp;
> >+ memset(p, 0, off); /* TODO: refactor dup code */
> >+ put2(p->len, len);
> >+ put2(p->lenck, ~len);
> >+ p->chanflg = 2;
> >+ p->seq = ctl->txseq;
> >+ p->doffset = off;
> >+ put4(b->rp + off, 0x20); /* BDC header */
> >+ if(iodebug) dump("send", b->rp, len);
> >+ qlock(&ctl->pktlock);
> >+ if(waserror()){
> >+ if(iodebug) print("halt frame %x %x\n", cfgr(Wfrmcnt+1), cfgr(Wfrmcnt+1));
> >+ cfgw(Framectl, Wfhalt);
> >+ while(cfgr(Wfrmcnt+1))
> >+ ;
> >+ while(cfgr(Wfrmcnt))
> >+ ;
> >+ qunlock(&ctl->pktlock);
> >+ nexterror();
> >+ }
> >+ packetrw(1, b->rp, len);
> >+ ctl->txseq++;
> >+ poperror();
> >+ qunlock(&ctl->pktlock);
> >+ freeb(b);
> >+ }
> >+ poperror();
> >+ qunlock(&ctl->tlock);
> >+}
> >+
> >+static void
> >+rproc(void *a)
> >+{
> >+ Ether *edev;
> >+ Ctlr *ctl;
> >+ Block *b;
> >+ Sdpcm *p;
> >+ Cmd *q;
> >+ int flowstart;
> >+ int bdc;
> >+
> >+ edev = a;
> >+ ctl = edev->ctlr;
> >+ flowstart = 0;
> >+ for(;;){
> >+ if(flowstart){
> >+ //print("F");
> >+ flowstart = 0;
> >+ txstart(edev);
> >+ }
> >+ b = wlreadpkt(ctl);
> >+ if(b == nil){
> >+ intwait(ctl, 1);
> >+ continue;
> >+ }
> >+ p = (Sdpcm*)b->rp;
> >+ if(p->window != ctl->txwindow || p->fcmask != ctl->fcmask){
> >+ lock(&ctl->txwinlock);
> >+ if(p->window != ctl->txwindow){
> >+ if(ctl->txseq == ctl->txwindow)
> >+ flowstart = 1;
> >+ ctl->txwindow = p->window;
> >+ }
> >+ if(p->fcmask != ctl->fcmask){
> >+ if((p->fcmask & 1<<2) == 0)
> >+ flowstart = 1;
> >+ ctl->fcmask = p->fcmask;
> >+ }
> >+ unlock(&ctl->txwinlock);
> >+ }
> >+ switch(p->chanflg & 0xF){
> >+ case 0:
> >+ if(iodebug) dump("rsp", b->rp, BLEN(b));
> >+ if(BLEN(b) < Sdpcmsz + Cmdsz)
> >+ break;
> >+ q = (Cmd*)(b->rp + Sdpcmsz);
> >+ if((q->id[0] | q->id[1]<<8) != ctl->reqid)
> >+ break;
> >+ ctl->rsp = b;
> >+ wakeup(&ctl->cmdr);
> >+ continue;
> >+ case 1:
> >+ if(iodebug) dump("event", b->rp, BLEN(b));
> >+ if(BLEN(b) > p->doffset + 4){
> >+ bdc = 4 + (b->rp[p->doffset + 3] << 2);
> >+ if(BLEN(b) > p->doffset + bdc){
> >+ b->rp += p->doffset + bdc; /* skip BDC header */
> >+ bcmevent(ctl, b->rp, BLEN(b));
> >+ break;
> >+ }
> >+ }
> >+ if(iodebug && BLEN(b) != p->doffset)
> >+ print("short event %lld %d\n", BLEN(b), p->doffset);
> >+ break;
> >+ case 2:
> >+ if(iodebug) dump("packet", b->rp, BLEN(b));
> >+ if(BLEN(b) > p->doffset + 4){
> >+ bdc = 4 + (b->rp[p->doffset + 3] << 2);
> >+ if(BLEN(b) >= p->doffset + bdc + ETHERHDRSIZE){
> >+ b->rp += p->doffset + bdc; /* skip BDC header */
> >+ etheriq(edev, b);
> >+ continue;
> >+ }
> >+ }
> >+ break;
> >+ default:
> >+ dump("ether4330: bad packet", b->rp, BLEN(b));
> >+ break;
> >+ }
> >+ freeb(b);
> >+ }
> >+}
> >+
> >+static void
> >+linkdown(Ctlr *ctl)
> >+{
> >+ Ether *edev;
> >+ Netfile *f;
> >+ int i;
> >+
> >+ edev = ctl->edev;
> >+ if(edev == nil || ctl->status != Connected)
> >+ return;
> >+ ctl->status = Disconnected;
> >+ /* send eof to aux/wpa */
> >+ for(i = 0; i < edev->nfile; i++){
> >+ f = edev->f[i];
> >+ if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e)
> >+ continue;
> >+ qwrite(f->in, 0, 0);
> >+ }
> >+}
> >+
> >+/*
> >+ * Command interface between host and firmware
> >+ */
> >+
> >+static char *eventnames[] = {
> >+ [0] = "set ssid",
> >+ [1] = "join",
> >+ [2] = "start",
> >+ [3] = "auth",
> >+ [4] = "auth ind",
> >+ [5] = "deauth",
> >+ [6] = "deauth ind",
> >+ [7] = "assoc",
> >+ [8] = "assoc ind",
> >+ [9] = "reassoc",
> >+ [10] = "reassoc ind",
> >+ [11] = "disassoc",
> >+ [12] = "disassoc ind",
> >+ [13] = "quiet start",
> >+ [14] = "quiet end",
> >+ [15] = "beacon rx",
> >+ [16] = "link",
> >+ [17] = "mic error",
> >+ [18] = "ndis link",
> >+ [19] = "roam",
> >+ [20] = "txfail",
> >+ [21] = "pmkid cache",
> >+ [22] = "retrograde tsf",
> >+ [23] = "prune",
> >+ [24] = "autoauth",
> >+ [25] = "eapol msg",
> >+ [26] = "scan complete",
> >+ [27] = "addts ind",
> >+ [28] = "delts ind",
> >+ [29] = "bcnsent ind",
> >+ [30] = "bcnrx msg",
> >+ [31] = "bcnlost msg",
> >+ [32] = "roam prep",
> >+ [33] = "pfn net found",
> >+ [34] = "pfn net lost",
> >+ [35] = "reset complete",
> >+ [36] = "join start",
> >+ [37] = "roam start",
> >+ [38] = "assoc start",
> >+ [39] = "ibss assoc",
> >+ [40] = "radio",
> >+ [41] = "psm watchdog",
> >+ [44] = "probreq msg",
> >+ [45] = "scan confirm ind",
> >+ [46] = "psk sup",
> >+ [47] = "country code changed",
> >+ [48] = "exceeded medium time",
> >+ [49] = "icv error",
> >+ [50] = "unicast decode error",
> >+ [51] = "multicast decode error",
> >+ [52] = "trace",
> >+ [53] = "bta hci event",
> >+ [54] = "if",
> >+ [55] = "p2p disc listen complete",
> >+ [56] = "rssi",
> >+ [57] = "pfn scan complete",
> >+ [58] = "extlog msg",
> >+ [59] = "action frame",
> >+ [60] = "action frame complete",
> >+ [61] = "pre assoc ind",
> >+ [62] = "pre reassoc ind",
> >+ [63] = "channel adopted",
> >+ [64] = "ap started",
> >+ [65] = "dfs ap stop",
> >+ [66] = "dfs ap resume",
> >+ [67] = "wai sta event",
> >+ [68] = "wai msg",
> >+ [69] = "escan result",
> >+ [70] = "action frame off chan complete",
> >+ [71] = "probresp msg",
> >+ [72] = "p2p probreq msg",
> >+ [73] = "dcs request",
> >+ [74] = "fifo credit map",
> >+ [75] = "action frame rx",
> >+ [76] = "wake event",
> >+ [77] = "rm complete",
> >+ [78] = "htsfsync",
> >+ [79] = "overlay req",
> >+ [80] = "csa complete ind",
> >+ [81] = "excess pm wake event",
> >+ [82] = "pfn scan none",
> >+ [83] = "pfn scan allgone",
> >+ [84] = "gtk plumbed",
> >+ [85] = "assoc ind ndis",
> >+ [86] = "reassoc ind ndis",
> >+ [87] = "assoc req ie",
> >+ [88] = "assoc resp ie",
> >+ [89] = "assoc recreated",
> >+ [90] = "action frame rx ndis",
> >+ [91] = "auth req",
> >+ [92] = "tdls peer event",
> >+ [127] = "bcmc credit support"
> >+};
> >+
> >+static char*
> >+evstring(uint event)
> >+{
> >+ static char buf[12];
> >+
> >+ if(event >= nelem(eventnames) || eventnames[event] == 0){
> >+ /* not reentrant but only called from one kproc */
> >+ snprint(buf, sizeof buf, "%d", event);
> >+ return buf;
> >+ }
> >+ return eventnames[event];
> >+}
> >+
> >+static void
> >+bcmevent(Ctlr *ctl, uchar *p, int len)
> >+{
> >+ int flags;
> >+ long event, status, reason;
> >+
> >+ if(len < ETHERHDRSIZE + 10 + 46)
> >+ return;
> >+ p += ETHERHDRSIZE + 10; /* skip bcm_ether header */
> >+ len -= ETHERHDRSIZE + 10;
> >+ flags = nhgets(p + 2);
> >+ event = nhgets(p + 6);
> >+ status = nhgetl(p + 8);
> >+ reason = nhgetl(p + 12);
> >+ if(EVENTDEBUG)
> >+ print("ether4330: [%s] status %ld flags %#x reason %ld\n",
> >+ evstring(event), status, flags, reason);
> >+ switch(event){
> >+ case 19: /* E_ROAM */
> >+ if(status == 0)
> >+ break;
> >+ /* fall through */
> >+ case 0: /* E_SET_SSID */
> >+ ctl->joinstatus = 1 + status;
> >+ wakeup(&ctl->joinr);
> >+ break;
> >+ case 16: /* E_LINK */
> >+ if(flags&1) /* link up */
> >+ break;
> >+ /* fall through */
> >+ case 5: /* E_DEAUTH */
> >+ case 6: /* E_DEAUTH_IND */
> >+ case 12: /* E_DISASSOC_IND */
> >+ linkdown(ctl);
> >+ break;
> >+ case 26: /* E_SCAN_COMPLETE */
> >+ break;
> >+ case 69: /* E_ESCAN_RESULT */
> >+ wlscanresult(ctl->edev, p + 48, len - 48);
> >+ break;
> >+ default:
> >+ if(status){
> >+ if(!EVENTDEBUG)
> >+ print("ether4330: [%s] error status %ld flags %#x reason %ld\n",
> >+ evstring(event), status, flags, reason);
> >+ dump("event", p, len);
> >+ }
> >+ }
> >+}
> >+
> >+static int
> >+joindone(void *a)
> >+{
> >+ return ((Ctlr*)a)->joinstatus;
> >+}
> >+
> >+static int
> >+waitjoin(Ctlr *ctl)
> >+{
> >+ int n;
> >+
> >+ sleep(&ctl->joinr, joindone, ctl);
> >+ n = ctl->joinstatus;
> >+ ctl->joinstatus = 0;
> >+ return n - 1;
> >+}
> >+
> >+static int
> >+cmddone(void *a)
> >+{
> >+ return ((Ctlr*)a)->rsp != nil;
> >+}
> >+
> >+static void
> >+wlcmd(Ctlr *ctl, int write, int op, void *data, int dlen, void *res, int rlen)
> >+{
> >+ Block *b;
> >+ Sdpcm *p;
> >+ Cmd *q;
> >+ int len, tlen;
> >+
> >+ if(write)
> >+ tlen = dlen + rlen;
> >+ else
> >+ tlen = MAX(dlen, rlen);
> >+ len = Sdpcmsz + Cmdsz + tlen;
> >+ b = allocb(len);
> >+ qlock(&ctl->cmdlock);
> >+ if(waserror()){
> >+ freeb(b);
> >+ qunlock(&ctl->cmdlock);
> >+ nexterror();
> >+ }
> >+ memset(b->wp, 0, len);
> >+ qlock(&ctl->pktlock);
> >+ p = (Sdpcm*)b->wp;
> >+ put2(p->len, len);
> >+ put2(p->lenck, ~len);
> >+ p->seq = ctl->txseq;
> >+ p->doffset = Sdpcmsz;
> >+ b->wp += Sdpcmsz;
> >+
> >+ q = (Cmd*)b->wp;
> >+ put4(q->cmd, op);
> >+ put4(q->len, tlen);
> >+ put2(q->flags, write? 2 : 0);
> >+ put2(q->id, ++ctl->reqid);
> >+ put4(q->status, 0);
> >+ b->wp += Cmdsz;
> >+
> >+ if(dlen > 0)
> >+ memmove(b->wp, data, dlen);
> >+ if(write)
> >+ memmove(b->wp + dlen, res, rlen);
> >+ b->wp += tlen;
> >+
> >+ if(iodebug) dump("cmd", b->rp, len);
> >+ packetrw(1, b->rp, len);
> >+ ctl->txseq++;
> >+ qunlock(&ctl->pktlock);
> >+ freeb(b);
> >+ b = nil;
> >+ USED(b);
> >+ sleep(&ctl->cmdr, cmddone, ctl);
> >+ b = ctl->rsp;
> >+ ctl->rsp = nil;
> >+ assert(b != nil);
> >+ p = (Sdpcm*)b->rp;
> >+ q = (Cmd*)(b->rp + p->doffset);
> >+ if(q->status[0] | q->status[1] | q->status[2] | q->status[3]){
> >+ print("ether4330: cmd %d error status %ld\n", op, get4(q->status));
> >+ dump("ether4330: cmd error", b->rp, BLEN(b));
> >+ error("wlcmd error");
> >+ }
> >+ if(!write)
> >+ memmove(res, q + 1, rlen);
> >+ freeb(b);
> >+ qunlock(&ctl->cmdlock);
> >+ poperror();
> >+}
> >+
> >+static void
> >+wlcmdint(Ctlr *ctl, int op, int val)
> >+{
> >+ uchar buf[4];
> >+
> >+ put4(buf, val);
> >+ wlcmd(ctl, 1, op, buf, 4, nil, 0);
> >+}
> >+
> >+static void
> >+wlgetvar(Ctlr *ctl, char *name, void *val, int len)
> >+{
> >+ wlcmd(ctl, 0, GetVar, name, strlen(name) + 1, val, len);
> >+}
> >+
> >+static void
> >+wlsetvar(Ctlr *ctl, char *name, void *val, int len)
> >+{
> >+ if(VARDEBUG){
> >+ char buf[32];
> >+ snprint(buf, sizeof buf, "wlsetvar %s:", name);
> >+ dump(buf, val, len);
> >+ }
> >+ wlcmd(ctl, 1, SetVar, name, strlen(name) + 1, val, len);
> >+}
> >+
> >+static void
> >+wlsetint(Ctlr *ctl, char *name, int val)
> >+{
> >+ uchar buf[4];
> >+
> >+ put4(buf, val);
> >+ wlsetvar(ctl, name, buf, 4);
> >+}
> >+
> >+static void
> >+wlwepkey(Ctlr *ctl, int i)
> >+{
> >+ uchar params[164];
> >+ uchar *p;
> >+
> >+ memset(params, 0, sizeof params);
> >+ p = params;
> >+ p = put4(p, i); /* index */
> >+ p = put4(p, ctl->keys[i].len);
> >+ memmove(p, ctl->keys[i].dat, ctl->keys[i].len);
> >+ p += 32 + 18*4; /* keydata, pad */
> >+ if(ctl->keys[i].len == WMinKeyLen)
> >+ p = put4(p, 1); /* algo = WEP1 */
> >+ else
> >+ p = put4(p, 3); /* algo = WEP128 */
> >+ put4(p, 2); /* flags = Primarykey */
> >+
> >+ wlsetvar(ctl, "wsec_key", params, sizeof params);
> >+}
> >+
> >+static void
> >+memreverse(char *dst, char *src, int len)
> >+{
> >+ src += len;
> >+ while(len-- > 0)
> >+ *dst++ = *--src;
> >+}
> >+
> >+static void
> >+wlwpakey(Ctlr *ctl, int id, uvlong iv, uchar *ea)
> >+{
> >+ uchar params[164];
> >+ uchar *p;
> >+ int pairwise;
> >+
> >+ if(id == CMrxkey)
> >+ return;
> >+ pairwise = (id == CMrxkey || id == CMtxkey);
> >+ memset(params, 0, sizeof params);
> >+ p = params;
> >+ if(pairwise)
> >+ p = put4(p, 0);
> >+ else
> >+ p = put4(p, id - CMrxkey0); /* group key id */
> >+ p = put4(p, ctl->keys[0].len);
> >+ memmove((char*)p, ctl->keys[0].dat, ctl->keys[0].len);
> >+ p += 32 + 18*4; /* keydata, pad */
> >+ if(ctl->cryptotype == Wpa)
> >+ p = put4(p, 2); /* algo = TKIP */
> >+ else
> >+ p = put4(p, 4); /* algo = AES_CCM */
> >+ if(pairwise)
> >+ p = put4(p, 0);
> >+ else
> >+ p = put4(p, 2); /* flags = Primarykey */
> >+ p += 3*4;
> >+ p = put4(p, 0); //pairwise); /* iv initialised */
> >+ p += 4;
> >+ p = put4(p, iv>>16); /* iv high */
> >+ p = put2(p, iv&0xFFFF); /* iv low */
> >+ p += 2 + 2*4; /* align, pad */
> >+ if(pairwise)
> >+ memmove(p, ea, Eaddrlen);
> >+
> >+ wlsetvar(ctl, "wsec_key", params, sizeof params);
> >+}
> >+
> >+static void
> >+wljoin(Ctlr *ctl, char *ssid, int chan)
> >+{
> >+ uchar params[72];
> >+ uchar *p;
> >+ int n;
> >+
> >+ if(chan != 0)
> >+ chan |= 0x2b00; /* 20Mhz channel width */
> >+ p = params;
> >+ n = strlen(ssid);
> >+ n = MIN(n, 32);
> >+ p = put4(p, n);
> >+ memmove(p, ssid, n);
> >+ memset(p + n, 0, 32 - n);
> >+ p += 32;
> >+ p = put4(p, 0xff); /* scan type */
> >+ if(chan != 0){
> >+ p = put4(p, 2); /* num probes */
> >+ p = put4(p, 120); /* active time */
> >+ p = put4(p, 390); /* passive time */
> >+ }else{
> >+ p = put4(p, -1); /* num probes */
> >+ p = put4(p, -1); /* active time */
> >+ p = put4(p, -1); /* passive time */
> >+ }
> >+ p = put4(p, -1); /* home time */
> >+ memset(p, 0xFF, Eaddrlen); /* bssid */
> >+ p += Eaddrlen;
> >+ p = put2(p, 0); /* pad */
> >+ if(chan != 0){
> >+ p = put4(p, 1); /* num chans */
> >+ p = put2(p, chan); /* chan spec */
> >+ p = put2(p, 0); /* pad */
> >+ assert(p == params + sizeof(params));
> >+ }else{
> >+ p = put4(p, 0); /* num chans */
> >+ assert(p == params + sizeof(params) - 4);
> >+ }
> >+
> >+ wlsetvar(ctl, "join", params, chan? sizeof params : sizeof params - 4);
> >+ ctl->status = Connecting;
> >+ switch(waitjoin(ctl)){
> >+ case 0:
> >+ ctl->status = Connected;
> >+ break;
> >+ case 3:
> >+ ctl->status = Disconnected;
> >+ error("wifi join: network not found");
> >+ case 1:
> >+ ctl->status = Disconnected;
> >+ error("wifi join: failed");
> >+ default:
> >+ ctl->status = Disconnected;
> >+ error("wifi join: error");
> >+ }
> >+}
> >+
> >+static void
> >+wlscanstart(Ctlr *ctl)
> >+{
> >+ /* version[4] action[2] sync_id[2] ssidlen[4] ssid[32] bssid[6] bss_type[1]
> >+ scan_type[1] nprobes[4] active_time[4] passive_time[4] home_time[4]
> >+ nchans[2] nssids[2] chans[nchans][2] ssids[nssids][32] */
> >+ /* hack - this is only correct on a little-endian cpu */
> >+ static uchar params[4+2+2+4+32+6+1+1+4*4+2+2+14*2+32+4] = {
> >+ 1,0,0,0,
> >+ 1,0,
> >+ 0x34,0x12,
> >+ 0,0,0,0,
> >+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> >+ 0xff,0xff,0xff,0xff,0xff,0xff,
> >+ 2,
> >+ 0,
> >+ 0xff,0xff,0xff,0xff,
> >+ 0xff,0xff,0xff,0xff,
> >+ 0xff,0xff,0xff,0xff,
> >+ 0xff,0xff,0xff,0xff,
> >+ 14,0,
> >+ 1,0,
> >+ 0x01,0x2b,0x02,0x2b,0x03,0x2b,0x04,0x2b,0x05,0x2e,0x06,0x2e,0x07,0x2e,
> >+ 0x08,0x2b,0x09,0x2b,0x0a,0x2b,0x0b,0x2b,0x0c,0x2b,0x0d,0x2b,0x0e,0x2b,
> >+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> >+ };
> >+
> >+ wlcmdint(ctl, 49, 0); /* PASSIVE_SCAN */
> >+ wlsetvar(ctl, "escan", params, sizeof params);
> >+}
> >+
> >+static uchar*
> >+gettlv(uchar *p, uchar *ep, int tag)
> >+{
> >+ int len;
> >+
> >+ while(p + 1 < ep){
> >+ len = p[1];
> >+ if(p + 2 + len > ep)
> >+ return nil;
> >+ if(p[0] == tag)
> >+ return p;
> >+ p += 2 + len;
> >+ }
> >+ return nil;
> >+}
> >+
> >+static void
> >+addscan(Block *bp, uchar *p, int len)
> >+{
> >+ char bssid[24];
> >+ char *auth, *auth2;
> >+ uchar *t, *et;
> >+ int ielen;
> >+ static uchar wpaie1[4] = { 0x00, 0x50, 0xf2, 0x01 };
> >+
> >+ snprint(bssid, sizeof bssid, ";bssid=%E", p + 8);
> >+ if(strstr((char*)bp->rp, bssid) != nil)
> >+ return;
> >+ bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
> >+ "ssid=%.*s%s;signal=%d;noise=%d;chan=%d",
> >+ p[18], (char*)p+19, bssid,
> >+ (short)get2(p+78), (signed char)p[80],
> >+ get2(p+72) & 0xF);
> >+ auth = auth2 = "";
> >+ if(get2(p + 16) & 0x10)
> >+ auth = ";wep";
> >+ ielen = get4(p + 0x78);
> >+ if(ielen > 0){
> >+ t = p + get4(p + 0x74);
> >+ et = t + ielen;
> >+ if(et > p + len)
> >+ return;
> >+ if(gettlv(t, et, 0x30) != nil){
> >+ auth = "";
> >+ auth2 = ";wpa2";
> >+ }
> >+ while((t = gettlv(t, et, 0xdd)) != nil){
> >+ if(t[1] > 4 && memcmp(t+2, wpaie1, 4) == 0){
> >+ auth = ";wpa";
> >+ break;
> >+ }
> >+ t += 2 + t[1];
> >+ }
> >+ }
> >+ bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
> >+ "%s%s\n", auth, auth2);
> >+}
> >+
> >+
> >+static void
> >+wlscanresult(Ether *edev, uchar *p, int len)
> >+{
> >+ Ctlr *ctlr;
> >+ Netfile **ep, *f, **fp;
> >+ Block *bp;
> >+ int nbss, i;
> >+
> >+ ctlr = edev->ctlr;
> >+ if(get4(p) > len)
> >+ return;
> >+ /* TODO: more syntax checking */
> >+ bp = ctlr->scanb;
> >+ if(bp == nil)
> >+ ctlr->scanb = bp = allocb(8192);
> >+ nbss = get2(p+10);
> >+ p += 12;
> >+ len -= 12;
> >+ if(0) dump("SCAN", p, len);
> >+ if(nbss){
> >+ addscan(bp, p, len);
> >+ return;
> >+ }
> >+ i = edev->scan;
> >+ ep = &edev->f[Ntypes];
> >+ for(fp = edev->f; fp < ep && i > 0; fp++){
> >+ f = *fp;
> >+ if(f == nil || f->scan == 0)
> >+ continue;
> >+ if(i == 1)
> >+ qpass(f->in, bp);
> >+ else
> >+ qpass(f->in, copyblock(bp, BLEN(bp)));
> >+ i--;
> >+ }
> >+ if(i)
> >+ freeb(bp);
> >+ ctlr->scanb = nil;
> >+}
> >+
> >+static void
> >+lproc(void *a)
> >+{
> >+ Ether *edev;
> >+ Ctlr *ctlr;
> >+ int secs;
> >+
> >+ edev = a;
> >+ ctlr = edev->ctlr;
> >+ secs = 0;
> >+ for(;;){
> >+ tsleep(&up->sleep, return0, 0, 1000);
> >+ if(ctlr->scansecs){
> >+ if(secs == 0){
> >+ if(waserror())
> >+ ctlr->scansecs = 0;
> >+ else{
> >+ wlscanstart(ctlr);
> >+ poperror();
> >+ }
> >+ secs = ctlr->scansecs;
> >+ }
> >+ --secs;
> >+ }else
> >+ secs = 0;
> >+ }
> >+}
> >+
> >+static void
> >+wlinit(Ether *edev, Ctlr *ctlr)
> >+{
> >+ uchar ea[Eaddrlen];
> >+ uchar eventmask[16];
> >+ char version[128];
> >+ char *p;
> >+ static uchar keepalive[12] = {1, 0, 11, 0, 0xd8, 0xd6, 0, 0, 0, 0, 0, 0};
> >+
> >+ wlgetvar(ctlr, "cur_etheraddr", ea, Eaddrlen);
> >+ memmove(edev->ea, ea, Eaddrlen);
> >+ memmove(edev->addr, ea, Eaddrlen);
> >+ print("ether4330: addr %E\n", edev->ea);
> >+ wlsetint(ctlr, "assoc_listen", 10);
> >+ if(ctlr->chipid == 43430 || ctlr->chipid == 0x4345)
> >+ wlcmdint(ctlr, 0x56, 0); /* powersave off */
> >+ else
> >+ wlcmdint(ctlr, 0x56, 2); /* powersave FAST */
> >+ wlsetint(ctlr, "bus:txglom", 0);
> >+ wlsetint(ctlr, "bcn_timeout", 10);
> >+ wlsetint(ctlr, "assoc_retry_max", 3);
> >+ if(ctlr->chipid == 0x4330){
> >+ wlsetint(ctlr, "btc_wire", 4);
> >+ wlsetint(ctlr, "btc_mode", 1);
> >+ wlsetvar(ctlr, "mkeep_alive", keepalive, 11);
> >+ }
> >+ memset(eventmask, 0xFF, sizeof eventmask);
> >+#define ENABLE(n) eventmask[n/8] |= 1<<(n%8)
> >+#define DISABLE(n) eventmask[n/8] &= ~(1<<(n%8))
> >+ DISABLE(40); /* E_RADIO */
> >+ DISABLE(44); /* E_PROBREQ_MSG */
> >+ DISABLE(54); /* E_IF */
> >+ DISABLE(71); /* E_PROBRESP_MSG */
> >+ DISABLE(20); /* E_TXFAIL */
> >+ DISABLE(124); /* ? */
> >+ wlsetvar(ctlr, "event_msgs", eventmask, sizeof eventmask);
> >+ wlcmdint(ctlr, 0xb9, 0x28); /* SET_SCAN_CHANNEL_TIME */
> >+ wlcmdint(ctlr, 0xbb, 0x28); /* SET_SCAN_UNASSOC_TIME */
> >+ wlcmdint(ctlr, 0x102, 0x82); /* SET_SCAN_PASSIVE_TIME */
> >+ wlcmdint(ctlr, 2, 0); /* UP */
> >+ memset(version, 0, sizeof version);
> >+ wlgetvar(ctlr, "ver", version, sizeof version - 1);
> >+ if((p = strchr(version, '\n')) != nil)
> >+ *p = '\0';
> >+ if(0) print("ether4330: %s\n", version);
> >+ wlsetint(ctlr, "roam_off", 1);
> >+ wlcmdint(ctlr, 0x14, 1); /* SET_INFRA 1 */
> >+ wlcmdint(ctlr, 10, 0); /* SET_PROMISC */
> >+ //wlcmdint(ctlr, 0x8e, 0); /* SET_BAND 0 */
> >+ //wlsetint(ctlr, "wsec", 1);
> >+ wlcmdint(ctlr, 2, 1); /* UP */
> >+ ctlr->keys[0].len = WMinKeyLen;
> >+ //wlwepkey(ctlr, 0);
> >+}
> >+
> >+/*
> >+ * Plan 9 driver interface
> >+ */
> >+
> >+static long
> >+etherbcmifstat(Ether* edev, void* a, long n, ulong offset)
> >+{
> >+ Ctlr *ctlr;
> >+ char *p;
> >+ int l;
> >+ static char *cryptoname[4] = {
> >+ [0] "off",
> >+ [Wep] "wep",
> >+ [Wpa] "wpa",
> >+ [Wpa2] "wpa2",
> >+ };
> >+ /* these strings are known by aux/wpa */
> >+ static char* connectstate[] = {
> >+ [Disconnected] = "unassociated",
> >+ [Connecting] = "connecting",
> >+ [Connected] = "associated",
> >+ };
> >+
> >+ ctlr = edev->ctlr;
> >+ if(ctlr == nil)
> >+ return 0;
> >+ p = malloc(READSTR);
> >+ l = 0;
> >+
> >+ l += snprint(p+l, READSTR-l, "channel: %d\n", ctlr->chanid);
> >+ l += snprint(p+l, READSTR-l, "essid: %s\n", ctlr->essid);
> >+ l += snprint(p+l, READSTR-l, "crypt: %s\n", cryptoname[ctlr->cryptotype]);
> >+ l += snprint(p+l, READSTR-l, "oq: %d\n", qlen(edev->oq));
> >+ l += snprint(p+l, READSTR-l, "txwin: %d\n", ctlr->txwindow);
> >+ l += snprint(p+l, READSTR-l, "txseq: %d\n", ctlr->txseq);
> >+ l += snprint(p+l, READSTR-l, "status: %s\n", connectstate[ctlr->status]);
> >+ USED(l);
> >+ n = readstr(offset, a, n, p);
> >+ free(p);
> >+ return n;
> >+}
> >+
> >+static void
> >+etherbcmtransmit(Ether *edev)
> >+{
> >+ Ctlr *ctlr;
> >+
> >+ ctlr = edev->ctlr;
> >+ if(ctlr == nil)
> >+ return;
> >+ txstart(edev);
> >+}
> >+
> >+static int
> >+parsehex(char *buf, int buflen, char *a)
> >+{
> >+ int i, k, n;
> >+
> >+ k = 0;
> >+ for(i = 0;k < buflen && *a; i++){
> >+ if(*a >= '0' && *a <= '9')
> >+ n = *a++ - '0';
> >+ else if(*a >= 'a' && *a <= 'f')
> >+ n = *a++ - 'a' + 10;
> >+ else if(*a >= 'A' && *a <= 'F')
> >+ n = *a++ - 'A' + 10;
> >+ else
> >+ break;
> >+
> >+ if(i & 1){
> >+ buf[k] |= n;
> >+ k++;
> >+ }
> >+ else
> >+ buf[k] = n<<4;
> >+ }
> >+ if(i & 1)
> >+ return -1;
> >+ return k;
> >+}
> >+
> >+static int
> >+wepparsekey(WKey* key, char* a)
> >+{
> >+ int i, k, len, n;
> >+ char buf[WMaxKeyLen];
> >+
> >+ len = strlen(a);
> >+ if(len == WMinKeyLen || len == WMaxKeyLen){
> >+ memset(key->dat, 0, sizeof(key->dat));
> >+ memmove(key->dat, a, len);
> >+ key->len = len;
> >+
> >+ return 0;
> >+ }
> >+ else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
> >+ k = 0;
> >+ for(i = 0; i < len; i++){
> >+ if(*a >= '0' && *a <= '9')
> >+ n = *a++ - '0';
> >+ else if(*a >= 'a' && *a <= 'f')
> >+ n = *a++ - 'a' + 10;
> >+ else if(*a >= 'A' && *a <= 'F')
> >+ n = *a++ - 'A' + 10;
> >+ else
> >+ return -1;
> >+
> >+ if(i & 1){
> >+ buf[k] |= n;
> >+ k++;
> >+ }
> >+ else
> >+ buf[k] = n<<4;
> >+ }
> >+
> >+ memset(key->dat, 0, sizeof(key->dat));
> >+ memmove(key->dat, buf, k);
> >+ key->len = k;
> >+
> >+ return 0;
> >+ }
> >+
> >+ return -1;
> >+}
> >+
> >+static int
> >+wpaparsekey(WKey *key, uvlong *ivp, char *a)
> >+{
> >+ int len;
> >+ char *e;
> >+
> >+ if(cistrncmp(a, "tkip:", 5) == 0 || cistrncmp(a, "ccmp:", 5) == 0)
> >+ a += 5;
> >+ else
> >+ return 1;
> >+ len = parsehex(key->dat, sizeof(key->dat), a);
> >+ if(len <= 0)
> >+ return 1;
> >+ key->len = len;
> >+ a += 2*len;
> >+ if(*a++ != '@')
> >+ return 1;
> >+ *ivp = strtoull(a, &e, 16);
> >+ if(e == a)
> >+ return -1;
> >+ return 0;
> >+}
> >+
> >+static void
> >+setauth(Ctlr *ctlr, Cmdbuf *cb, char *a)
> >+{
> >+ uchar wpaie[32];
> >+ int i;
> >+
> >+ i = parsehex((char*)wpaie, sizeof wpaie, a);
> >+ if(i < 2 || i != wpaie[1] + 2)
> >+ cmderror(cb, "bad wpa ie syntax");
> >+ if(wpaie[0] == 0xdd)
> >+ ctlr->cryptotype = Wpa;
> >+ else if(wpaie[0] == 0x30)
> >+ ctlr->cryptotype = Wpa2;
> >+ else
> >+ cmderror(cb, "bad wpa ie");
> >+ wlsetvar(ctlr, "wpaie", wpaie, i);
> >+ if(ctlr->cryptotype == Wpa){
> >+ wlsetint(ctlr, "wpa_auth", 4|2); /* auth_psk | auth_unspecified */
> >+ wlsetint(ctlr, "auth", 0);
> >+ wlsetint(ctlr, "wsec", 2); /* tkip */
> >+ wlsetint(ctlr, "wpa_auth", 4); /* auth_psk */
> >+ }else{
> >+ wlsetint(ctlr, "wpa_auth", 0x80|0x40); /* auth_psk | auth_unspecified */
> >+ wlsetint(ctlr, "auth", 0);
> >+ wlsetint(ctlr, "wsec", 4); /* aes */
> >+ wlsetint(ctlr, "wpa_auth", 0x80); /* auth_psk */
> >+ }
> >+}
> >+
> >+static int
> >+setcrypt(Ctlr *ctlr, Cmdbuf*, char *a)
> >+{
> >+ if(cistrcmp(a, "wep") == 0 || cistrcmp(a, "on") == 0)
> >+ ctlr->cryptotype = Wep;
> >+ else if(cistrcmp(a, "off") == 0 || cistrcmp(a, "none") == 0)
> >+ ctlr->cryptotype = 0;
> >+ else
> >+ return 0;
> >+ wlsetint(ctlr, "auth", ctlr->cryptotype);
> >+ return 1;
> >+}
> >+
> >+static long
> >+etherbcmctl(Ether* edev, void* buf, long n)
> >+{
> >+ Ctlr *ctlr;
> >+ Cmdbuf *cb;
> >+ Cmdtab *ct;
> >+ uchar ea[Eaddrlen];
> >+ uvlong iv;
> >+ int i;
> >+
> >+ if((ctlr = edev->ctlr) == nil)
> >+ error(Enonexist);
> >+ USED(ctlr);
> >+
> >+ cb = parsecmd(buf, n);
> >+ if(waserror()){
> >+ free(cb);
> >+ nexterror();
> >+ }
> >+ ct = lookupcmd(cb, cmds, nelem(cmds));
> >+ switch(ct->index){
> >+ case CMauth:
> >+ setauth(ctlr, cb, cb->f[1]);
> >+ if(ctlr->essid[0])
> >+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
> >+ break;
> >+ case CMchannel:
> >+ if((i = atoi(cb->f[1])) < 0 || i > 16)
> >+ cmderror(cb, "bad channel number");
> >+ //wlcmdint(ctlr, 30, i); /* SET_CHANNEL */
> >+ ctlr->chanid = i;
> >+ break;
> >+ case CMcrypt:
> >+ if(setcrypt(ctlr, cb, cb->f[1])){
> >+ if(ctlr->essid[0])
> >+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
> >+ }else
> >+ cmderror(cb, "bad crypt type");
> >+ break;
> >+ case CMessid:
> >+ if(cistrcmp(cb->f[1], "default") == 0)
> >+ memset(ctlr->essid, 0, sizeof(ctlr->essid));
> >+ else{
> >+ strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid) - 1);
> >+ ctlr->essid[sizeof(ctlr->essid) - 1] = '\0';
> >+ }
> >+ if(!waserror()){
> >+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
> >+ poperror();
> >+ }
> >+ break;
> >+ case CMjoin: /* join essid channel wep|on|off|wpakey */
> >+ if(strcmp(cb->f[1], "") != 0){ /* empty string for no change */
> >+ if(cistrcmp(cb->f[1], "default") != 0){
> >+ strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid)-1);
> >+ ctlr->essid[sizeof(ctlr->essid)-1] = 0;
> >+ }else
> >+ memset(ctlr->essid, 0, sizeof(ctlr->essid));
> >+ }else if(ctlr->essid[0] == 0)
> >+ cmderror(cb, "essid not set");
> >+ if((i = atoi(cb->f[2])) >= 0 && i <= 16)
> >+ ctlr->chanid = i;
> >+ else
> >+ cmderror(cb, "bad channel number");
> >+ if(!setcrypt(ctlr, cb, cb->f[3]))
> >+ setauth(ctlr, cb, cb->f[3]);
> >+ if(ctlr->essid[0])
> >+ wljoin(ctlr, ctlr->essid, ctlr->chanid);
> >+ break;
> >+ case CMkey1:
> >+ case CMkey2:
> >+ case CMkey3:
> >+ case CMkey4:
> >+ i = ct->index - CMkey1;
> >+ if(wepparsekey(&ctlr->keys[i], cb->f[1]))
> >+ cmderror(cb, "bad WEP key syntax");
> >+ wlsetint(ctlr, "wsec", 1); /* wep enabled */
> >+ wlwepkey(ctlr, i);
> >+ break;
> >+ case CMrxkey:
> >+ case CMrxkey0:
> >+ case CMrxkey1:
> >+ case CMrxkey2:
> >+ case CMrxkey3:
> >+ case CMtxkey:
> >+ if(parseether(ea, cb->f[1]) < 0)
> >+ cmderror(cb, "bad ether addr");
> >+ if(wpaparsekey(&ctlr->keys[0], &iv, cb->f[2]))
> >+ cmderror(cb, "bad wpa key");
> >+ wlwpakey(ctlr, ct->index, iv, ea);
> >+ break;
> >+ case CMdebug:
> >+ iodebug = atoi(cb->f[1]);
> >+ break;
> >+ }
> >+ poperror();
> >+ free(cb);
> >+ return n;
> >+}
> >+
> >+static void
> >+etherbcmscan(void *a, uint secs)
> >+{
> >+ Ether* edev;
> >+ Ctlr* ctlr;
> >+
> >+ edev = a;
> >+ ctlr = edev->ctlr;
> >+ ctlr->scansecs = secs;
> >+}
> >+
> >+static void
> >+etherbcmattach(Ether* edev)
> >+{
> >+ Ctlr *ctlr;
> >+
> >+ ctlr = edev->ctlr;
> >+ qlock(&ctlr->alock);
> >+ if(waserror()){
> >+ //print("ether4330: attach failed: %s\n", up->errstr);
> >+ qunlock(&ctlr->alock);
> >+ nexterror();
> >+ }
> >+ if(ctlr->edev == nil){
> >+ if(ctlr->chipid == 0){
> >+ sdioinit();
> >+ sbinit(ctlr);
> >+ }
> >+ fwload(ctlr);
> >+ sbenable(ctlr);
> >+ kproc("wifireader", rproc, edev);
> >+ kproc("wifitimer", lproc, edev);
> >+ if(ctlr->regufile)
> >+ reguload(ctlr, ctlr->regufile);
> >+ wlinit(edev, ctlr);
> >+ ctlr->edev = edev;
> >+ }
> >+ qunlock(&ctlr->alock);
> >+ poperror();
> >+}
> >+
> >+static void
> >+etherbcmshutdown(Ether*)
> >+{
> >+ sdioreset();
> >+}
> >+
> >+
> >+static int
> >+etherbcmpnp(Ether* edev)
> >+{
> >+ static Ctlr *ctlr;
> >+
> >+ if(ctlr != nil)
> >+ return -1;
> >+
> >+ ctlr = malloc(sizeof(Ctlr));
> >+ ctlr->chanid = Wifichan;
> >+ edev->ctlr = ctlr;
> >+ edev->attach = etherbcmattach;
> >+ edev->transmit = etherbcmtransmit;
> >+ edev->ifstat = etherbcmifstat;
> >+ edev->ctl = etherbcmctl;
> >+ edev->scanbs = etherbcmscan;
> >+ edev->shutdown = etherbcmshutdown;
> >+ edev->arg = edev;
> >+
> >+ return 0;
> >+}
> >+
> >+void
> >+ether4330link(void)
> >+{
> >+ addethercard("4330", etherbcmpnp);
> >+}
> >--- a/sys/src/9/bcm64/pi4 Sun Oct 22 09:21:23 2023
> >+++ b/sys/src/9/bcm64/pi4 Sun Oct 22 10:53:02 2023
> >@@ -32,6 +32,7 @@
> > archbcm4 pci
> > usbxhcipci pci usbxhci archbcm4
> > ethergenet ethermii
> >+ ether4330 emmc
> > ethermedium
> > loopbackmedium
> > netdevmedium
> >--- a/sys/src/9/port/sd.h Sun Oct 22 09:21:23 2023
> >+++ b/sys/src/9/port/sd.h Sun Oct 22 10:53:02 2023
> >@@ -164,6 +164,31 @@
> > char *name;
> > };
> >
> >+/* Commands */
> >+extern SDiocmd GO_IDLE_STATE;
> >+extern SDiocmd SEND_OP_COND;
> >+extern SDiocmd ALL_SEND_CID;
> >+extern SDiocmd SET_RELATIVE_ADDR;
> >+extern SDiocmd SEND_RELATIVE_ADDR;
> >+extern SDiocmd SWITCH;
> >+extern SDiocmd SWITCH_FUNC;
> >+extern SDiocmd SELECT_CARD;
> >+extern SDiocmd SEND_EXT_CSD;
> >+extern SDiocmd SD_SEND_IF_COND;
> >+extern SDiocmd SEND_CSD;
> >+extern SDiocmd STOP_TRANSMISSION;
> >+extern SDiocmd SEND_STATUS;
> >+extern SDiocmd SET_BLOCKLEN;
> >+extern SDiocmd READ_SINGLE_BLOCK;
> >+extern SDiocmd READ_MULTIPLE_BLOCK;
> >+extern SDiocmd WRITE_SINGLE_BLOCK;
> >+extern SDiocmd WRITE_MULTIPLE_BLOCK;
> >+
> >+/* prefix for following app-specific commands */
> >+extern SDiocmd APP_CMD;
> >+extern SDiocmd SD_SET_BUS_WIDTH;
> >+extern SDiocmd SD_SEND_OP_COND;
> >+
> > struct SDio {
> > char *name;
> > int (*init)(SDio*);
> >
> >
>
> let me rephrase: is this known to actually work, and which hardware does it actually work with. asking for a fqa.
>
> sl
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2023-10-22 19:06 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <1734B8F2A873B2D850DB1C299ECACF8F@9front.org>
2023-10-22 18:43 ` [9front] Fwd: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org) Stanley Lieber
2023-10-22 18:45 ` [9front] " Stanley Lieber
2023-10-22 19:01 ` adventures in9
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).