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.2 required=5.0 tests=DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FROM autolearn=no autolearn_force=no version=3.4.4 Received: (qmail 15625 invoked from network); 22 Oct 2023 19:06:26 -0000 Received: from 9front.inri.net (168.235.81.73) by inbox.vuxu.org with ESMTPUTF8; 22 Oct 2023 19:06:26 -0000 Received: from mail-lf1-f44.google.com ([209.85.167.44]) by 9front; Sun Oct 22 15:01:37 -0400 2023 Received: by mail-lf1-f44.google.com with SMTP id 2adb3069b0e04-507bd64814fso3682154e87.1 for <9front@9front.org>; Sun, 22 Oct 2023 12:01:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698001294; x=1698606094; darn=9front.org; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=vhfCQbF1rVmr+eZSOimJRyVX1XIwJLWIP0VuACDuEO4=; b=V748swYyFb+FE0KMr9jbQFRFjtHbncULACzPO+3FrL0SNEOxNZjZF9jaj9ph9lNZwJ IMMaTcliDdDjFAWmTQPDZ75KdRq+hYIfP+sfk3D1yHYjRphwP+3yjOd8A3BaZG6KtF6N DzbQIs0lHfd7aW1XFWImDMKd+h36x7/9cgMs6zS4gNT9zjoeYEfntbLq6o8Wd0dcapJe kDj9z2yH0+cSW294LaJU/lYNZOLGOFQDhTYG+5gCnkJU2iUK8YcmKnsIDeiraTbipWJh sEeYeS3JHTae8derf4SM0u/CvS2EJU0tpNKd3uSFC2srtVCY2u2Yp9jjzZ2Rk8pdNf9o uLpw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698001294; x=1698606094; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vhfCQbF1rVmr+eZSOimJRyVX1XIwJLWIP0VuACDuEO4=; b=f/OZ6yltw9uW1roTsgiQd0tQu1FYFyLAUXYVYF7tI1Q3FKxYjEnWnkNTPoDeo1qLih 8gejzQmyMTCVawgqZNaRk/D7XF8G/oUkKnuamnFNrFHLM6GPqIOfseqoIsJozb1nJnjf /fmeGuHiXzbMuAdhvogB6adcXvbTVVqZNO2UBGzASBgR4ux/28qHZyfERisyEEs7YCWU f+j3xkqSg8C+CU3bhMh+xRrG+TOIMLEtArTXjIZmona+LZoI5EXFZHu7XztFld26aS4D /biw+e9M8qOKTIwH99eIT3K+Is/kOjGw4sK2I7EqWq1+8AdyHJeILE8eyEjRJPRyAdKF c5gw== X-Gm-Message-State: AOJu0YwlGVqdq02v/a+jZqW3bKS87LMKbhAV5UOrIEGJkS2gtbHklBA4 hgRhoKccfHSpYx1ruvKzKmm8SgiT1I88imgybvHosLX8 X-Google-Smtp-Source: AGHT+IHoDzcDQP1wiDkb+pjmShizAHDbqcn+Tdb0zv0nPEdW262TKbHR1tJWoD3PcjDeQ0OyLVhzTITW+bmuGSM0VdE= X-Received: by 2002:a05:6512:3747:b0:500:daf6:3898 with SMTP id a7-20020a056512374700b00500daf63898mr3935158lfs.26.1698001293608; Sun, 22 Oct 2023 12:01:33 -0700 (PDT) MIME-Version: 1.0 References: <1734B8F2A873B2D850DB1C299ECACF8F@9front.org> <4C53B52B-3A22-46DF-8CE1-80BD3543F6EF@stanleylieber.com> In-Reply-To: From: adventures in9 Date: Sun, 22 Oct 2023 12:01:21 -0700 Message-ID: To: 9front@9front.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: self-healing browser factory base cloud engine Subject: Re: [9front] Re: Fwd: [9front-commits] bcm: add wifi driver (keegan@undefinedbehaviour.org) Reply-To: 9front@9front.org Precedence: bulk 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=E2=80=AFAM Stanley Lieber wrote: > > On October 22, 2023 2:43:42 PM EDT, Stanley Lieber = 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@undefinedbehaviou= r.org) > > > > > >Thanks Richard Miller > >--- > >diff f2cfee358f329519e913a20142d96b1e0029633c fb96a050f8a9d5f23da3557ba8= 9251ffb73889bf > >--- 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 =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.cardr, 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.r); > >+ if(i&Cardintr) > >+ wakeup(&emmc.cardr); > > WR(Irpten, r[Irpten] & ~i); > > } > > > >+ > >+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.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 =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.2-3.4 volts */ > >+ V2_8 =3D 3<<15, /* 2.7-2.9 volts */ > >+ V2_0 =3D 1<<8, /* 2.0-2.1 volts */ > >+ S18R =3D 1<<24, /* switch to 1.8V 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 clo= ck */ > >+ ForceHT =3D 0x02, /* high throughput cloc= k */ > >+ 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, > >+ > >+ /* 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.cal.40181"; > >+static char config40183[] =3D "bcmdhd.cal.40183.26MHz"; > >+ > >+struct { > >+ int chipid; > >+ int chiprev; > >+ char *fwfile; > >+ char *cfgfile; > >+ char *regufile; > >+} firmware[] =3D { > >+ { 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] =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.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", c= md->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.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 =3D 0; > >+ for(retry =3D 0; retry < 10; retry++){ > >+ arg =3D (1<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)|(data&0x= FF); > >+ if((arg & ~0xFF) =3D=3D (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 =3D sdiocmd(&IO_RW_DIRECT, arg); > >+ if((r & 0xCF00) =3D=3D 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 =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->errs= tr); > >+ qunlock(&sdiolock); > >+ nexterror(); > >+ } > >+ cmd.data =3D write? 2 : 1; /* Host2card : Card2hos= t */ > >+ if(blk){ > >+ cmd.data +=3D 2; /* Multiblock | Blkcnte= n */ > >+ 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 | i= ncr<<26 | (addr&0x1FFFF)<<9 | (bcount&0x1FF)); > >+ sdio.io(&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); > >+} > >+ > >+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.init(&sdio); > >+ sdio.enable(&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.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< >+ sdiowr(Fn0, Intenable, 0); > >+ for(i =3D 0; !(sdiord(Fn0, Ioready) & 1< >+ if(i =3D=3D 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 =3D (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", o= ff, 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.2x %2.2x %2.2x %2.2x\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]<<2= 4; > >+ addr &=3D ~0xFFF; > >+ if(SBDEBUG) print("core %x %s %#lux\n", coreid, b= uf[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 & 0x3F) + 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->so= cramrev); > >+ 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, 8= 192 * ((n & 0x3F) + 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= ->socramregs =3D=3D 0))) > >+ error("corescan didn't find essential cores\n"); > >+ if(ctl->armcore =3D=3D 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 by= tes @ %#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)) =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->chipcommon + 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->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 =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); /* protoc= ol version */ > >+ cfgwritel(Fn1, ctl->sdregs + Intmask, FrameInt | MailboxInt | Fcc= hange); > >+ sdioset(Fn0, Ioenable, 1< >+ for(i =3D 0; !(sdiord(Fn0, Ioready) & 1< >+ if(i =3D=3D 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< >+} > >+ > >+ > >+/* > >+ * Firmware and config file uploading > >+ */ > >+ > >+/* > >+ * Condense config file contents (in buffer buf with length n) > >+ * to 'var=3Dvalue\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 =3D 0; /* true if in a comment */ > >+ ep =3D buf + n; /* end of input */ > >+ op =3D buf; /* end of output */ > >+ lp =3D buf; /* start of current output line */ > >+ for(p =3D buf; p < ep; p++){ > >+ switch(c =3D *p){ > >+ case '#': > >+ skipping =3D 1; > >+ break; > >+ case '\0': > >+ case '\n': > >+ skipping =3D 0; > >+ if(op !=3D lp){ > >+ *op++ =3D '\0'; > >+ lp =3D op; > >+ } > >+ break; > >+ case '\r': > >+ break; > >+ default: > >+ if(!skipping) > >+ *op++ =3D c; > >+ break; > >+ } > >+ } > >+ if(!skipping && op !=3D lp) > >+ *op++ =3D '\0'; > >+ *op++ =3D '\0'; > >+ for(n =3D op - buf; n & 03; n++) > >+ *op++ =3D '\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 =3D namec(nbuf, Aopen, OREAD, 0); > >+ poperror(); > >+ }else if(!waserror()){ > >+ snprint(nbuf, sizeof nbuf, "/lib/firmware/%s", file); > >+ c =3D namec(nbuf, Aopen, OREAD, 0); > >+ poperror(); > >+ }else{ > >+ c =3D 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 =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.c, buf, sizeof(ctl->resetve= c.c)); > >+ 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..."); > >+ if(!isconfig) > >+ off =3D 0; > >+ for(;;){ > >+ if(!isconfig){ > >+ n =3D devtab[c->type]->read(c, buf, Uploa= dsz, 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 of= fset %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 (.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 =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].chipid !=3D ctl->chipid || > >+ firmware[i].chiprev !=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].regufile; > >+ 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..."); > >+ upload(ctl, firmware[i].fwfile, 0); > >+ if(FWDEBUG) print("config load..."); > >+ n =3D upload(ctl, firmware[i].cfgfile, 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.i !=3D 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 =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, cfgread= l(Fn1, ctlr->sdregs + Intstatus)); > >+ if(ints & MailboxInt){ > >+ mbox =3D cfgreadl(Fn1, ctlr->sdregs + Hostmboxdat= a); > >+ 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 %.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 +=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(Wfrm= cnt+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->fc= mask){ > >+ 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 + ETHERH= DRSIZE){ > >+ 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 heade= r */ > >+ 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"= , > >+ 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 f= lags %#x reason %ld\n", > >+ evstring(event), status, flags, r= eason); > >+ 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, in= t 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; > >+ > >+ 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].len); > >+ memmove(p, ctl->keys[i].dat, ctl->keys[i].len); > >+ p +=3D 32 + 18*4; /* keydata, pad */ > >+ if(ctl->keys[i].len =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].len); > >+ memmove((char*)p, ctl->keys[0].dat, ctl->keys[0].len); > >+ 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] b= ss_type[1] > >+ scan_type[1] nprobes[4] active_time[4] passive_time[4] ho= me_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,0x= 2e,0x07,0x2e, > >+ 0x08,0x2b,0x09,0x2b,0x0a,0x2b,0x0b,0x2b,0x0c,0x2b,0x0d,0x= 2b,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%.*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].len =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->cr= yptotype]); > >+ 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) > >+{ > >+ 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; > >+ > >+ 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 | aut= h_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 | aut= h_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(ctl= r->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 > > 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