From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=ham autolearn_force=no version=3.4.4 Received: (qmail 14814 invoked from network); 22 Oct 2023 18:45:00 -0000 Received: from 9front.inri.net (168.235.81.73) by inbox.vuxu.org with ESMTPUTF8; 22 Oct 2023 18:45:00 -0000 Received: from gaff.inri.net ([168.235.71.243]) by 9front; Sun Oct 22 14:43:43 -0400 2023 Received: from [127.0.0.1] ([168.235.81.125]) by gaff; Sun Oct 22 14:43:43 -0400 2023 Date: Sun, 22 Oct 2023 14:43:42 -0400 From: Stanley Lieber To: 9front@9front.org In-Reply-To: <1734B8F2A873B2D850DB1C299ECACF8F@9front.org> References: <1734B8F2A873B2D850DB1C299ECACF8F@9front.org> Message-ID: <4C53B52B-3A22-46DF-8CE1-80BD3543F6EF@stanleylieber.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: standard-based database factory Subject: [9front] =?US-ASCII?Q?Fwd=3A_=5B9front-commits=5D_bcm=3A_add_wifi_d?= =?US-ASCII?Q?river_=28keegan=40undefinedbehaviour=2Eorg=29?= Reply-To: 9front@9front.org Precedence: bulk this is for rpi, yes? is there a list of specific hardware this is known to= work with? sl -------- Original Message -------- From: commits@git=2E9front=2Eorg Sent: October 22, 2023 10:53:02 AM EDT To: 9front-commits@9front=2Eorg Subject: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour= =2Eorg) Thanks Richard Miller --- diff f2cfee358f329519e913a20142d96b1e0029633c fb96a050f8a9d5f23da3557ba892= 51ffb73889bf --- a/sys/src/9/bcm/emmc=2Ec Sun Oct 22 09:21:23 2023 +++ b/sys/src/9/bcm/emmc=2Ec Sun Oct 22 10:53:02 2023 @@ -129,6 +129,7 @@ =20 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); } =20 +int +sdiocardintr(int wait) +{ + u32int *r =3D (u32int*)EMMCREGS; + int i; + + WR(Interrupt, Cardintr); + while(((i =3D r[Interrupt]) & Cardintr) =3D=3D 0){ + if(!wait) + return 0; + WR(Irpten, r[Irpten] | Cardintr); + sleep(&emmc=2Ecardr, cardintready, 0); + } + WR(Interrupt, Cardintr); + return i; +} + static int emmccmd(SDio*, SDiocmd *cmd, u32int arg, u32int *resp) { @@ -434,21 +452,25 @@ i =3D r[Interrupt]; if(i&(Datadone|Err)) wakeup(&emmc=2Er); + if(i&Cardintr) + wakeup(&emmc=2Ecardr); WR(Irpten, r[Irpten] & ~i); } =20 + +SDio sdio =3D { + "emmc", + emmcinit, + emmcenable, + emmcinquiry, + emmccmd, + emmciosetup, + emmcio, + emmcbus, +}; + void emmclink(void) { - static SDio io =3D { - "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=2Ec Sun Oct 22 10:53:02 2023 @@ -0,0 +1,2411 @@ +/* + * Broadcom bcm4330 wifi (sdio interface) + */ + +#include "u=2Eh" +#include "=2E=2E/port/lib=2Eh" +#include "mem=2Eh" +#include "dat=2Eh" +#include "fns=2Eh" +#include "io=2Eh" +#include "=2E=2E/port/error=2Eh" +#include "=2E=2E/port/netif=2Eh" +#include "=2E=2E/port/sd=2Eh" +#include "=2E=2E/port/etherif=2Eh" + +#define CACHELINESZ 64 /* temp */ + +extern SDio sdio; +extern int sdiocardintr(int); + +SDiocmd IO_SEND_OP_COND =3D { 5, 3, 0, 0, "IO_SEND_OP_COND" }; +SDiocmd IO_RW_DIRECT =3D { 52, 1, 0, 0, "IO_RW_DIRECT" }; + +enum{ + SDIODEBUG =3D 0, + SBDEBUG =3D 0, + EVENTDEBUG =3D 0, + VARDEBUG =3D 0, + FWDEBUG =3D 0, + + Corescansz =3D 512, + Uploadsz =3D 2048, + Sdpcmsz =3D 12, /* sizeof(Sdpcmsz) */ + Cmdsz =3D 16, /* sizeof(Cmd) */ + + SDfreq =3D 25*Mhz, /* standard SD frequency */ + SDfreqhs =3D 50*Mhz, /* high speed frequency */ + + Wifichan =3D 0, /* default channel */ + Firmwarecmp =3D 1, + + ARMcm3 =3D 0x82A, + ARM7tdmi =3D 0x825, + ARMcr4 =3D 0x83E, + + Fn0 =3D 0, + Fn1 =3D 1, + Fn2 =3D 2, + Fbr1 =3D 0x100, + Fbr2 =3D 0x200, + + /* CCCR */ + Ioenable =3D 0x02, + Ioready =3D 0x03, + Intenable =3D 0x04, + Intpend =3D 0x05, + Ioabort =3D 0x06, + Busifc =3D 0x07, + Capability =3D 0x08, + Blksize =3D 0x10, + Highspeed =3D 0x13, + + /* SELECT_CARD args */ + Rcashift =3D 16, + + /* SEND_OP_COND args */ + Hcs =3D 1<<30, /* host supports SDHC & SDXC */ + V3_3 =3D 3<<20, /* 3=2E2-3=2E4 volts */ + V2_8 =3D 3<<15, /* 2=2E7-2=2E9 volts */ + V2_0 =3D 1<<8, /* 2=2E0-2=2E1 volts */ + S18R =3D 1<<24, /* switch to 1=2E8V request */ + + /* Sonics Silicon Backplane (access to cores on chip) */ + Sbwsize =3D 0x8000, + Sb32bit =3D 0x8000, + Sbaddr =3D 0x1000a, + Enumbase =3D 0x18000000, + Framectl=3D 0x1000d, + Rfhalt =3D 0x01, + Wfhalt =3D 0x02, + Clkcsr =3D 0x1000e, + ForceALP =3D 0x01, /* active low-power clock */ + ForceHT =3D 0x02, /* high throughput clock */ + ForceILP =3D 0x04, /* idle low-power clock */ + ReqALP =3D 0x08, + ReqHT =3D 0x10, + Nohwreq =3D 0x20, + ALPavail =3D 0x40, + HTavail =3D 0x80, + Pullups =3D 0x1000f, + Wfrmcnt =3D 0x10019, + Rfrmcnt =3D 0x1001b, + =09 + /* core control regs */ + Ioctrl =3D 0x408, + Resetctrl =3D 0x800, + + /* socram regs */ + Coreinfo =3D 0x00, + Bankidx =3D 0x10, + Bankinfo =3D 0x40, + Bankpda =3D 0x44, + + /* armcr4 regs */ + Cr4Cap =3D 0x04, + Cr4Bankidx =3D 0x40, + Cr4Bankinfo =3D 0x44, + Cr4Cpuhalt =3D 0x20, + + /* chipcommon regs */ + Gpiopullup =3D 0x58, + Gpiopulldown =3D 0x5c, + Chipctladdr =3D 0x650, + Chipctldata =3D 0x654, + + /* sdio core regs */ + Intstatus =3D 0x20, + Fcstate =3D 1<<4, + Fcchange =3D 1<<5, + FrameInt =3D 1<<6, + MailboxInt =3D 1<<7, + Intmask =3D 0x24, + Sbmbox =3D 0x40, + Sbmboxdata =3D 0x48, + Hostmboxdata=3D 0x4c, + Fwready =3D 0x80, + + /* wifi control commands */ + GetVar =3D 262, + SetVar =3D 263, + + /* status */ + Disconnected=3D 0, + Connecting, + Connected, +}; + +typedef struct Ctlr Ctlr; + +enum{ + Wpa =3D 1, + Wep =3D 2, + Wpa2 =3D 3, + WNameLen =3D 32, + WNKeys =3D 4, + WKeyLen =3D 32, + WMinKeyLen =3D 5, + WMaxKeyLen =3D 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[] =3D { + {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[] =3D "bcmdhd=2Ecal=2E40181"; +static char config40183[] =3D "bcmdhd=2Ecal=2E40183=2E26MHz"; + +struct { + int chipid; + int chiprev; + char *fwfile; + char *cfgfile; + char *regufile; +} firmware[] =3D { + { 0x4330, 3, "fw_bcm40183b1=2Ebin", config40183, 0 }, + { 0x4330, 4, "fw_bcm40183b2=2Ebin", config40183, 0 }, + { 43362, 0, "fw_bcm40181a0=2Ebin", config40181, 0 }, + { 43362, 1, "fw_bcm40181a2=2Ebin", config40181, 0 }, + { 43430, 1, "brcmfmac43430-sdio=2Ebin", "brcmfmac43430-sdio=2Etxt", 0 }, + { 43430, 2, "brcmfmac43436-sdio=2Ebin", "brcmfmac43436-sdio=2Etxt", "br= cmfmac43436-sdio=2Eclm_blob" }, + { 0x4345, 6, "brcmfmac43455-sdio=2Ebin", "brcmfmac43455-sdio=2Etxt", "br= cmfmac43455-sdio=2Eclm_blob" }, + { 0x4345, 9, "brcmfmac43456-sdio=2Ebin", "brcmfmac43456-sdio=2Etxt", "br= cmfmac43456-sdio=2Eclm_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] =3D v; + p[1] =3D v >> 8; + return p + 2; +} + +static uchar* +put4(uchar *p, long v) +{ + p[0] =3D v; + p[1] =3D v >> 8; + p[2] =3D v >> 16; + p[3] =3D 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 =3D a; + print("%s:", s); + for(i =3D 0; i < n; i++) + print("%c%2=2E2x", i&15? ' ' : '\n', *p++); + print("\n"); +} + +/* + * SDIO communication with dongle + */ +static ulong +sdiocmd_locked(SDiocmd *cmd, ulong arg) +{ + u32int resp[4]; + + sdio=2Ecmd(&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 =3D sdiocmd_locked(cmd, arg); + qunlock(&sdiolock); + poperror(); + return r; + +} + +static ulong +trysdiocmd(SDiocmd *cmd, ulong arg) +{ + ulong r; + + if(waserror()) + return 0; + r =3D sdiocmd(cmd, arg); + poperror(); + return r; +} + +static int +sdiord(int fn, int addr) +{ + int r; + + r =3D sdiocmd(&IO_RW_DIRECT, (0<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)); + if(r & 0xCF00){ + print("ether4330: sdiord(%x, %x) fail: %2=2E2ux %2=2E2ux\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 =3D 0; + for(retry =3D 0; retry < 10; retry++){ + arg =3D (1<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)|(data&0xFF); + if((arg & ~0xFF) =3D=3D (1<<31|0<<28|7<<9)){ + switch(arg&3){ + case 0: + sdio=2Ebus(&sdio, 1, 0); + break; + case 2: + sdio=2Ebus(&sdio, 4, 0); + break; + } + } + r =3D sdiocmd(&IO_RW_DIRECT, arg); + if((r & 0xCF00) =3D=3D 0) + return; + } + print("ether4330: sdiowr(%x, %x, %x) fail: %2=2E2ux %2=2E2ux\n", fn, add= r, 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 =3D { 53, 1, 0, 0, "IO_RW_EXTENDED" }; + + bsize =3D fn =3D=3D Fn2? 512 : 64; + while(len > 0){ + if(len >=3D 511*bsize){ + blk =3D 1; + bcount =3D 511; + m =3D bcount*bsize; + }else if(len > bsize){ + blk =3D 1; + bcount =3D len/bsize; + m =3D bcount*bsize; + }else{ + blk =3D 0; + bcount =3D len; + m =3D bcount; + } + qlock(&sdiolock); + if(waserror()){ + print("ether4330: sdiorwext fail: %s\n", up->errstr); + qunlock(&sdiolock); + nexterror(); + } + cmd=2Edata =3D write? 2 : 1; /* Host2card : Card2host */ + if(blk){ + cmd=2Edata +=3D 2; /* Multiblock | Blkcnten */ + sdio=2Eiosetup(&sdio, write, a, bsize, bcount); + }else + sdio=2Eiosetup(&sdio, write, a, bcount, 1); + sdiocmd_locked(&cmd, write<<31 | (fn&7)<<28 | blk<<27 | incr<<26 | (add= r&0x1FFFF)<<9 | (bcount&0x1FF)); + sdio=2Eio(&sdio, write, a, m); + qunlock(&sdiolock); + poperror(); + len -=3D m; + a =3D (char*)a + m; + if(incr) + addr +=3D m; + } +} + +static void +sdioset(int fn, int addr, int bits) +{ + sdiowr(fn, addr, sdiord(fn, addr) | bits); +} +=09 +static void +sdioinit(void) +{ + ulong ocr, rca; + int i; + + /* disconnect emmc from SD card (connect sdhost instead) */ + for(i =3D 48; i <=3D 53; i++) + gpiosel(i, Alt0); + /* connect emmc to wifi */ + for(i =3D 34; i <=3D 39; i++){ + gpiosel(i, Alt3); + if(i =3D=3D 34) + gpiopulloff(i); + else + gpiopullup(i); + } + sdio=2Einit(&sdio); + sdio=2Eenable(&sdio); + sdiocmd(&GO_IDLE_STATE, 0); + ocr =3D trysdiocmd(&IO_SEND_OP_COND, 0); + i =3D 0; + while((ocr & (1<<31)) =3D=3D 0){ + if(++i > 5){ + print("ether4330: no response to sdio access: ocr =3D %lux\n", ocr); + error(Eio); + } + ocr =3D trysdiocmd(&IO_SEND_OP_COND, V3_3); + tsleep(&up->sleep, return0, nil, 100); + } + rca =3D sdiocmd(&SEND_RELATIVE_ADDR, 0) >> Rcashift; + sdiocmd(&SELECT_CARD, rca << Rcashift); + sdio=2Ebus(&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<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 =3D (uchar*)ROUND((uintptr)cbuf, CACHELINESZ); + memset(p, 0, 4); + sdiorwext(fn, 0, p, 4, off|Sb32bit, 1); + if(SDIODEBUG) print("cfgreadl %lux: %2=2E2x %2=2E2x %2=2E2x %2=2E2x\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 =3D (uchar*)ROUND((uintptr)cbuf, CACHELINESZ); + put4(p, data); + if(SDIODEBUG) print("cfgwritel %lux: %2=2E2x %2=2E2x %2=2E2x %2=2E2x\n",= off, p[0], p[1], p[2], p[3]); + retry =3D 0; + while(waserror()){ + print("ether4330: cfgwritel retry %lux %ux\n", off, data); + sdioabort(fn); + if(++retry =3D=3D 3) + nexterror(); + } + sdiorwext(fn, 1, p, 4, off|Sb32bit, 1); + poperror(); +} + +static void +sbwindow(ulong addr) +{ + addr &=3D ~(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 >=3D 4){ + n =3D len; + n &=3D ~3; + sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1); + off +=3D n; + buf +=3D n; + len -=3D n; + } + while(len > 0){ + sdiowr(Fn1, off|Sb32bit, *buf); + off++; + buf++; + len--; + } + }else{ + if(len >=3D 4){ + n =3D len; + n &=3D ~3; + sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1); + off +=3D n; + buf +=3D n; + len -=3D n; + } + while(len > 0){ + *buf =3D sdiord(Fn1, off|Sb32bit); + off++; + buf++; + len--; + } + } + poperror(); +} + +static void +sbmem(int write, uchar *buf, int len, ulong off) +{ + ulong n; + + n =3D ROUNDUP(off, Sbwsize) - off; + if(n =3D=3D 0) + n =3D Sbwsize; + while(len > 0){ + if(n > len) + n =3D len; + sbwindow(off); + sbrw(Fn1, write, buf, n, off & (Sbwsize-1)); + off +=3D n; + buf +=3D n; + len -=3D n; + n =3D Sbwsize; + } +} + +static void +packetrw(int write, uchar *buf, int len) +{ + int n; + int retry; + + n =3D 2048; + while(len > 0){ + if(n > len) + n =3D ROUND(len, 4); + retry =3D 0; + while(waserror()){ + sdioabort(Fn2); + if(++retry =3D=3D 3) + nexterror(); + } + sdiorwext(Fn2, write, buf, n, Enumbase, 0); + poperror(); + buf +=3D n; + len -=3D 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) !=3D 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) =3D=3D 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) !=3D 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 =3D sdmalloc(Corescansz); + if(buf =3D=3D nil) + error(Enomem); + sbmem(0, buf, Corescansz, r); + coreid =3D 0; + corerev =3D 0; + for(i =3D 0; i < Corescansz; i +=3D 4){ + switch(buf[i]&0xF){ + case 0xF: /* end */ + sdfree(buf); + return; + case 0x1: /* core info */ + if((buf[i+4]&0xF) !=3D 0x1) + break; + coreid =3D (buf[i+1] | buf[i+2]<<8) & 0xFFF; + i +=3D 4; + corerev =3D buf[i+3]; + break; + case 0x05: /* address */ + addr =3D buf[i+1]<<8 | buf[i+2]<<16 | buf[i+3]<<24; + addr &=3D ~0xFFF; + if(SBDEBUG) print("core %x %s %#lux\n", coreid, buf[i]&0xC0? "ctl" : "= mem", addr); + switch(coreid){ + case 0x800: + if((buf[i] & 0xC0) =3D=3D 0) + ctl->chipcommon =3D addr; + break; + case ARMcm3: + case ARM7tdmi: + case ARMcr4: + ctl->armcore =3D coreid; + if(buf[i] & 0xC0){ + if(ctl->armctl =3D=3D 0) + ctl->armctl =3D addr; + }else{ + if(ctl->armregs =3D=3D 0) + ctl->armregs =3D addr; + } + break; + case 0x80E: + if(buf[i] & 0xC0) + ctl->socramctl =3D addr; + else if(ctl->socramregs =3D=3D 0) + ctl->socramregs =3D addr; + ctl->socramrev =3D corerev; + break; + case 0x829: + if((buf[i] & 0xC0) =3D=3D 0) + ctl->sdregs =3D addr; + ctl->sdiorev =3D corerev; + break; + case 0x812: + if(buf[i] & 0xC0) + ctl->d11ctl =3D addr; + break; + } + } + } + sdfree(buf); +} + +static void +ramscan(Ctlr *ctl) +{ + ulong r, n, size; + int banks, i; + + if(ctl->armcore =3D=3D ARMcr4){ + r =3D ctl->armregs; + sbwindow(r); + n =3D cfgreadl(Fn1, r + Cr4Cap); + if(SBDEBUG) print("cr4 banks %lux\n", n); + banks =3D ((n>>4) & 0xF) + (n & 0xF); + size =3D 0; + for(i =3D 0; i < banks; i++){ + cfgwritel(Fn1, r + Cr4Bankidx, i); + n =3D cfgreadl(Fn1, r + Cr4Bankinfo); + if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0= x3F) + 1)); + size +=3D 8192 * ((n & 0x3F) + 1); + } + ctl->socramsize =3D size; + ctl->rambase =3D 0x198000; + return; + } + if(ctl->socramrev <=3D 7 || ctl->socramrev =3D=3D 12){ + print("ether4330: SOCRAM rev %d not supported\n", ctl->socramrev); + error(Eio); + } + sbreset(ctl->socramctl, 0, 0); + r =3D ctl->socramregs; + sbwindow(r); + n =3D cfgreadl(Fn1, r + Coreinfo); + if(SBDEBUG) print("socramrev %d coreinfo %lux\n", ctl->socramrev, n); + banks =3D (n>>4) & 0xF; + size =3D 0; + for(i =3D 0; i < banks; i++){ + cfgwritel(Fn1, r + Bankidx, i); + n =3D cfgreadl(Fn1, r + Bankinfo); + if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x= 3F) + 1)); + size +=3D 8192 * ((n & 0x3F) + 1); + } + ctl->socramsize =3D size; + ctl->rambase =3D 0; + if(ctl->chipid =3D=3D 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 =3D cfgreadl(Fn1, Enumbase); + chipid =3D 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 =3D chipid; + ctl->chiprev =3D (r>>16)&0xF; + break; + default: + print("ether4330: chipid %#x (%d) not supported\n", chipid, chipid); + error(Eio); + } + r =3D cfgreadl(Fn1, Enumbase + 63*4); + corescan(ctl, r); + if(ctl->armctl =3D=3D 0 || ctl->d11ctl =3D=3D 0 || + (ctl->armcore =3D=3D ARMcm3 && (ctl->socramctl =3D=3D 0 || ctl->socra= mregs =3D=3D 0))) + error("corescan didn't find essential cores\n"); + if(ctl->armcore =3D=3D ARMcr4) + sbreset(ctl->armctl, Cr4Cpuhalt, Cr4Cpuhalt); + else=09 + 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->socrams= ize, ctl->rambase); + cfgw(Clkcsr, 0); + microdelay(10); + if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr)); + cfgw(Clkcsr, Nohwreq | ReqALP); + while((cfgr(Clkcsr) & (HTavail|ALPavail)) =3D=3D 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 !=3D 0x4330 && ctl->chipid !=3D 43362) + return; + cfgwritel(Fn1, ctl->chipcommon + Chipctladdr, 1); + if(cfgreadl(Fn1, ctl->chipcommon + Chipctladdr) !=3D 1) + print("ether4330: can't set Chipctladdr\n"); + else{ + r =3D cfgreadl(Fn1, ctl->chipcommon + Chipctldata); + if(SBDEBUG) print("chipcommon PMU (%lux) %lux", cfgreadl(Fn1, ctl->chip= common + Chipctladdr), r); + /* set SDIO drive strength >=3D 6mA */ + r &=3D ~0x3800; + if(ctl->chipid =3D=3D 0x4330) + r |=3D 3<<11; + else + r |=3D 7<<11; + cfgwritel(Fn1, ctl->chipcommon + Chipctldata, r); + if(SBDEBUG) print("-> %lux (=3D %lux)\n", r, cfgreadl(Fn1, ctl->chipcom= mon + Chipctldata)); + } +} + +static void +sbenable(Ctlr *ctl) +{ + int i; + + if(SBDEBUG) print("enabling HT clock=2E=2E=2E"); + cfgw(Clkcsr, 0); + delay(1); + cfgw(Clkcsr, ReqHT); + for(i =3D 0; (cfgr(Clkcsr) & HTavail) =3D=3D 0; i++){ + if(i =3D=3D 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<sleep, return0, nil, 100); + } + sdiowr(Fn0, Intenable, (1<genbuf, sizeof up->genbuf, "can't find %s in /boot or /lib/= firmware", file); + error(up->genbuf); + } + return c;=09 +} + +static int +upload(Ctlr *ctl, char *file, int isconfig) +{ + Chan *c; + uchar *buf; + uchar *cbuf; + int off, n; + + buf =3D cbuf =3D nil; + c =3D findfirmware(file); + if(waserror()){ + cclose(c); + sdfree(buf); + sdfree(cbuf); + nexterror(); + } + buf =3D sdmalloc(Uploadsz); + if(buf =3D=3D nil) + error(Enomem); + if(Firmwarecmp){ + cbuf =3D sdmalloc(Uploadsz); + if(cbuf =3D=3D nil) + error(Enomem); + } + off =3D 0; + for(;;){ + n =3D devtab[c->type]->read(c, buf, Uploadsz, off); + if(n <=3D 0) + break; + if(isconfig){ + n =3D condense(buf, n); + off =3D ctl->socramsize - n - 4; + }else if(off =3D=3D 0) + memmove(ctl->resetvec=2Ec, buf, sizeof(ctl->resetvec=2Ec)); + while(n&3) + buf[n++] =3D 0; + sbmem(1, buf, n, ctl->rambase + off); + if(isconfig) + break; + off +=3D n; + } + if(Firmwarecmp){ + if(FWDEBUG) print("compare=2E=2E=2E"); + if(!isconfig) + off =3D 0; + for(;;){ + if(!isconfig){ + n =3D devtab[c->type]->read(c, buf, Uploadsz, off); + if(n <=3D 0) + break; + while(n&3) + buf[n++] =3D 0; + } + sbmem(0, cbuf, n, ctl->rambase + off); + if(memcmp(buf, cbuf, n) !=3D 0){ + print("ether4330: firmware load failed offset %d\n", off); + error(Eio); + } + if(isconfig) + break; + off +=3D n; + } + } + if(FWDEBUG) print("\n"); + poperror(); + cclose(c); + sdfree(buf); + sdfree(cbuf); + return n; +} + +/* + * Upload regulatory file (=2Eclm) to firmware=2E + * 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 =3D 2+2+4+4, + Regusz =3D 1400, + Regutyp =3D 2, + Flagclm =3D 1<<12, + Firstpkt=3D 1<<1, + Lastpkt =3D 1<<2, + }; + + buf =3D nil; + c =3D findfirmware(file); + if(waserror()){ + cclose(c); + free(buf); + nexterror(); + } + buf =3D malloc(Reguhdr+Regusz+1); + if(buf =3D=3D nil) + error(Enomem); + put2(buf+2, Regutyp); + put2(buf+8, 0); + off =3D 0; + flag =3D Flagclm | Firstpkt; + while((flag&Lastpkt) =3D=3D 0){ + n =3D devtab[c->type]->read(c, buf+Reguhdr, Regusz+1, off); + if(n <=3D 0) + break; + if(n =3D=3D Regusz+1) + --n; + else{ + while(n&7) + buf[Reguhdr+n++] =3D 0; + flag |=3D Lastpkt; + } + put2(buf+0, flag); + put4(buf+4, n); + wlsetvar(ctl, "clmload", buf, Reguhdr + n); + off +=3D n; + flag &=3D ~Firstpkt; + } + poperror(); + cclose(c); + free(buf); +} + +static void +fwload(Ctlr *ctl) +{ + uchar buf[4]; + uint i, n; + + i =3D 0; + while(firmware[i]=2Echipid !=3D ctl->chipid || + firmware[i]=2Echiprev !=3D ctl->chiprev){ + if(++i =3D=3D nelem(firmware)){ + print("ether4330: no firmware for chipid %x (%d) chiprev %d\n", + ctl->chipid, ctl->chipid, ctl->chiprev); + error("no firmware"); + } + } + ctl->regufile =3D firmware[i]=2Eregufile; + cfgw(Clkcsr, ReqALP); + while((cfgr(Clkcsr) & ALPavail) =3D=3D 0) + microdelay(10); + memset(buf, 0, 4); + sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4); + if(FWDEBUG) print("firmware load=2E=2E=2E"); + upload(ctl, firmware[i]=2Efwfile, 0); + if(FWDEBUG) print("config load=2E=2E=2E"); + n =3D upload(ctl, firmware[i]=2Ecfgfile, 1); + n /=3D 4; + n =3D (n & 0xFFFF) | (~n << 16); + put4(buf, n); + sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4); + if(ctl->armcore =3D=3D ARMcr4){ + sbwindow(ctl->sdregs); + cfgwritel(Fn1, ctl->sdregs + Intstatus, ~0); + if(ctl->resetvec=2Ei !=3D 0){ + if(SBDEBUG) print("%ux\n", ctl->resetvec=2Ei); + sbmem(1, ctl->resetvec=2Ec, sizeof(ctl->resetvec=2Ec), 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 =3D sdiord(Fn0, Intpend); + if(i =3D=3D 0){ + tsleep(&up->sleep, return0, 0, 10); + continue; + } + ints =3D cfgreadl(Fn1, ctlr->sdregs + Intstatus); + cfgwritel(Fn1, ctlr->sdregs + Intstatus, ints); + if(0) print("INTS: (%x) %lux -> %lux\n", i, ints, cfgreadl(Fn1, ctlr->s= dregs + Intstatus)); + if(ints & MailboxInt){ + mbox =3D 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 =3D allocb(2048); + p =3D (Sdpcm*)b->wp; + qlock(&ctl->pktlock); + for(;;){ + packetrw(0, b->wp, Sdpcmsz); + len =3D p->len[0] | p->len[1]<<8; + if(len =3D=3D 0){ + freeb(b); + b =3D nil; + break; + } + lenck =3D p->lenck[0] | p->lenck[1]<<8; + if(lenck !=3D (len ^ 0xFFFF) || + len < Sdpcmsz || len > 2048){ + print("ether4330: wlreadpkt error len %=2E4x lenck %=2E4x\n", len, len= ck); + cfgw(Framectl, Rfhalt); + while(cfgr(Rfrmcnt+1)) + ; + while(cfgr(Rfrmcnt)) + ; + continue; + } + if(len > Sdpcmsz) + packetrw(0, b->wp + Sdpcmsz, len - Sdpcmsz); + b->wp +=3D len; + break; + } + qunlock(&ctl->pktlock); + return b; +} + +static void +txstart(Ether *edev) +{ + Ctlr *ctl; + Sdpcm *p; + Block *b; + int len, off; + + ctl =3D edev->ctlr; + if(!canqlock(&ctl->tlock)) + return; + if(waserror()){ + qunlock(&ctl->tlock); + return; + } + for(;;){ + lock(&ctl->txwinlock); + if(ctl->txseq =3D=3D ctl->txwindow){ + //print("f"); + unlock(&ctl->txwinlock); + break; + } + if(ctl->fcmask & 1<<2){ + //print("x"); + unlock(&ctl->txwinlock); + break; + } + unlock(&ctl->txwinlock); + b =3D qget(edev->oq); + if(b =3D=3D nil) + break; + off =3D ((uintptr)b->rp & 3) + Sdpcmsz; + b =3D padblock(b, off + 4); + len =3D BLEN(b); + p =3D (Sdpcm*)b->rp; + memset(p, 0, off); /* TODO: refactor dup code */ + put2(p->len, len); + put2(p->lenck, ~len); + p->chanflg =3D 2; + p->seq =3D ctl->txseq; + p->doffset =3D 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 =3D a; + ctl =3D edev->ctlr; + flowstart =3D 0; + for(;;){ + if(flowstart){ + //print("F"); + flowstart =3D 0; + txstart(edev); + } + b =3D wlreadpkt(ctl); + if(b =3D=3D nil){ + intwait(ctl, 1); + continue; + } + p =3D (Sdpcm*)b->rp; + if(p->window !=3D ctl->txwindow || p->fcmask !=3D ctl->fcmask){ + lock(&ctl->txwinlock); + if(p->window !=3D ctl->txwindow){ + if(ctl->txseq =3D=3D ctl->txwindow) + flowstart =3D 1; + ctl->txwindow =3D p->window; + } + if(p->fcmask !=3D ctl->fcmask){ + if((p->fcmask & 1<<2) =3D=3D 0) + flowstart =3D 1; + ctl->fcmask =3D 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 =3D (Cmd*)(b->rp + Sdpcmsz); + if((q->id[0] | q->id[1]<<8) !=3D ctl->reqid) + break; + ctl->rsp =3D b; + wakeup(&ctl->cmdr); + continue; + case 1: + if(iodebug) dump("event", b->rp, BLEN(b)); + if(BLEN(b) > p->doffset + 4){ + bdc =3D 4 + (b->rp[p->doffset + 3] << 2); + if(BLEN(b) > p->doffset + bdc){ + b->rp +=3D p->doffset + bdc; /* skip BDC header */ + bcmevent(ctl, b->rp, BLEN(b)); + break; + } + } + if(iodebug && BLEN(b) !=3D 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 =3D 4 + (b->rp[p->doffset + 3] << 2); + if(BLEN(b) >=3D p->doffset + bdc + ETHERHDRSIZE){ + b->rp +=3D 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 =3D ctl->edev; + if(edev =3D=3D nil || ctl->status !=3D Connected) + return; + ctl->status =3D Disconnected; + /* send eof to aux/wpa */ + for(i =3D 0; i < edev->nfile; i++){ + f =3D edev->f[i]; + if(f =3D=3D nil || f->in =3D=3D nil || f->inuse =3D=3D 0 || f->type != =3D 0x888e) + continue; + qwrite(f->in, 0, 0); + } +} + +/* + * Command interface between host and firmware + */ + +static char *eventnames[] =3D { + [0] =3D "set ssid", + [1] =3D "join", + [2] =3D "start", + [3] =3D "auth", + [4] =3D "auth ind", + [5] =3D "deauth", + [6] =3D "deauth ind", + [7] =3D "assoc", + [8] =3D "assoc ind", + [9] =3D "reassoc", + [10] =3D "reassoc ind", + [11] =3D "disassoc", + [12] =3D "disassoc ind", + [13] =3D "quiet start", + [14] =3D "quiet end", + [15] =3D "beacon rx", + [16] =3D "link", + [17] =3D "mic error", + [18] =3D "ndis link", + [19] =3D "roam", + [20] =3D "txfail", + [21] =3D "pmkid cache", + [22] =3D "retrograde tsf", + [23] =3D "prune", + [24] =3D "autoauth", + [25] =3D "eapol msg", + [26] =3D "scan complete", + [27] =3D "addts ind", + [28] =3D "delts ind", + [29] =3D "bcnsent ind", + [30] =3D "bcnrx msg", + [31] =3D "bcnlost msg", + [32] =3D "roam prep", + [33] =3D "pfn net found", + [34] =3D "pfn net lost", + [35] =3D "reset complete", + [36] =3D "join start", + [37] =3D "roam start", + [38] =3D "assoc start", + [39] =3D "ibss assoc", + [40] =3D "radio", + [41] =3D "psm watchdog", + [44] =3D "probreq msg", + [45] =3D "scan confirm ind", + [46] =3D "psk sup", + [47] =3D "country code changed", + [48] =3D "exceeded medium time", + [49] =3D "icv error", + [50] =3D "unicast decode error", + [51] =3D "multicast decode error", + [52] =3D "trace", + [53] =3D "bta hci event", + [54] =3D "if", + [55] =3D "p2p disc listen complete", + [56] =3D "rssi", + [57] =3D "pfn scan complete", + [58] =3D "extlog msg", + [59] =3D "action frame", + [60] =3D "action frame complete", + [61] =3D "pre assoc ind", + [62] =3D "pre reassoc ind", + [63] =3D "channel adopted", + [64] =3D "ap started", + [65] =3D "dfs ap stop", + [66] =3D "dfs ap resume", + [67] =3D "wai sta event", + [68] =3D "wai msg", + [69] =3D "escan result", + [70] =3D "action frame off chan complete", + [71] =3D "probresp msg", + [72] =3D "p2p probreq msg", + [73] =3D "dcs request", + [74] =3D "fifo credit map", + [75] =3D "action frame rx", + [76] =3D "wake event", + [77] =3D "rm complete", + [78] =3D "htsfsync", + [79] =3D "overlay req", + [80] =3D "csa complete ind", + [81] =3D "excess pm wake event", + [82] =3D "pfn scan none", + [83] =3D "pfn scan allgone", + [84] =3D "gtk plumbed", + [85] =3D "assoc ind ndis", + [86] =3D "reassoc ind ndis", + [87] =3D "assoc req ie", + [88] =3D "assoc resp ie", + [89] =3D "assoc recreated", + [90] =3D "action frame rx ndis", + [91] =3D "auth req", + [92] =3D "tdls peer event", + [127] =3D "bcmc credit support" +}; + +static char* +evstring(uint event) +{ + static char buf[12]; + + if(event >=3D nelem(eventnames) || eventnames[event] =3D=3D 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 +=3D ETHERHDRSIZE + 10; /* skip bcm_ether header */ + len -=3D ETHERHDRSIZE + 10; + flags =3D nhgets(p + 2); + event =3D nhgets(p + 6); + status =3D nhgetl(p + 8); + reason =3D nhgetl(p + 12); + if(EVENTDEBUG) + print("ether4330: [%s] status %ld flags %#x reason %ld\n",=20 + evstring(event), status, flags, reason); + switch(event){ + case 19: /* E_ROAM */ + if(status =3D=3D 0) + break; + /* fall through */ + case 0: /* E_SET_SSID */ + ctl->joinstatus =3D 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 =3D ctl->joinstatus; + ctl->joinstatus =3D 0; + return n - 1; +} + +static int +cmddone(void *a) +{ + return ((Ctlr*)a)->rsp !=3D 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 =3D dlen + rlen; + else + tlen =3D MAX(dlen, rlen); + len =3D Sdpcmsz + Cmdsz + tlen; + b =3D allocb(len); + qlock(&ctl->cmdlock); + if(waserror()){ + freeb(b); + qunlock(&ctl->cmdlock); + nexterror(); + } + memset(b->wp, 0, len); + qlock(&ctl->pktlock); + p =3D (Sdpcm*)b->wp; + put2(p->len, len); + put2(p->lenck, ~len); + p->seq =3D ctl->txseq; + p->doffset =3D Sdpcmsz; + b->wp +=3D Sdpcmsz; +=09 + q =3D (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 +=3D Cmdsz; + + if(dlen > 0) + memmove(b->wp, data, dlen); + if(write) + memmove(b->wp + dlen, res, rlen); + b->wp +=3D tlen; + + if(iodebug) dump("cmd", b->rp, len); + packetrw(1, b->rp, len); + ctl->txseq++; + qunlock(&ctl->pktlock); + freeb(b); + b =3D nil; + USED(b); + sleep(&ctl->cmdr, cmddone, ctl); + b =3D ctl->rsp; + ctl->rsp =3D nil; + assert(b !=3D nil); + p =3D (Sdpcm*)b->rp; + q =3D (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 =3D params; + p =3D put4(p, i); /* index */ + p =3D put4(p, ctl->keys[i]=2Elen); + memmove(p, ctl->keys[i]=2Edat, ctl->keys[i]=2Elen); + p +=3D 32 + 18*4; /* keydata, pad */ + if(ctl->keys[i]=2Elen =3D=3D WMinKeyLen) + p =3D put4(p, 1); /* algo =3D WEP1 */ + else + p =3D put4(p, 3); /* algo =3D WEP128 */ + put4(p, 2); /* flags =3D Primarykey */ + + wlsetvar(ctl, "wsec_key", params, sizeof params); +} + +static void +memreverse(char *dst, char *src, int len) +{ + src +=3D len; + while(len-- > 0) + *dst++ =3D *--src; +} + +static void +wlwpakey(Ctlr *ctl, int id, uvlong iv, uchar *ea) +{ + uchar params[164]; + uchar *p; + int pairwise; + + if(id =3D=3D CMrxkey) + return; + pairwise =3D (id =3D=3D CMrxkey || id =3D=3D CMtxkey); + memset(params, 0, sizeof params); + p =3D params; + if(pairwise) + p =3D put4(p, 0); + else + p =3D put4(p, id - CMrxkey0); /* group key id */ + p =3D put4(p, ctl->keys[0]=2Elen); + memmove((char*)p, ctl->keys[0]=2Edat, ctl->keys[0]=2Elen); + p +=3D 32 + 18*4; /* keydata, pad */ + if(ctl->cryptotype =3D=3D Wpa) + p =3D put4(p, 2); /* algo =3D TKIP */ + else + p =3D put4(p, 4); /* algo =3D AES_CCM */ + if(pairwise) + p =3D put4(p, 0); + else + p =3D put4(p, 2); /* flags =3D Primarykey */ + p +=3D 3*4; + p =3D put4(p, 0); //pairwise); /* iv initialised */ + p +=3D 4; + p =3D put4(p, iv>>16); /* iv high */ + p =3D put2(p, iv&0xFFFF); /* iv low */ + p +=3D 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 !=3D 0) + chan |=3D 0x2b00; /* 20Mhz channel width */ + p =3D params; + n =3D strlen(ssid); + n =3D MIN(n, 32); + p =3D put4(p, n); + memmove(p, ssid, n); + memset(p + n, 0, 32 - n); + p +=3D 32; + p =3D put4(p, 0xff); /* scan type */ + if(chan !=3D 0){ + p =3D put4(p, 2); /* num probes */ + p =3D put4(p, 120); /* active time */ + p =3D put4(p, 390); /* passive time */ + }else{ + p =3D put4(p, -1); /* num probes */ + p =3D put4(p, -1); /* active time */ + p =3D put4(p, -1); /* passive time */ + } + p =3D put4(p, -1); /* home time */ + memset(p, 0xFF, Eaddrlen); /* bssid */ + p +=3D Eaddrlen; + p =3D put2(p, 0); /* pad */ + if(chan !=3D 0){ + p =3D put4(p, 1); /* num chans */ + p =3D put2(p, chan); /* chan spec */ + p =3D put2(p, 0); /* pad */ + assert(p =3D=3D params + sizeof(params)); + }else{ + p =3D put4(p, 0); /* num chans */ + assert(p =3D=3D params + sizeof(params) - 4); + } + + wlsetvar(ctl, "join", params, chan? sizeof params : sizeof params - 4); + ctl->status =3D Connecting; + switch(waitjoin(ctl)){ + case 0: + ctl->status =3D Connected; + break; + case 3: + ctl->status =3D Disconnected; + error("wifi join: network not found"); + case 1: + ctl->status =3D Disconnected; + error("wifi join: failed"); + default: + ctl->status =3D 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] =3D { + 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 =3D p[1]; + if(p + 2 + len > ep) + return nil; + if(p[0] =3D=3D tag) + return p; + p +=3D 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] =3D { 0x00, 0x50, 0xf2, 0x01 }; + + snprint(bssid, sizeof bssid, ";bssid=3D%E", p + 8); + if(strstr((char*)bp->rp, bssid) !=3D nil) + return; + bp->wp =3D (uchar*)seprint((char*)bp->wp, (char*)bp->lim, + "ssid=3D%=2E*s%s;signal=3D%d;noise=3D%d;chan=3D%d", + p[18], (char*)p+19, bssid, + (short)get2(p+78), (signed char)p[80], + get2(p+72) & 0xF); + auth =3D auth2 =3D ""; + if(get2(p + 16) & 0x10) + auth =3D ";wep"; + ielen =3D get4(p + 0x78); + if(ielen > 0){ + t =3D p + get4(p + 0x74); + et =3D t + ielen; + if(et > p + len) + return; + if(gettlv(t, et, 0x30) !=3D nil){ + auth =3D ""; + auth2 =3D ";wpa2"; + } + while((t =3D gettlv(t, et, 0xdd)) !=3D nil){ + if(t[1] > 4 && memcmp(t+2, wpaie1, 4) =3D=3D 0){ + auth =3D ";wpa"; + break; + } + t +=3D 2 + t[1]; + } + } + bp->wp =3D (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 =3D edev->ctlr; + if(get4(p) > len) + return; + /* TODO: more syntax checking */ + bp =3D ctlr->scanb; + if(bp =3D=3D nil) + ctlr->scanb =3D bp =3D allocb(8192); + nbss =3D get2(p+10); + p +=3D 12; + len -=3D 12; + if(0) dump("SCAN", p, len); + if(nbss){ + addscan(bp, p, len); + return; + } + i =3D edev->scan; + ep =3D &edev->f[Ntypes]; + for(fp =3D edev->f; fp < ep && i > 0; fp++){ + f =3D *fp; + if(f =3D=3D nil || f->scan =3D=3D 0) + continue; + if(i =3D=3D 1) + qpass(f->in, bp); + else + qpass(f->in, copyblock(bp, BLEN(bp))); + i--; + } + if(i) + freeb(bp); + ctlr->scanb =3D nil; +} + +static void +lproc(void *a) +{ + Ether *edev; + Ctlr *ctlr; + int secs; + + edev =3D a; + ctlr =3D edev->ctlr; + secs =3D 0; + for(;;){ + tsleep(&up->sleep, return0, 0, 1000); + if(ctlr->scansecs){ + if(secs =3D=3D 0){ + if(waserror()) + ctlr->scansecs =3D 0; + else{ + wlscanstart(ctlr); + poperror(); + } + secs =3D ctlr->scansecs; + } + --secs; + }else + secs =3D 0; + } +} + +static void +wlinit(Ether *edev, Ctlr *ctlr) +{ + uchar ea[Eaddrlen]; + uchar eventmask[16]; + char version[128]; + char *p; + static uchar keepalive[12] =3D {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 =3D=3D 43430 || ctlr->chipid =3D=3D 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 =3D=3D 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] |=3D 1<<(n%8) +#define DISABLE(n) eventmask[n/8] &=3D ~(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 =3D strchr(version, '\n')) !=3D nil) + *p =3D '\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]=2Elen =3D 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] =3D { + [0] "off", + [Wep] "wep", + [Wpa] "wpa", + [Wpa2] "wpa2", + }; + /* these strings are known by aux/wpa */ + static char* connectstate[] =3D { + [Disconnected] =3D "unassociated", + [Connecting] =3D "connecting", + [Connected] =3D "associated", + }; + + ctlr =3D edev->ctlr; + if(ctlr =3D=3D nil) + return 0; + p =3D malloc(READSTR); + l =3D 0; + + l +=3D snprint(p+l, READSTR-l, "channel: %d\n", ctlr->chanid); + l +=3D snprint(p+l, READSTR-l, "essid: %s\n", ctlr->essid); + l +=3D snprint(p+l, READSTR-l, "crypt: %s\n", cryptoname[ctlr->cryptotyp= e]); + l +=3D snprint(p+l, READSTR-l, "oq: %d\n", qlen(edev->oq)); + l +=3D snprint(p+l, READSTR-l, "txwin: %d\n", ctlr->txwindow); + l +=3D snprint(p+l, READSTR-l, "txseq: %d\n", ctlr->txseq); + l +=3D snprint(p+l, READSTR-l, "status: %s\n", connectstate[ctlr->status= ]); + USED(l); + n =3D readstr(offset, a, n, p); + free(p); + return n; +} + +static void +etherbcmtransmit(Ether *edev) +{ + Ctlr *ctlr; + + ctlr =3D edev->ctlr; + if(ctlr =3D=3D nil) + return; + txstart(edev); +} + +static int +parsehex(char *buf, int buflen, char *a) +{ + int i, k, n; + + k =3D 0; + for(i =3D 0;k < buflen && *a; i++){ + if(*a >=3D '0' && *a <=3D '9') + n =3D *a++ - '0'; + else if(*a >=3D 'a' && *a <=3D 'f') + n =3D *a++ - 'a' + 10; + else if(*a >=3D 'A' && *a <=3D 'F') + n =3D *a++ - 'A' + 10; + else + break; + + if(i & 1){ + buf[k] |=3D n; + k++; + } + else + buf[k] =3D n<<4; + } + if(i & 1) + return -1; + return k; +} + +static int +wepparsekey(WKey* key, char* a)=20 +{ + int i, k, len, n; + char buf[WMaxKeyLen]; + + len =3D strlen(a); + if(len =3D=3D WMinKeyLen || len =3D=3D WMaxKeyLen){ + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, a, len); + key->len =3D len; + + return 0; + } + else if(len =3D=3D WMinKeyLen*2 || len =3D=3D WMaxKeyLen*2){ + k =3D 0; + for(i =3D 0; i < len; i++){ + if(*a >=3D '0' && *a <=3D '9') + n =3D *a++ - '0'; + else if(*a >=3D 'a' && *a <=3D 'f') + n =3D *a++ - 'a' + 10; + else if(*a >=3D 'A' && *a <=3D 'F') + n =3D *a++ - 'A' + 10; + else + return -1; +=09 + if(i & 1){ + buf[k] |=3D n; + k++; + } + else + buf[k] =3D n<<4; + } + + memset(key->dat, 0, sizeof(key->dat)); + memmove(key->dat, buf, k); + key->len =3D k; + + return 0; + } + + return -1; +} + +static int +wpaparsekey(WKey *key, uvlong *ivp, char *a) +{ + int len; + char *e; + + if(cistrncmp(a, "tkip:", 5) =3D=3D 0 || cistrncmp(a, "ccmp:", 5) =3D=3D = 0) + a +=3D 5; + else + return 1; + len =3D parsehex(key->dat, sizeof(key->dat), a); + if(len <=3D 0) + return 1; + key->len =3D len; + a +=3D 2*len; + if(*a++ !=3D '@') + return 1; + *ivp =3D strtoull(a, &e, 16); + if(e =3D=3D a) + return -1; + return 0; +} + +static void +setauth(Ctlr *ctlr, Cmdbuf *cb, char *a) +{ + uchar wpaie[32]; + int i; + + i =3D parsehex((char*)wpaie, sizeof wpaie, a); + if(i < 2 || i !=3D wpaie[1] + 2) + cmderror(cb, "bad wpa ie syntax"); + if(wpaie[0] =3D=3D 0xdd) + ctlr->cryptotype =3D Wpa; + else if(wpaie[0] =3D=3D 0x30) + ctlr->cryptotype =3D Wpa2; + else + cmderror(cb, "bad wpa ie"); + wlsetvar(ctlr, "wpaie", wpaie, i); + if(ctlr->cryptotype =3D=3D 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") =3D=3D 0 || cistrcmp(a, "on") =3D=3D 0) + ctlr->cryptotype =3D Wep; + else if(cistrcmp(a, "off") =3D=3D 0 || cistrcmp(a, "none") =3D=3D 0) + ctlr->cryptotype =3D 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 =3D edev->ctlr) =3D=3D nil) + error(Enonexist); + USED(ctlr); + + cb =3D parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct =3D 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 =3D atoi(cb->f[1])) < 0 || i > 16) + cmderror(cb, "bad channel number"); + //wlcmdint(ctlr, 30, i); /* SET_CHANNEL */ + ctlr->chanid =3D 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") =3D=3D 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] =3D '\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], "") !=3D 0){ /* empty string for no change */ + if(cistrcmp(cb->f[1], "default") !=3D 0){ + strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid)-1); + ctlr->essid[sizeof(ctlr->essid)-1] =3D 0; + }else + memset(ctlr->essid, 0, sizeof(ctlr->essid)); + }else if(ctlr->essid[0] =3D=3D 0) + cmderror(cb, "essid not set"); + if((i =3D atoi(cb->f[2])) >=3D 0 && i <=3D 16) + ctlr->chanid =3D 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 =3D 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 =3D atoi(cb->f[1]); + break; + } + poperror(); + free(cb); + return n; +} + +static void +etherbcmscan(void *a, uint secs) +{ + Ether* edev; + Ctlr* ctlr; + + edev =3D a; + ctlr =3D edev->ctlr; + ctlr->scansecs =3D secs; +} + +static void +etherbcmattach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr =3D edev->ctlr; + qlock(&ctlr->alock); + if(waserror()){ + //print("ether4330: attach failed: %s\n", up->errstr); + qunlock(&ctlr->alock); + nexterror(); + } + if(ctlr->edev =3D=3D nil){ + if(ctlr->chipid =3D=3D 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 =3D edev; + } + qunlock(&ctlr->alock); + poperror(); +} + +static void +etherbcmshutdown(Ether*) +{ + sdioreset(); +} + + +static int +etherbcmpnp(Ether* edev) +{ + static Ctlr *ctlr; + + if(ctlr !=3D nil) + return -1; + + ctlr =3D malloc(sizeof(Ctlr)); + ctlr->chanid =3D Wifichan; + edev->ctlr =3D ctlr; + edev->attach =3D etherbcmattach; + edev->transmit =3D etherbcmtransmit; + edev->ifstat =3D etherbcmifstat; + edev->ctl =3D etherbcmctl; + edev->scanbs =3D etherbcmscan; + edev->shutdown =3D etherbcmshutdown; + edev->arg =3D 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=20 ethergenet ethermii + ether4330 emmc ethermedium loopbackmedium netdevmedium --- a/sys/src/9/port/sd=2Eh Sun Oct 22 09:21:23 2023 +++ b/sys/src/9/port/sd=2Eh Sun Oct 22 10:53:02 2023 @@ -164,6 +164,31 @@ char *name; }; =20 +/* 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*);