Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/next/external/cache/sources/wl/shared/bcmutils.c
Views: 3959
/*1* Driver O/S-independent utility routines2*3* $Copyright Open Broadcom Corporation$4* $Id: bcmutils.c 401759 2013-05-13 16:08:08Z sudhirbs $5*/67#include <bcm_cfg.h>8#include <typedefs.h>9#include <bcmdefs.h>10#if defined(__FreeBSD__) || defined(__NetBSD__)11#include <sys/param.h>12#if __NetBSD_Version__ >= 50000000313#include <sys/stdarg.h>14#else15#include <machine/stdarg.h>16#endif17#else18#include <stdarg.h>19#endif /* NetBSD */20#ifdef BCMDRIVER2122#include <osl.h>23#include <bcmutils.h>24#if !defined(BCMDONGLEHOST) || defined(BCMNVRAM) || defined(WLC_LOW)25#include <siutils.h>26#include <bcmnvram.h>27#endif2829#else /* !BCMDRIVER */3031#include <stdio.h>32#include <string.h>33#include <bcmutils.h>3435#if defined(BCMEXTSUP)36#include <bcm_osl.h>37#endif3839#ifdef DSLCPE40#ifndef ASSERT41#define ASSERT(exp)42#endif43#endif /* DSLCPE */4445#endif /* !BCMDRIVER */4647#if defined(_WIN32) || defined(NDIS) || defined(vxworks) || defined(__vxworks) || defined(_CFE_)48/* xxx debatable */49#include <bcmstdlib.h>50#endif51#include <bcmendian.h>52#include <bcmdevs.h>53#include <proto/ethernet.h>54#include <proto/vlan.h>55#include <proto/bcmip.h>56#include <proto/802.1d.h>57#include <proto/802.11.h>58#ifdef BCMPERFSTATS59#include <bcmperf.h>60#endif61#include <proto/bcmipv6.h>62void *_bcmutils_dummy_fn = NULL;6364#ifdef BCMDRIVER6566#ifdef WLC_LOW67/* nvram vars cache */68static char *nvram_vars = NULL;69static int vars_len = -1;70#endif /* WLC_LOW */7172#if !defined(BCMDONGLEHOST)73int74BCMATTACHFN(pktpool_init)(osl_t *osh, pktpool_t *pktp, int *pplen, int plen, bool istx)75{76int i, err = BCME_OK;77void *p;78int pktplen;7980ASSERT(pktp != NULL);81ASSERT(osh != NULL);82ASSERT(pplen != NULL);8384pktplen = *pplen;8586bzero(pktp, sizeof(pktpool_t));87pktp->inited = TRUE;88pktp->istx = istx ? TRUE : FALSE;89pktp->plen = (uint16)plen;90*pplen = 0;9192pktp->maxlen = PKTPOOL_LEN_MAX;93if (pktplen > pktp->maxlen)94pktplen = pktp->maxlen;9596for (i = 0; i < pktplen; i++) {97p = PKTGET(osh, plen, pktp->istx);98if (p == NULL) {99/* Not able to allocate all requested pkts100* so just return what was actually allocated101* We can add to the pool later102*/103if (pktp->w == 0)104err = BCME_NOMEM;105106goto exit;107}108109PKTSETPOOL(osh, p, TRUE, pktp);110pktp->q[i] = p;111pktp->w++;112pktp->len++;113#ifdef BCMDBG_POOL114pktp->dbg_q[pktp->dbg_qlen++].p = p;115#endif116}117118exit:119*pplen = pktp->w;120pktp->len++; /* Add one for end */121return err;122}123124int125BCMATTACHFN(pktpool_deinit)(osl_t *osh, pktpool_t *pktp)126{127int i;128int cnt;129130ASSERT(osh != NULL);131ASSERT(pktp != NULL);132133cnt = pktp->len;134for (i = 0; i < cnt; i++) {135if (pktp->q[i] != NULL) {136PKTSETPOOL(osh, pktp->q[i], FALSE, NULL);137PKTFREE(osh, pktp->q[i], pktp->istx);138pktp->q[i] = NULL;139pktp->len--;140}141#ifdef BCMDBG_POOL142if (pktp->dbg_q[i].p != NULL)143pktp->dbg_q[i].p = NULL;144#endif145}146pktp->inited = FALSE;147148/* Are there still pending pkts? */149ASSERT(pktpool_len(pktp) == 0);150151return 0;152}153154int155pktpool_fill(osl_t *osh, pktpool_t *pktp, bool minimal)156{157void *p;158int err = 0;159int len, psize, maxlen;160161ASSERT(pktpool_plen(pktp) != 0);162163maxlen = pktpool_maxlen(pktp);164psize = minimal ? (maxlen >> 2) : maxlen;165len = pktpool_len(pktp);166for (; len < psize; len++) {167p = PKTGET(osh, pktpool_plen(pktp), FALSE);168if (p == NULL) {169err = BCME_NOMEM;170break;171}172173if (pktpool_add(pktp, p) != BCME_OK) {174PKTFREE(osh, p, FALSE);175err = BCME_ERROR;176break;177}178}179180return err;181}182183uint16184pktpool_avail(pktpool_t *pktp)185{186if (pktp->w == pktp->r)187return 0;188189return (pktp->w > pktp->r) ? (pktp->w - pktp->r) : ((pktp->len) - (pktp->r - pktp->w));190}191192static void *193pktpool_deq(pktpool_t *pktp)194{195void *p;196197if (pktp->r == pktp->w)198return NULL;199200p = pktp->q[pktp->r];201ASSERT(p != NULL);202203pktp->q[pktp->r++] = NULL;204pktp->r %= (pktp->len);205206return p;207}208209static void210pktpool_enq(pktpool_t *pktp, void *p)211{212uint16 next;213214ASSERT(p != NULL);215216next = (pktp->w + 1) % (pktp->len);217if (next == pktp->r) {218/* Should not happen; otherwise pkt leak */219ASSERT(0);220return;221}222223ASSERT(pktp->q[pktp->w] == NULL);224225#ifdef BCMDBG_ASSERT226if (pktpool_avail(pktp)) {227int prev = (pktp->w == 0) ? (pktp->len - 1) : (pktp->w - 1);228ASSERT(pktp->q[prev] != p);229}230#endif231pktp->q[pktp->w] = p;232pktp->w = next;233}234235int236BCMATTACHFN(pktpool_avail_register)(pktpool_t *pktp, pktpool_cb_t cb, void *arg)237{238int i;239240ASSERT(cb != NULL);241242i = pktp->cbcnt;243if (i == PKTPOOL_CB_MAX)244return BCME_ERROR;245246ASSERT(pktp->cbs[i].cb == NULL);247pktp->cbs[i].cb = cb;248pktp->cbs[i].arg = arg;249pktp->cbcnt++;250251return 0;252}253254int255BCMATTACHFN(pktpool_empty_register)(pktpool_t *pktp, pktpool_cb_t cb, void *arg)256{257int i;258259ASSERT(cb != NULL);260261i = pktp->ecbcnt;262if (i == PKTPOOL_CB_MAX)263return BCME_ERROR;264265ASSERT(pktp->ecbs[i].cb == NULL);266pktp->ecbs[i].cb = cb;267pktp->ecbs[i].arg = arg;268pktp->ecbcnt++;269270return 0;271}272273static int274pktpool_empty_notify(pktpool_t *pktp)275{276int i;277278pktp->empty = TRUE;279for (i = 0; i < pktp->ecbcnt; i++) {280ASSERT(pktp->ecbs[i].cb != NULL);281pktp->ecbs[i].cb(pktp, pktp->ecbs[i].arg);282}283pktp->empty = FALSE;284285return 0;286}287288#ifdef BCMDBG_POOL289int290pktpool_dbg_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg)291{292int i;293294ASSERT(cb);295296i = pktp->dbg_cbcnt;297if (i == PKTPOOL_CB_MAX)298return BCME_ERROR;299300ASSERT(pktp->dbg_cbs[i].cb == NULL);301pktp->dbg_cbs[i].cb = cb;302pktp->dbg_cbs[i].arg = arg;303pktp->dbg_cbcnt++;304305return 0;306}307308int pktpool_dbg_notify(pktpool_t *pktp);309310int311pktpool_dbg_notify(pktpool_t *pktp)312{313int i;314315for (i = 0; i < pktp->dbg_cbcnt; i++) {316ASSERT(pktp->dbg_cbs[i].cb);317pktp->dbg_cbs[i].cb(pktp, pktp->dbg_cbs[i].arg);318}319320return 0;321}322323int324pktpool_dbg_dump(pktpool_t *pktp)325{326int i;327328printf("pool len=%d maxlen=%d\n", pktp->dbg_qlen, pktp->maxlen);329for (i = 0; i < pktp->dbg_qlen; i++) {330ASSERT(pktp->dbg_q[i].p);331printf("%d, p: 0x%x dur:%lu us state:%d\n", i,332pktp->dbg_q[i].p, pktp->dbg_q[i].dur/100, PKTPOOLSTATE(pktp->dbg_q[i].p));333}334335return 0;336}337338int339pktpool_stats_dump(pktpool_t *pktp, pktpool_stats_t *stats)340{341int i;342int state;343344bzero(stats, sizeof(pktpool_stats_t));345for (i = 0; i < pktp->dbg_qlen; i++) {346ASSERT(pktp->dbg_q[i].p != NULL);347348state = PKTPOOLSTATE(pktp->dbg_q[i].p);349switch (state) {350case POOL_TXENQ:351stats->enq++; break;352case POOL_TXDH:353stats->txdh++; break;354case POOL_TXD11:355stats->txd11++; break;356case POOL_RXDH:357stats->rxdh++; break;358case POOL_RXD11:359stats->rxd11++; break;360case POOL_RXFILL:361stats->rxfill++; break;362case POOL_IDLE:363stats->idle++; break;364}365}366367return 0;368}369370int371pktpool_start_trigger(pktpool_t *pktp, void *p)372{373uint32 cycles, i;374375if (!PKTPOOL(NULL, p))376return 0;377378OSL_GETCYCLES(cycles);379380for (i = 0; i < pktp->dbg_qlen; i++) {381ASSERT(pktp->dbg_q[i].p != NULL);382383if (pktp->dbg_q[i].p == p) {384pktp->dbg_q[i].cycles = cycles;385break;386}387}388389return 0;390}391392int pktpool_stop_trigger(pktpool_t *pktp, void *p);393int394pktpool_stop_trigger(pktpool_t *pktp, void *p)395{396uint32 cycles, i;397398if (!PKTPOOL(NULL, p))399return 0;400401OSL_GETCYCLES(cycles);402403for (i = 0; i < pktp->dbg_qlen; i++) {404ASSERT(pktp->dbg_q[i].p != NULL);405406if (pktp->dbg_q[i].p == p) {407if (pktp->dbg_q[i].cycles == 0)408break;409410if (cycles >= pktp->dbg_q[i].cycles)411pktp->dbg_q[i].dur = cycles - pktp->dbg_q[i].cycles;412else413pktp->dbg_q[i].dur =414(((uint32)-1) - pktp->dbg_q[i].cycles) + cycles + 1;415416pktp->dbg_q[i].cycles = 0;417break;418}419}420421return 0;422}423#endif /* BCMDBG_POOL */424425int426pktpool_avail_notify_normal(osl_t *osh, pktpool_t *pktp)427{428ASSERT(pktp);429pktp->availcb_excl = NULL;430return 0;431}432433int434pktpool_avail_notify_exclusive(osl_t *osh, pktpool_t *pktp, pktpool_cb_t cb)435{436int i;437438ASSERT(pktp);439ASSERT(pktp->availcb_excl == NULL);440for (i = 0; i < pktp->cbcnt; i++) {441if (cb == pktp->cbs[i].cb) {442pktp->availcb_excl = &pktp->cbs[i];443break;444}445}446447if (pktp->availcb_excl == NULL)448return BCME_ERROR;449else450return 0;451}452453static int454pktpool_avail_notify(pktpool_t *pktp)455{456int i, k, idx;457int avail;458459ASSERT(pktp);460if (pktp->availcb_excl != NULL) {461pktp->availcb_excl->cb(pktp, pktp->availcb_excl->arg);462return 0;463}464465k = pktp->cbcnt - 1;466for (i = 0; i < pktp->cbcnt; i++) {467avail = pktpool_avail(pktp);468469if (avail) {470if (pktp->cbtoggle)471idx = i;472else473idx = k--;474475ASSERT(pktp->cbs[idx].cb != NULL);476pktp->cbs[idx].cb(pktp, pktp->cbs[idx].arg);477}478}479480/* Alternate between filling from head or tail481*/482pktp->cbtoggle ^= 1;483484return 0;485}486487void *488pktpool_get(pktpool_t *pktp)489{490void *p;491492p = pktpool_deq(pktp);493494if (p == NULL) {495/* Notify and try to reclaim tx pkts */496if (pktp->ecbcnt)497pktpool_empty_notify(pktp);498499p = pktpool_deq(pktp);500}501502return p;503}504505void506pktpool_free(pktpool_t *pktp, void *p)507{508ASSERT(p != NULL);509510#ifdef BCMDBG_POOL511/* pktpool_stop_trigger(pktp, p); */512#endif513514pktpool_enq(pktp, p);515516if (pktp->emptycb_disable)517return;518519if (pktp->cbcnt) {520if (pktp->empty == FALSE)521pktpool_avail_notify(pktp);522}523}524525int526pktpool_add(pktpool_t *pktp, void *p)527{528ASSERT(p != NULL);529530if (pktpool_len(pktp) == pktp->maxlen)531return BCME_RANGE;532533ASSERT(pktpool_plen(pktp) == PKTLEN(NULL, p)); /* pkts in pool have same length */534PKTSETPOOL(NULL, p, TRUE, pktp);535536pktp->len++;537if (pktp->r > pktp->w) {538/* Add to tail */539ASSERT(pktp->q[pktp->len - 1] == NULL);540pktp->q[pktp->len - 1] = p;541} else542pktpool_enq(pktp, p);543544#ifdef BCMDBG_POOL545pktp->dbg_q[pktp->dbg_qlen++].p = p;546#endif547548return 0;549}550551int552pktpool_setmaxlen(pktpool_t *pktp, uint16 maxlen)553{554if (maxlen > PKTPOOL_LEN_MAX)555maxlen = PKTPOOL_LEN_MAX;556557/* if pool is already beyond maxlen, then just cap it558* since we currently do not reduce the pool len559* already allocated560*/561pktp->maxlen = (pktpool_len(pktp) > maxlen) ? pktpool_len(pktp) : maxlen;562563return pktp->maxlen;564}565566void567pktpool_emptycb_disable(pktpool_t *pktp, bool disable)568{569ASSERT(pktp);570571pktp->emptycb_disable = disable;572}573574bool575pktpool_emptycb_disabled(pktpool_t *pktp)576{577ASSERT(pktp);578return pktp->emptycb_disable;579}580#endif /* BCMDONGLEHOST */581582/* copy a pkt buffer chain into a buffer */583uint584pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf)585{586uint n, ret = 0;587588if (len < 0)589len = 4096; /* "infinite" */590591/* skip 'offset' bytes */592for (; p && offset; p = PKTNEXT(osh, p)) {593if (offset < (uint)PKTLEN(osh, p))594break;595offset -= PKTLEN(osh, p);596}597598if (!p)599return 0;600601/* copy the data */602for (; p && len; p = PKTNEXT(osh, p)) {603n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);604bcopy(PKTDATA(osh, p) + offset, buf, n);605buf += n;606len -= n;607ret += n;608offset = 0;609}610611return ret;612}613614/* copy a buffer into a pkt buffer chain */615uint616pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf)617{618uint n, ret = 0;619620/* skip 'offset' bytes */621for (; p && offset; p = PKTNEXT(osh, p)) {622if (offset < (uint)PKTLEN(osh, p))623break;624offset -= PKTLEN(osh, p);625}626627if (!p)628return 0;629630/* copy the data */631for (; p && len; p = PKTNEXT(osh, p)) {632n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);633bcopy(buf, PKTDATA(osh, p) + offset, n);634buf += n;635len -= n;636ret += n;637offset = 0;638}639640return ret;641}642643#ifdef NOTYET644/* copy data from one pkt buffer (chain) to another */645uint646pkt2pktcopy(osl_t *osh, void *p1, uint offs1, void *p2, uint offs2, int maxlen)647{648uint8 *dp1, *dp2;649uint len1, len2, copylen, totallen;650651for (; p1 && offs; p1 = PKTNEXT(osh, p1)) {652if (offs1 < (uint)PKTLEN(osh, p1))653break;654offs1 -= PKTLEN(osh, p1);655}656for (; p2 && offs; p2 = PKTNEXT(osh, p2)) {657if (offs2 < (uint)PKTLEN(osh, p2))658break;659offs2 -= PKTLEN(osh, p2);660}661662/* Heck w/it, only need the above for now */663}664#endif /* NOTYET */665666667/* return total length of buffer chain */668uint BCMFASTPATH669pkttotlen(osl_t *osh, void *p)670{671uint total;672int len;673674total = 0;675for (; p; p = PKTNEXT(osh, p)) {676len = PKTLEN(osh, p);677#ifdef MACOSX678if (len < 0) {679/* Bad packet length, just drop and exit */680printf("wl: pkttotlen bad (%p,%d)\n", p, len);681break;682}683#endif /* MACOSX */684total += len;685}686687return (total);688}689690/* return the last buffer of chained pkt */691void *692pktlast(osl_t *osh, void *p)693{694for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p))695;696697return (p);698}699700/* count segments of a chained packet */701uint BCMFASTPATH702pktsegcnt(osl_t *osh, void *p)703{704uint cnt;705706for (cnt = 0; p; p = PKTNEXT(osh, p))707cnt++;708709return cnt;710}711712713/* count segments of a chained packet */714uint BCMFASTPATH715pktsegcnt_war(osl_t *osh, void *p)716{717uint cnt;718uint8 *pktdata;719uint len, remain, align64;720721for (cnt = 0; p; p = PKTNEXT(osh, p)) {722cnt++;723len = PKTLEN(osh, p);724if (len > 128) {725pktdata = (uint8 *)PKTDATA(osh, p); /* starting address of data */726/* Check for page boundary straddle (2048B) */727if (((uintptr)pktdata & ~0x7ff) != ((uintptr)(pktdata+len) & ~0x7ff))728cnt++;729730align64 = (uint)((uintptr)pktdata & 0x3f); /* aligned to 64B */731align64 = (64 - align64) & 0x3f;732len -= align64; /* bytes from aligned 64B to end */733/* if aligned to 128B, check for MOD 128 between 1 to 4B */734remain = len % 128;735if (remain > 0 && remain <= 4)736cnt++; /* add extra seg */737}738}739740return cnt;741}742743uint8 * BCMFASTPATH744pktdataoffset(osl_t *osh, void *p, uint offset)745{746uint total = pkttotlen(osh, p);747uint pkt_off = 0, len = 0;748uint8 *pdata = (uint8 *) PKTDATA(osh, p);749750if (offset > total)751return NULL;752753for (; p; p = PKTNEXT(osh, p)) {754pdata = (uint8 *) PKTDATA(osh, p);755pkt_off = offset - len;756len += PKTLEN(osh, p);757if (len > offset)758break;759}760return (uint8*) (pdata+pkt_off);761}762763764/* given a offset in pdata, find the pkt seg hdr */765void *766pktoffset(osl_t *osh, void *p, uint offset)767{768uint total = pkttotlen(osh, p);769uint len = 0;770771if (offset > total)772return NULL;773774for (; p; p = PKTNEXT(osh, p)) {775len += PKTLEN(osh, p);776if (len > offset)777break;778}779return p;780}781782/*783* osl multiple-precedence packet queue784* hi_prec is always >= the number of the highest non-empty precedence785*/786void * BCMFASTPATH787pktq_penq(struct pktq *pq, int prec, void *p)788{789struct pktq_prec *q;790791ASSERT(prec >= 0 && prec < pq->num_prec);792ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */793794ASSERT(!pktq_full(pq));795ASSERT(!pktq_pfull(pq, prec));796797q = &pq->q[prec];798799if (q->head)800PKTSETLINK(q->tail, p);801else802q->head = p;803804q->tail = p;805q->len++;806807pq->len++;808809if (pq->hi_prec < prec)810pq->hi_prec = (uint8)prec;811812return p;813}814815void * BCMFASTPATH816pktq_penq_head(struct pktq *pq, int prec, void *p)817{818struct pktq_prec *q;819820ASSERT(prec >= 0 && prec < pq->num_prec);821ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */822823ASSERT(!pktq_full(pq));824ASSERT(!pktq_pfull(pq, prec));825826q = &pq->q[prec];827828if (q->head == NULL)829q->tail = p;830831PKTSETLINK(p, q->head);832q->head = p;833q->len++;834835pq->len++;836837if (pq->hi_prec < prec)838pq->hi_prec = (uint8)prec;839840return p;841}842843void * BCMFASTPATH844pktq_pdeq(struct pktq *pq, int prec)845{846struct pktq_prec *q;847void *p;848849ASSERT(prec >= 0 && prec < pq->num_prec);850851q = &pq->q[prec];852853if ((p = q->head) == NULL)854return NULL;855856if ((q->head = PKTLINK(p)) == NULL)857q->tail = NULL;858859q->len--;860861pq->len--;862863PKTSETLINK(p, NULL);864865return p;866}867868void * BCMFASTPATH869pktq_pdeq_prev(struct pktq *pq, int prec, void *prev_p)870{871struct pktq_prec *q;872void *p;873874ASSERT(prec >= 0 && prec < pq->num_prec);875876q = &pq->q[prec];877878if (prev_p == NULL)879return NULL;880881if ((p = PKTLINK(prev_p)) == NULL)882return NULL;883884if (q->tail == p)885q->tail = prev_p;886887q->len--;888889pq->len--;890891PKTSETLINK(prev_p, PKTLINK(p));892PKTSETLINK(p, NULL);893894return p;895}896897void * BCMFASTPATH898pktq_pdeq_tail(struct pktq *pq, int prec)899{900struct pktq_prec *q;901void *p, *prev;902903ASSERT(prec >= 0 && prec < pq->num_prec);904905q = &pq->q[prec];906907if ((p = q->head) == NULL)908return NULL;909910for (prev = NULL; p != q->tail; p = PKTLINK(p))911prev = p;912913if (prev)914PKTSETLINK(prev, NULL);915else916q->head = NULL;917918q->tail = prev;919q->len--;920921pq->len--;922923return p;924}925926void927pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ifpkt_cb_t fn, int arg)928{929struct pktq_prec *q;930void *p, *prev = NULL;931932q = &pq->q[prec];933p = q->head;934while (p) {935if (fn == NULL || (*fn)(p, arg)) {936bool head = (p == q->head);937if (head)938q->head = PKTLINK(p);939else940PKTSETLINK(prev, PKTLINK(p));941PKTSETLINK(p, NULL);942PKTFREE(osh, p, dir);943q->len--;944pq->len--;945p = (head ? q->head : PKTLINK(prev));946} else {947prev = p;948p = PKTLINK(p);949}950}951952if (q->head == NULL) {953ASSERT(q->len == 0);954q->tail = NULL;955}956}957958bool BCMFASTPATH959pktq_pdel(struct pktq *pq, void *pktbuf, int prec)960{961struct pktq_prec *q;962void *p;963964ASSERT(prec >= 0 && prec < pq->num_prec);965966/* XXX Should this just assert pktbuf? */967if (!pktbuf)968return FALSE;969970q = &pq->q[prec];971972if (q->head == pktbuf) {973if ((q->head = PKTLINK(pktbuf)) == NULL)974q->tail = NULL;975} else {976for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p))977;978if (p == NULL)979return FALSE;980981PKTSETLINK(p, PKTLINK(pktbuf));982if (q->tail == pktbuf)983q->tail = p;984}985986q->len--;987pq->len--;988PKTSETLINK(pktbuf, NULL);989return TRUE;990}991992void993pktq_init(struct pktq *pq, int num_prec, int max_len)994{995int prec;996997ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC);998999/* pq is variable size; only zero out what's requested */1000bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));10011002pq->num_prec = (uint16)num_prec;10031004pq->max = (uint16)max_len;10051006for (prec = 0; prec < num_prec; prec++)1007pq->q[prec].max = pq->max;1008}10091010void1011pktq_set_max_plen(struct pktq *pq, int prec, int max_len)1012{1013ASSERT(prec >= 0 && prec < pq->num_prec);10141015if (prec < pq->num_prec)1016pq->q[prec].max = (uint16)max_len;1017}10181019void * BCMFASTPATH1020pktq_deq(struct pktq *pq, int *prec_out)1021{1022struct pktq_prec *q;1023void *p;1024int prec;10251026if (pq->len == 0)1027return NULL;10281029while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)1030pq->hi_prec--;10311032q = &pq->q[prec];10331034if ((p = q->head) == NULL)1035return NULL;10361037if ((q->head = PKTLINK(p)) == NULL)1038q->tail = NULL;10391040q->len--;10411042pq->len--;10431044if (prec_out)1045*prec_out = prec;10461047PKTSETLINK(p, NULL);10481049return p;1050}10511052void * BCMFASTPATH1053pktq_deq_tail(struct pktq *pq, int *prec_out)1054{1055struct pktq_prec *q;1056void *p, *prev;1057int prec;10581059if (pq->len == 0)1060return NULL;10611062for (prec = 0; prec < pq->hi_prec; prec++)1063if (pq->q[prec].head)1064break;10651066q = &pq->q[prec];10671068if ((p = q->head) == NULL)1069return NULL;10701071for (prev = NULL; p != q->tail; p = PKTLINK(p))1072prev = p;10731074if (prev)1075PKTSETLINK(prev, NULL);1076else1077q->head = NULL;10781079q->tail = prev;1080q->len--;10811082pq->len--;10831084if (prec_out)1085*prec_out = prec;10861087PKTSETLINK(p, NULL);10881089return p;1090}10911092void *1093pktq_peek(struct pktq *pq, int *prec_out)1094{1095int prec;10961097if (pq->len == 0)1098return NULL;10991100while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)1101pq->hi_prec--;11021103if (prec_out)1104*prec_out = prec;11051106return (pq->q[prec].head);1107}11081109void *1110pktq_peek_tail(struct pktq *pq, int *prec_out)1111{1112int prec;11131114if (pq->len == 0)1115return NULL;11161117for (prec = 0; prec < pq->hi_prec; prec++)1118if (pq->q[prec].head)1119break;11201121if (prec_out)1122*prec_out = prec;11231124return (pq->q[prec].tail);1125}11261127void1128pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg)1129{1130int prec;11311132/* Optimize flush, if pktq len = 0, just return.1133* pktq len of 0 means pktq's prec q's are all empty.1134*/1135if (pq->len == 0) {1136return;1137}11381139for (prec = 0; prec < pq->num_prec; prec++)1140pktq_pflush(osh, pq, prec, dir, fn, arg);1141if (fn == NULL)1142ASSERT(pq->len == 0);1143}11441145/* Return sum of lengths of a specific set of precedences */1146int1147pktq_mlen(struct pktq *pq, uint prec_bmp)1148{1149int prec, len;11501151len = 0;11521153for (prec = 0; prec <= pq->hi_prec; prec++)1154if (prec_bmp & (1 << prec))1155len += pq->q[prec].len;11561157return len;1158}11591160/* Priority peek from a specific set of precedences */1161void * BCMFASTPATH1162pktq_mpeek(struct pktq *pq, uint prec_bmp, int *prec_out)1163{1164struct pktq_prec *q;1165void *p;1166int prec;11671168if (pq->len == 0)1169{1170return NULL;1171}1172while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)1173pq->hi_prec--;11741175while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)1176if (prec-- == 0)1177return NULL;11781179q = &pq->q[prec];11801181if ((p = q->head) == NULL)1182return NULL;11831184if (prec_out)1185*prec_out = prec;11861187return p;1188}1189/* Priority dequeue from a specific set of precedences */1190void * BCMFASTPATH1191pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out)1192{1193struct pktq_prec *q;1194void *p;1195int prec;11961197if (pq->len == 0)1198return NULL;11991200while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)1201pq->hi_prec--;12021203while ((pq->q[prec].head == NULL) || ((prec_bmp & (1 << prec)) == 0))1204if (prec-- == 0)1205return NULL;12061207q = &pq->q[prec];12081209if ((p = q->head) == NULL)1210return NULL;12111212if ((q->head = PKTLINK(p)) == NULL)1213q->tail = NULL;12141215q->len--;12161217if (prec_out)1218*prec_out = prec;12191220pq->len--;12211222PKTSETLINK(p, NULL);12231224return p;1225}12261227#endif /* BCMDRIVER */12281229#if defined(BCMROMBUILD)1230const unsigned char BCMROMDATA(bcm_ctype)[] = {1231#else1232const unsigned char bcm_ctype[] = {1233#endif12341235_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 0-7 */1236_BCM_C, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C,1237_BCM_C, /* 8-15 */1238_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 16-23 */1239_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 24-31 */1240_BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 32-39 */1241_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 40-47 */1242_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D, /* 48-55 */1243_BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 56-63 */1244_BCM_P, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X,1245_BCM_U|_BCM_X, _BCM_U, /* 64-71 */1246_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 72-79 */1247_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 80-87 */1248_BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 88-95 */1249_BCM_P, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X,1250_BCM_L|_BCM_X, _BCM_L, /* 96-103 */1251_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */1252_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */1253_BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */12540, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-143 */12550, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144-159 */1256_BCM_S|_BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,1257_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 160-175 */1258_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,1259_BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 176-191 */1260_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,1261_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, /* 192-207 */1262_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U, _BCM_U, _BCM_U,1263_BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L, /* 208-223 */1264_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,1265_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, /* 224-239 */1266_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L, _BCM_L, _BCM_L,1267_BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */1268};12691270ulong1271BCMROMFN(bcm_strtoul)(const char *cp, char **endp, uint base)1272{1273ulong result, last_result = 0, value;1274bool minus;12751276minus = FALSE;12771278while (bcm_isspace(*cp))1279cp++;12801281if (cp[0] == '+')1282cp++;1283else if (cp[0] == '-') {1284minus = TRUE;1285cp++;1286}12871288if (base == 0) {1289if (cp[0] == '0') {1290if ((cp[1] == 'x') || (cp[1] == 'X')) {1291base = 16;1292cp = &cp[2];1293} else {1294base = 8;1295cp = &cp[1];1296}1297} else1298base = 10;1299} else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {1300cp = &cp[2];1301}13021303result = 0;13041305while (bcm_isxdigit(*cp) &&1306(value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) {1307result = result*base + value;1308/* Detected overflow */1309if (result < last_result && !minus)1310return (ulong)-1;1311last_result = result;1312cp++;1313}13141315if (minus)1316result = (ulong)(-(long)result);13171318if (endp)1319*endp = DISCARD_QUAL(cp, char);13201321return (result);1322}13231324int1325BCMROMFN(bcm_atoi)(const char *s)1326{1327return (int)bcm_strtoul(s, NULL, 10);1328}13291330/* return pointer to location of substring 'needle' in 'haystack' */1331char *1332BCMROMFN(bcmstrstr)(const char *haystack, const char *needle)1333{1334int len, nlen;1335int i;13361337if ((haystack == NULL) || (needle == NULL))1338return DISCARD_QUAL(haystack, char);13391340nlen = strlen(needle);1341len = strlen(haystack) - nlen + 1;13421343for (i = 0; i < len; i++)1344if (memcmp(needle, &haystack[i], nlen) == 0)1345return DISCARD_QUAL(&haystack[i], char);1346return (NULL);1347}13481349char *1350BCMROMFN(bcmstrcat)(char *dest, const char *src)1351{1352char *p;13531354p = dest + strlen(dest);13551356while ((*p++ = *src++) != '\0')1357;13581359return (dest);1360}13611362char *1363BCMROMFN(bcmstrncat)(char *dest, const char *src, uint size)1364{1365char *endp;1366char *p;13671368p = dest + strlen(dest);1369endp = p + size;13701371while (p != endp && (*p++ = *src++) != '\0')1372;13731374return (dest);1375}137613771378/****************************************************************************1379* Function: bcmstrtok1380*1381* Purpose:1382* Tokenizes a string. This function is conceptually similiar to ANSI C strtok(),1383* but allows strToken() to be used by different strings or callers at the same1384* time. Each call modifies '*string' by substituting a NULL character for the1385* first delimiter that is encountered, and updates 'string' to point to the char1386* after the delimiter. Leading delimiters are skipped.1387*1388* Parameters:1389* string (mod) Ptr to string ptr, updated by token.1390* delimiters (in) Set of delimiter characters.1391* tokdelim (out) Character that delimits the returned token. (May1392* be set to NULL if token delimiter is not required).1393*1394* Returns: Pointer to the next token found. NULL when no more tokens are found.1395*****************************************************************************1396*/1397char *1398bcmstrtok(char **string, const char *delimiters, char *tokdelim)1399{1400unsigned char *str;1401unsigned long map[8];1402int count;1403char *nextoken;14041405if (tokdelim != NULL) {1406/* Prime the token delimiter */1407*tokdelim = '\0';1408}14091410/* Clear control map */1411for (count = 0; count < 8; count++) {1412map[count] = 0;1413}14141415/* Set bits in delimiter table */1416do {1417map[*delimiters >> 5] |= (1 << (*delimiters & 31));1418}1419while (*delimiters++);14201421str = (unsigned char*)*string;14221423/* Find beginning of token (skip over leading delimiters). Note that1424* there is no token iff this loop sets str to point to the terminal1425* null (*str == '\0')1426*/1427while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) {1428str++;1429}14301431nextoken = (char*)str;14321433/* Find the end of the token. If it is not the end of the string,1434* put a null there.1435*/1436for (; *str; str++) {1437if (map[*str >> 5] & (1 << (*str & 31))) {1438if (tokdelim != NULL) {1439*tokdelim = *str;1440}14411442*str++ = '\0';1443break;1444}1445}14461447*string = (char*)str;14481449/* Determine if a token has been found. */1450if (nextoken == (char *) str) {1451return NULL;1452}1453else {1454return nextoken;1455}1456}145714581459#define xToLower(C) \1460((C >= 'A' && C <= 'Z') ? (char)((int)C - (int)'A' + (int)'a') : C)146114621463/****************************************************************************1464* Function: bcmstricmp1465*1466* Purpose: Compare to strings case insensitively.1467*1468* Parameters: s1 (in) First string to compare.1469* s2 (in) Second string to compare.1470*1471* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if1472* t1 > t2, when ignoring case sensitivity.1473*****************************************************************************1474*/1475int1476bcmstricmp(const char *s1, const char *s2)1477{1478char dc, sc;14791480while (*s2 && *s1) {1481dc = xToLower(*s1);1482sc = xToLower(*s2);1483if (dc < sc) return -1;1484if (dc > sc) return 1;1485s1++;1486s2++;1487}14881489if (*s1 && !*s2) return 1;1490if (!*s1 && *s2) return -1;1491return 0;1492}149314941495/****************************************************************************1496* Function: bcmstrnicmp1497*1498* Purpose: Compare to strings case insensitively, upto a max of 'cnt'1499* characters.1500*1501* Parameters: s1 (in) First string to compare.1502* s2 (in) Second string to compare.1503* cnt (in) Max characters to compare.1504*1505* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if1506* t1 > t2, when ignoring case sensitivity.1507*****************************************************************************1508*/1509int1510bcmstrnicmp(const char* s1, const char* s2, int cnt)1511{1512char dc, sc;15131514while (*s2 && *s1 && cnt) {1515dc = xToLower(*s1);1516sc = xToLower(*s2);1517if (dc < sc) return -1;1518if (dc > sc) return 1;1519s1++;1520s2++;1521cnt--;1522}15231524if (!cnt) return 0;1525if (*s1 && !*s2) return 1;1526if (!*s1 && *s2) return -1;1527return 0;1528}15291530/* parse a xx:xx:xx:xx:xx:xx format ethernet address */1531int1532BCMROMFN(bcm_ether_atoe)(const char *p, struct ether_addr *ea)1533{1534int i = 0;1535char *ep;15361537for (;;) {1538ea->octet[i++] = (char) bcm_strtoul(p, &ep, 16);1539p = ep;1540if (!*p++ || i == 6)1541break;1542}15431544return (i == 6);1545}15461547#ifdef _HNDRTE_15481549const struct ether_addr ether_bcast = {{255, 255, 255, 255, 255, 255}};1550const struct ether_addr ether_null = {{0, 0, 0, 0, 0, 0}};1551const struct ether_addr ether_ipv6_mcast = {{0x33, 0x33, 0x00, 0x00, 0x00, 0x01}};15521553int1554ether_isbcast(const void *ea)1555{1556return (memcmp(ea, ðer_bcast, sizeof(struct ether_addr)) == 0);1557}15581559int1560ether_isnulladdr(const void *ea)1561{1562return (memcmp(ea, ðer_null, sizeof(struct ether_addr)) == 0);1563}15641565#endif /* _HNDRTE_ */15661567#if defined(CONFIG_USBRNDIS_RETAIL) || defined(NDIS_MINIPORT_DRIVER)1568/* registry routine buffer preparation utility functions:1569* parameter order is like strncpy, but returns count1570* of bytes copied. Minimum bytes copied is null char(1)/wchar(2)1571*/1572ulong1573wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen)1574{1575ulong copyct = 1;1576ushort i;15771578if (abuflen == 0)1579return 0;15801581/* wbuflen is in bytes */1582wbuflen /= sizeof(ushort);15831584for (i = 0; i < wbuflen; ++i) {1585if (--abuflen == 0)1586break;1587*abuf++ = (char) *wbuf++;1588++copyct;1589}1590*abuf = '\0';15911592return copyct;1593}1594#endif /* CONFIG_USBRNDIS_RETAIL || NDIS_MINIPORT_DRIVER */15951596char *1597bcm_ether_ntoa(const struct ether_addr *ea, char *buf)1598{1599static const char hex[] =1600{1601'0', '1', '2', '3', '4', '5', '6', '7',1602'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'1603};1604const uint8 *octet = ea->octet;1605char *p = buf;1606int i;16071608for (i = 0; i < 6; i++, octet++) {1609*p++ = hex[(*octet >> 4) & 0xf];1610*p++ = hex[*octet & 0xf];1611*p++ = ':';1612}16131614*(p-1) = '\0';16151616return (buf);1617}16181619char *1620bcm_ip_ntoa(struct ipv4_addr *ia, char *buf)1621{1622snprintf(buf, 16, "%d.%d.%d.%d",1623ia->addr[0], ia->addr[1], ia->addr[2], ia->addr[3]);1624return (buf);1625}16261627char *1628bcm_ipv6_ntoa(void *ipv6, char *buf)1629{1630/* Implementing RFC 5952 Sections 4 + 5 */1631/* Not thoroughly tested */1632uint16 *a = (uint16 *)ipv6;1633char *p = buf;1634int i, i_max = -1, cnt = 0, cnt_max = 1;1635uint8 *a4 = NULL;16361637for (i = 0; i < IPV6_ADDR_LEN/2; i++) {1638if (a[i]) {1639if (cnt > cnt_max) {1640cnt_max = cnt;1641i_max = i - cnt;1642}1643cnt = 0;1644} else1645cnt++;1646}1647if (cnt > cnt_max) {1648cnt_max = cnt;1649i_max = i - cnt;1650}1651if (i_max == 0 &&1652/* IPv4-translated: ::ffff:0:a.b.c.d */1653((cnt_max == 4 && a[4] == 0xffff && a[5] == 0) ||1654/* IPv4-mapped: ::ffff:a.b.c.d */1655(cnt_max == 5 && a[5] == 0xffff)))1656a4 = (uint8*) (a + 6);16571658for (i = 0; i < IPV6_ADDR_LEN/2; i++) {1659if ((uint8*) (a + i) == a4) {1660snprintf(p, 16, ":%u.%u.%u.%u", a4[0], a4[1], a4[2], a4[3]);1661break;1662} else if (i == i_max) {1663*p++ = ':';1664i += cnt_max - 1;1665p[0] = ':';1666p[1] = '\0';1667} else {1668if (i)1669*p++ = ':';1670p += snprintf(p, 8, "%x", ntoh16(a[i]));1671}1672}16731674return buf;1675}16761677#ifdef BCMDRIVER16781679void1680bcm_mdelay(uint ms)1681{1682uint i;16831684for (i = 0; i < ms; i++) {1685OSL_DELAY(1000);1686}1687}16881689#if !defined(BCMDONGLEHOST)1690/*1691* Search the name=value vars for a specific one and return its value.1692* Returns NULL if not found.1693*/1694char *1695getvar(char *vars, const char *name)1696{1697#ifdef _MINOSL_1698return NULL;1699#else1700char *s;1701int len;17021703if (!name)1704return NULL;17051706len = strlen(name);1707if (len == 0)1708return NULL;17091710/* first look in vars[] */1711for (s = vars; s && *s;) {1712if ((bcmp(s, name, len) == 0) && (s[len] == '='))1713return (&s[len+1]);17141715while (*s++)1716;1717}17181719/* then query nvram */1720return (nvram_get(name));1721#endif /* defined(_MINOSL_) */1722}17231724/*1725* Search the vars for a specific one and return its value as1726* an integer. Returns 0 if not found.1727*/1728int1729getintvar(char *vars, const char *name)1730{1731#ifdef _MINOSL_1732return 0;1733#else1734char *val;17351736if ((val = getvar(vars, name)) == NULL)1737return (0);17381739return (bcm_strtoul(val, NULL, 0));1740#endif /* _MINOSL_ */1741}17421743int1744getintvararray(char *vars, const char *name, int index)1745{1746#ifdef _MINOSL_1747return 0;1748#else1749char *buf, *endp;1750int i = 0;1751int val = 0;17521753if ((buf = getvar(vars, name)) == NULL) {1754return (0);1755}17561757/* table values are always separated by "," or " " */1758while (*buf != '\0') {1759val = bcm_strtoul(buf, &endp, 0);1760if (i == index) {1761return val;1762}1763buf = endp;1764/* delimiter is ',' */1765if (*buf == ',')1766buf++;1767i++;1768}1769return 0;1770#endif /* _MINOSL_ */1771}17721773int1774getintvararraysize(char *vars, const char *name)1775{1776#ifdef _MINOSL_1777return 0;1778#else1779char *buf, *endp;1780int count = 0;1781int val = 0;17821783if ((buf = getvar(vars, name)) == NULL) {1784return (0);1785}17861787/* table values are always separated by "," or " " */1788while (*buf != '\0') {1789val = bcm_strtoul(buf, &endp, 0);1790buf = endp;1791/* delimiter is ',' */1792if (*buf == ',')1793buf++;1794count++;1795}1796BCM_REFERENCE(val);1797return count;1798#endif /* _MINOSL_ */1799}18001801/* Search for token in comma separated token-string */1802static int1803findmatch(const char *string, const char *name)1804{1805uint len;1806char *c;18071808len = strlen(name);1809while ((c = strchr(string, ',')) != NULL) {1810if (len == (uint)(c - string) && !strncmp(string, name, len))1811return 1;1812string = c + 1;1813}18141815return (!strcmp(string, name));1816}18171818/* Return gpio pin number assigned to the named pin1819*1820* Variable should be in format:1821*1822* gpio<N>=pin_name,pin_name1823*1824* This format allows multiple features to share the gpio with mutual1825* understanding.1826*1827* 'def_pin' is returned if a specific gpio is not defined for the requested functionality1828* and if def_pin is not used by others.1829*/1830uint1831getgpiopin(char *vars, char *pin_name, uint def_pin)1832{1833char name[] = "gpioXXXX";1834char *val;1835uint pin;18361837/* Go thru all possibilities till a match in pin name */1838for (pin = 0; pin < GPIO_NUMPINS; pin ++) {1839snprintf(name, sizeof(name), "gpio%d", pin);1840val = getvar(vars, name);1841if (val && findmatch(val, pin_name))1842return pin;1843}18441845if (def_pin != GPIO_PIN_NOTDEFINED) {1846/* make sure the default pin is not used by someone else */1847snprintf(name, sizeof(name), "gpio%d", def_pin);1848if (getvar(vars, name)) {1849def_pin = GPIO_PIN_NOTDEFINED;1850}1851}1852return def_pin;1853}1854#endif /* !defined(BCMDONGLEHOST) */18551856#if defined(BCMPERFSTATS) || defined(BCMTSTAMPEDLOGS)18571858#define LOGSIZE 256 /* should be power of 2 to avoid div below */1859static struct {1860uint cycles;1861char *fmt;1862uint a1;1863uint a2;1864} logtab[LOGSIZE];18651866/* last entry logged */1867static uint logi = 0;1868/* next entry to read */1869static uint readi = 0;1870#endif /* defined(BCMPERFSTATS) || defined(BCMTSTAMPEDLOGS) */18711872#ifdef BCMPERFSTATS1873/* XXX TODO: make the utility configurable (choose between icache, dcache, hits, misses ...) */1874void1875bcm_perf_enable()1876{1877BCMPERF_ENABLE_INSTRCOUNT();1878BCMPERF_ENABLE_ICACHE_MISS();1879BCMPERF_ENABLE_ICACHE_HIT();1880}18811882/* WARNING: This routine uses OSL_GETCYCLES(), which can give unexpected results on1883* modern speed stepping CPUs. Use bcmtslog() instead in combination with TSF counter.1884*/1885void1886bcmlog(char *fmt, uint a1, uint a2)1887{1888static uint last = 0;1889uint cycles, i;1890OSL_GETCYCLES(cycles);18911892i = logi;18931894logtab[i].cycles = cycles - last;1895logtab[i].fmt = fmt;1896logtab[i].a1 = a1;1897logtab[i].a2 = a2;18981899logi = (i + 1) % LOGSIZE;1900last = cycles;1901}19021903/* Same as bcmlog but specializes the use of a1 and a2 to1904* store icache misses and instruction count.1905* XXX TODO : make this use a configuration array to decide what counter to read.1906* We are limited to 2 numbers but it seems it is the most we can get anyway1907* since dcache and icache cannot be enabled at the same time. Recording1908* both the hits and misses at the same time for a given cache is not that useful either.1909*/19101911void1912bcmstats(char *fmt)1913{1914static uint last = 0;1915static uint32 ic_miss = 0;1916static uint32 instr_count = 0;1917uint32 ic_miss_cur;1918uint32 instr_count_cur;1919uint cycles, i;19201921OSL_GETCYCLES(cycles);1922BCMPERF_GETICACHE_MISS(ic_miss_cur);1923BCMPERF_GETINSTRCOUNT(instr_count_cur);19241925i = logi;19261927logtab[i].cycles = cycles - last;1928logtab[i].a1 = ic_miss_cur - ic_miss;1929logtab[i].a2 = instr_count_cur - instr_count;1930logtab[i].fmt = fmt;19311932logi = (i + 1) % LOGSIZE;19331934last = cycles;1935instr_count = instr_count_cur;1936ic_miss = ic_miss_cur;1937}19381939/*1940* XXX TODO (linux version): a "proc" version where the log would be dumped1941* on the proc file directly.1942*/19431944void1945bcmdumplog(char *buf, int size)1946{1947char *limit;1948int j = 0;1949int num;19501951limit = buf + size - 80;1952*buf = '\0';19531954num = logi - readi;19551956if (num < 0)1957num += LOGSIZE;19581959/* print in chronological order */19601961for (j = 0; j < num && (buf < limit); readi = (readi + 1) % LOGSIZE, j++) {1962if (logtab[readi].fmt == NULL)1963continue;1964buf += snprintf(buf, (limit - buf), "%d\t", logtab[readi].cycles);1965buf += snprintf(buf, (limit - buf), logtab[readi].fmt, logtab[readi].a1,1966logtab[readi].a2);1967buf += snprintf(buf, (limit - buf), "\n");1968}19691970}197119721973/*1974* Dump one log entry at a time.1975* Return index of next entry or -1 when no more .1976*/1977int1978bcmdumplogent(char *buf, uint i)1979{1980bool hit;19811982/*1983* If buf is NULL, return the starting index,1984* interpreting i as the indicator of last 'i' entries to dump.1985*/1986if (buf == NULL) {1987i = ((i > 0) && (i < (LOGSIZE - 1))) ? i : (LOGSIZE - 1);1988return ((logi - i) % LOGSIZE);1989}19901991*buf = '\0';19921993ASSERT(i < LOGSIZE);19941995if (i == logi)1996return (-1);19971998hit = FALSE;1999for (; (i != logi) && !hit; i = (i + 1) % LOGSIZE) {2000if (logtab[i].fmt == NULL)2001continue;2002buf += sprintf(buf, "%d: %d\t", i, logtab[i].cycles);2003buf += sprintf(buf, logtab[i].fmt, logtab[i].a1, logtab[i].a2);2004buf += sprintf(buf, "\n");2005hit = TRUE;2006}20072008return (i);2009}20102011#endif /* BCMPERFSTATS */20122013#if defined(BCMTSTAMPEDLOGS)2014/* Store a TSF timestamp and a log line in the log buffer */2015void2016bcmtslog(uint32 tstamp, char *fmt, uint a1, uint a2)2017{2018uint i = logi;2019bool use_delta = FALSE;2020static uint32 last = 0; /* used only when use_delta is true */20212022logtab[i].cycles = tstamp;2023if (use_delta)2024logtab[i].cycles -= last;20252026logtab[i].fmt = fmt;2027logtab[i].a1 = a1;2028logtab[i].a2 = a2;20292030if (use_delta)2031last = tstamp;2032logi = (i + 1) % LOGSIZE;2033}20342035/* Print out a microsecond timestamp as "sec.ms.us " */2036void2037bcmprinttstamp(uint32 ticks)2038{2039uint us, ms, sec;20402041us = (ticks % TSF_TICKS_PER_MS) * 1000 / TSF_TICKS_PER_MS;2042ms = ticks / TSF_TICKS_PER_MS;2043sec = ms / 1000;2044ms -= sec * 1000;2045printf("%04u.%03u.%03u ", sec, ms, us);2046}20472048/* Print out the log buffer with timestamps */2049void2050bcmprinttslogs(void)2051{2052int j = 0;2053int num;20542055num = logi - readi;2056if (num < 0)2057num += LOGSIZE;20582059/* Format and print the log entries directly in chronological order */2060for (j = 0; j < num; readi = (readi + 1) % LOGSIZE, j++) {2061if (logtab[readi].fmt == NULL)2062continue;2063bcmprinttstamp(logtab[readi].cycles);2064printf(logtab[readi].fmt, logtab[readi].a1, logtab[readi].a2);2065printf("\n");2066}2067}20682069/* Identical to bcmdumplog, but output is based on tsf instead of cycles.2070XXX Todo:2071These logging and printing/dumping routines have become too numerous.2072Simplify by combining routines and adding parameters for TSF vs System clock,2073printing vs dumping to buffer, and whether output shoud be normalized to usecs.2074Also, why are some routines #ifdeffed and others not?2075Also, rdtscl((x)) used in linux OSL_GETCYCLES should be avoided since this will slow2076down and speed up on speed-stepping cpus.2077*/2078void2079bcmdumptslog(char *buf, int size)2080{2081char *limit;2082int j = 0;2083int num;2084uint us, ms, sec;20852086limit = buf + size - 80;2087*buf = '\0';20882089num = logi - readi;20902091if (num < 0)2092num += LOGSIZE;20932094/* print in chronological order */2095for (j = 0; j < num && (buf < limit); readi = (readi + 1) % LOGSIZE, j++) {2096if (logtab[readi].fmt == NULL)2097continue;2098us = (logtab[readi].cycles % TSF_TICKS_PER_MS) * 1000 / TSF_TICKS_PER_MS;2099ms = logtab[readi].cycles / TSF_TICKS_PER_MS;2100sec = ms / 1000;2101ms -= sec * 1000;21022103buf += snprintf(buf, (limit - buf), "%04u.%03u.%03u ", sec, ms, us);2104/* buf += snprintf(buf, (limit - buf), "%d\t", logtab[readi].cycles); */2105buf += snprintf(buf, (limit - buf), logtab[readi].fmt, logtab[readi].a1,2106logtab[readi].a2);2107buf += snprintf(buf, (limit - buf), "\n");2108}2109}21102111#endif /* BCMTSTAMPEDLOGS */21122113#if defined(BCMDBG) || defined(DHD_DEBUG)2114/* pretty hex print a pkt buffer chain */2115void2116prpkt(const char *msg, osl_t *osh, void *p0)2117{2118void *p;21192120if (msg && (msg[0] != '\0'))2121printf("%s:\n", msg);21222123for (p = p0; p; p = PKTNEXT(osh, p))2124prhex(NULL, PKTDATA(osh, p), PKTLEN(osh, p));2125}2126#endif /* BCMDBG || DHD_DEBUG */21272128/* Takes an Ethernet frame and sets out-of-bound PKTPRIO.2129* Also updates the inplace vlan tag if requested.2130* For debugging, it returns an indication of what it did.2131*/2132uint BCMFASTPATH2133pktsetprio(void *pkt, bool update_vtag)2134{2135struct ether_header *eh;2136struct ethervlan_header *evh;2137uint8 *pktdata;2138int priority = 0;2139int rc = 0;21402141pktdata = (uint8 *)PKTDATA(NULL, pkt);2142ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16)));21432144eh = (struct ether_header *) pktdata;21452146if (eh->ether_type == hton16(ETHER_TYPE_8021Q)) {2147uint16 vlan_tag;2148int vlan_prio, dscp_prio = 0;21492150evh = (struct ethervlan_header *)eh;21512152vlan_tag = ntoh16(evh->vlan_tag);2153vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK;21542155if (evh->ether_type == hton16(ETHER_TYPE_IP)) {2156uint8 *ip_body = pktdata + sizeof(struct ethervlan_header);2157uint8 tos_tc = IP_TOS46(ip_body);2158dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);2159}21602161/* DSCP priority gets precedence over 802.1P (vlan tag) */2162if (dscp_prio != 0) {2163priority = dscp_prio;2164rc |= PKTPRIO_VDSCP;2165} else {2166priority = vlan_prio;2167rc |= PKTPRIO_VLAN;2168}2169/*2170* If the DSCP priority is not the same as the VLAN priority,2171* then overwrite the priority field in the vlan tag, with the2172* DSCP priority value. This is required for Linux APs because2173* the VLAN driver on Linux, overwrites the skb->priority field2174* with the priority value in the vlan tag2175*/2176if (update_vtag && (priority != vlan_prio)) {2177vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT);2178vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT;2179evh->vlan_tag = hton16(vlan_tag);2180rc |= PKTPRIO_UPD;2181}2182} else if (eh->ether_type == hton16(ETHER_TYPE_IP)) {2183uint8 *ip_body = pktdata + sizeof(struct ether_header);2184uint8 tos_tc = IP_TOS46(ip_body);2185priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);2186rc |= PKTPRIO_DSCP;2187}21882189ASSERT(priority >= 0 && priority <= MAXPRIO);2190PKTSETPRIO(pkt, priority);2191return (rc | priority);2192}21932194#ifndef BCM_BOOTLOADER2195/* XXX: The 0.5KB string table is not removed by compiler even though it's unused */21962197static char bcm_undeferrstr[32];2198static const char *const bcmerrorstrtable[] = BCMERRSTRINGTABLE;21992200/* Convert the error codes into related error strings */2201const char *2202bcmerrorstr(int bcmerror)2203{2204/* check if someone added a bcmerror code but forgot to add errorstring */2205ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1));22062207if (bcmerror > 0 || bcmerror < BCME_LAST) {2208snprintf(bcm_undeferrstr, sizeof(bcm_undeferrstr), "Undefined error %d", bcmerror);2209return bcm_undeferrstr;2210}22112212ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN);22132214return bcmerrorstrtable[-bcmerror];2215}22162217#endif /* !BCM_BOOTLOADER */22182219#ifdef WLC_LOW2220static void2221BCMINITFN(bcm_nvram_refresh)(char *flash)2222{2223int i;2224int ret = 0;22252226ASSERT(flash != NULL);22272228/* default "empty" vars cache */2229bzero(flash, 2);22302231if ((ret = nvram_getall(flash, MAX_NVRAM_SPACE)))2232return;22332234/* determine nvram length */2235for (i = 0; i < MAX_NVRAM_SPACE; i++) {2236if (flash[i] == '\0' && flash[i+1] == '\0')2237break;2238}22392240if (i > 1)2241vars_len = i + 2;2242else2243vars_len = 0;2244}22452246char *2247bcm_nvram_vars(uint *length)2248{2249#ifndef BCMNVRAMR2250/* cache may be stale if nvram is read/write */2251if (nvram_vars) {2252ASSERT(!bcmreclaimed);2253bcm_nvram_refresh(nvram_vars);2254}2255#endif2256if (length)2257*length = vars_len;2258return nvram_vars;2259}22602261/* copy nvram vars into locally-allocated multi-string array */2262int2263BCMINITFN(bcm_nvram_cache)(void *sih)2264{2265int ret = 0;2266void *osh;2267char *flash = NULL;22682269if (vars_len >= 0) {2270#ifndef BCMNVRAMR2271bcm_nvram_refresh(nvram_vars);2272#endif2273return 0;2274}22752276osh = si_osh((si_t *)sih);22772278/* allocate memory and read in flash */2279if (!(flash = MALLOC(osh, MAX_NVRAM_SPACE))) {2280ret = BCME_NOMEM;2281goto exit;2282}22832284bcm_nvram_refresh(flash);22852286#ifdef BCMNVRAMR2287if (vars_len > 3) {2288/* copy into a properly-sized buffer */2289if (!(nvram_vars = MALLOC(osh, vars_len))) {2290ret = BCME_NOMEM;2291} else2292bcopy(flash, nvram_vars, vars_len);2293}2294MFREE(osh, flash, MAX_NVRAM_SPACE);2295#else2296/* cache must be full size of nvram if read/write */2297nvram_vars = flash;2298#endif /* BCMNVRAMR */22992300exit:2301return ret;2302}2303#endif /* WLC_LOW */23042305#ifdef BCMDBG_PKT /* pkt logging for debugging */2306/* Add a packet to the pktlist */2307static void2308_pktlist_add(pktlist_info_t *pktlist, void *pkt, int line, char *file)2309{2310uint16 i;2311char *basename;2312#ifdef BCMDBG_PTRACE2313uint16 *idx = PKTLIST_IDX(pkt);2314#endif /* BCMDBG_PTRACE */23152316ASSERT(pktlist->count < PKTLIST_SIZE);23172318/* Verify the packet is not already part of the list */2319for (i = 0; i < pktlist->count; i++) {2320if (pktlist->list[i].pkt == pkt)2321ASSERT(0);2322}2323pktlist->list[pktlist->count].pkt = pkt;2324pktlist->list[pktlist->count].line = line;23252326basename = strrchr(file, '/');2327if (basename)2328basename++;2329else2330basename = file;2331pktlist->list[pktlist->count].file = basename;2332#ifdef BCMDBG_PTRACE2333*idx = pktlist->count;2334bzero(pktlist->list[pktlist->count].pkt_trace, PKTTRACE_MAX_BYTES);2335#endif /* BCMDBG_PTRACE */2336pktlist->count++;23372338return;2339}23402341void2342pktlist_add(pktlist_info_t *pktlist, void *pkt, int line, char *file)2343{2344void *p;2345for (p = pkt; p != NULL; p = PKTCLINK(p))2346_pktlist_add(pktlist, p, line, file);2347}23482349/* Remove a packet from the pktlist */2350static void2351_pktlist_remove(pktlist_info_t *pktlist, void *pkt)2352{2353uint16 i;2354uint16 num = pktlist->count;2355#ifdef BCMDBG_PTRACE2356uint16 *idx = PKTLIST_IDX(pkt);23572358ASSERT((*idx) < pktlist->count);2359#endif /* BCMDBG_PTRACE */23602361/* find the index where pkt exists */2362for (i = 0; i < num; i++) {2363/* check for the existence of pkt in the list */2364if (pktlist->list[i].pkt == pkt) {2365#ifdef BCMDBG_PTRACE2366ASSERT((*idx) == i);2367#endif /* BCMDBG_PTRACE */2368/* replace with the last element */2369pktlist->list[i].pkt = pktlist->list[num-1].pkt;2370pktlist->list[i].line = pktlist->list[num-1].line;2371pktlist->list[i].file = pktlist->list[num-1].file;2372#ifdef BCMDBG_PTRACE2373memcpy(pktlist->list[i].pkt_trace, pktlist->list[num-1].pkt_trace,2374PKTTRACE_MAX_BYTES);2375idx = PKTLIST_IDX(pktlist->list[i].pkt);2376*idx = i;2377#endif /* BCMDBG_PTRACE */2378pktlist->count--;2379return;2380}2381}2382ASSERT(0);2383}23842385void2386pktlist_remove(pktlist_info_t *pktlist, void *pkt)2387{2388void *p;2389for (p = pkt; p != NULL; p = PKTCLINK(p))2390_pktlist_remove(pktlist, p);2391}23922393#ifdef BCMDBG_PTRACE2394static void2395_pktlist_trace(pktlist_info_t *pktlist, void *pkt, uint16 bit)2396{2397uint16 *idx = PKTLIST_IDX(pkt);23982399ASSERT(((*idx) < pktlist->count) && (bit < PKTTRACE_MAX_BITS));2400ASSERT(pktlist->list[(*idx)].pkt == pkt);24012402pktlist->list[(*idx)].pkt_trace[bit/NBBY] |= (1 << ((bit)%NBBY));24032404}2405void2406pktlist_trace(pktlist_info_t *pktlist, void *pkt, uint16 bit)2407{2408void *p;2409for (p = pkt; p != NULL; p = PKTCLINK(p))2410_pktlist_trace(pktlist, p, bit);2411}2412#endif /* BCMDBG_PTRACE */24132414/* Dump the pktlist (and the contents of each packet if 'data'2415* is set). 'buf' should be large enough2416*/24172418char *2419pktlist_dump(pktlist_info_t *pktlist, char *buf)2420{2421char *obuf = buf;2422uint16 i;24232424if (buf != NULL)2425buf += sprintf(buf, "Packet list dump:\n");2426else2427printf("Packet list dump:\n");24282429for (i = 0; i < (pktlist->count); i++) {2430if (buf != NULL)2431buf += sprintf(buf, "Pkt_addr: 0x%p Line: %d File: %s\t",2432pktlist->list[i].pkt, pktlist->list[i].line,2433pktlist->list[i].file);2434else2435printf("Pkt_addr: 0x%p Line: %d File: %s\t", pktlist->list[i].pkt,2436pktlist->list[i].line, pktlist->list[i].file);24372438/* #ifdef NOTDEF Remove this ifdef to print pkttag and pktdata */2439if (buf != NULL) {2440if (PKTTAG(pktlist->list[i].pkt)) {2441/* Print pkttag */2442buf += sprintf(buf, "Pkttag(in hex): ");2443buf += bcm_format_hex(buf, PKTTAG(pktlist->list[i].pkt),2444OSL_PKTTAG_SZ);2445}2446buf += sprintf(buf, "Pktdata(in hex): ");2447buf += bcm_format_hex(buf, PKTDATA(NULL, pktlist->list[i].pkt),2448PKTLEN(NULL, pktlist->list[i].pkt));2449} else {2450void *pkt = pktlist->list[i].pkt, *npkt;24512452printf("Pkt[%d] Dump:\n", i);2453while (pkt) {2454int hroom, pktlen;2455uchar *src;2456#ifdef BCMDBG_PTRACE2457uint16 *idx = PKTLIST_IDX(pkt);24582459ASSERT((*idx) < pktlist->count);2460prhex("Pkt Trace (in hex):", pktlist->list[(*idx)].pkt_trace,2461PKTTRACE_MAX_BYTES);2462#endif /* BCMDBG_PTRACE */2463npkt = (void *)PKTNEXT(NULL, pkt);2464PKTSETNEXT(NULL, pkt, NULL);24652466src = (uchar *)(PKTTAG(pkt));2467pktlen = PKTLEN(NULL, pkt);2468hroom = PKTHEADROOM(NULL, pkt);24692470printf("Pkttag_addr: %p\n", src);2471if (src)2472prhex("Pkttag(in hex): ", src, OSL_PKTTAG_SZ);2473src = (uchar *) (PKTDATA(NULL, pkt));2474printf("Pkthead_addr: %p len: %d\n", src - hroom, hroom);2475prhex("Pkt headroom content(in hex): ", src - hroom, hroom);2476printf("Pktdata_addr: %p len: %d\n", src, pktlen);2477prhex("Pktdata(in hex): ", src, pktlen);24782479pkt = npkt;2480}2481}2482/* #endif NOTDEF */24832484if (buf != NULL)2485buf += sprintf(buf, "\n");2486else2487printf("\n");2488}2489return obuf;2490}2491#endif /* BCMDBG_PKT */24922493/* iovar table lookup */2494/* XXX could mandate sorted tables and do a binary search */2495const bcm_iovar_t*2496bcm_iovar_lookup(const bcm_iovar_t *table, const char *name)2497{2498const bcm_iovar_t *vi;2499const char *lookup_name;25002501/* skip any ':' delimited option prefixes */2502lookup_name = strrchr(name, ':');2503if (lookup_name != NULL)2504lookup_name++;2505else2506lookup_name = name;25072508ASSERT(table != NULL);25092510for (vi = table; vi->name; vi++) {2511if (!strcmp(vi->name, lookup_name))2512return vi;2513}2514/* ran to end of table */25152516return NULL; /* var name not found */2517}25182519int2520bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set)2521{2522int bcmerror = 0;25232524/* length check on io buf */2525switch (vi->type) {2526case IOVT_BOOL:2527case IOVT_INT8:2528case IOVT_INT16:2529case IOVT_INT32:2530case IOVT_UINT8:2531case IOVT_UINT16:2532case IOVT_UINT32:2533/* all integers are int32 sized args at the ioctl interface */2534if (len < (int)sizeof(int)) {2535bcmerror = BCME_BUFTOOSHORT;2536}2537break;25382539case IOVT_BUFFER:2540/* buffer must meet minimum length requirement */2541if (len < vi->minlen) {2542bcmerror = BCME_BUFTOOSHORT;2543}2544break;25452546case IOVT_VOID:2547if (!set) {2548/* Cannot return nil... */2549bcmerror = BCME_UNSUPPORTED;2550} else if (len) {2551/* Set is an action w/o parameters */2552bcmerror = BCME_BUFTOOLONG;2553}2554break;25552556default:2557/* unknown type for length check in iovar info */2558ASSERT(0);2559bcmerror = BCME_UNSUPPORTED;2560}25612562return bcmerror;2563}25642565#endif /* BCMDRIVER */256625672568/*******************************************************************************2569* crc82570*2571* Computes a crc8 over the input data using the polynomial:2572*2573* x^8 + x^7 +x^6 + x^4 + x^2 + 12574*2575* The caller provides the initial value (either CRC8_INIT_VALUE2576* or the previous returned value) to allow for processing of2577* discontiguous blocks of data. When generating the CRC the2578* caller is responsible for complementing the final return value2579* and inserting it into the byte stream. When checking, a final2580* return value of CRC8_GOOD_VALUE indicates a valid CRC.2581*2582* Reference: Dallas Semiconductor Application Note 272583* Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",2584* ver 3, Aug 1993, [email protected], Rocksoft Pty Ltd.,2585* ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt2586*2587* ****************************************************************************2588*/25892590static const uint8 crc8_table[256] = {25910x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,25920x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,25930x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,25940xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,25950x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,25960x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,25970xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,25980xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,25990xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,26000xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,26010x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,26020x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,26030x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,26040xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,26050x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,26060x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,26070xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,26080xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,26090x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,26100x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,26110xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,26120x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,26130x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,26140x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,26150x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,26160x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,26170xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,26180x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,26190x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,26200x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,26210xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,26220xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F2623};26242625#define CRC_INNER_LOOP(n, c, x) \2626(c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff]26272628uint82629BCMROMFN(hndcrc8)(2630uint8 *pdata, /* pointer to array of data to process */2631uint nbytes, /* number of input data bytes to process */2632uint8 crc /* either CRC8_INIT_VALUE or previous return value */2633)2634{2635/* hard code the crc loop instead of using CRC_INNER_LOOP macro2636* to avoid the undefined and unnecessary (uint8 >> 8) operation.2637*/2638while (nbytes-- > 0)2639crc = crc8_table[(crc ^ *pdata++) & 0xff];26402641return crc;2642}26432644/*******************************************************************************2645* crc162646*2647* Computes a crc16 over the input data using the polynomial:2648*2649* x^16 + x^12 +x^5 + 12650*2651* The caller provides the initial value (either CRC16_INIT_VALUE2652* or the previous returned value) to allow for processing of2653* discontiguous blocks of data. When generating the CRC the2654* caller is responsible for complementing the final return value2655* and inserting it into the byte stream. When checking, a final2656* return value of CRC16_GOOD_VALUE indicates a valid CRC.2657*2658* Reference: Dallas Semiconductor Application Note 272659* Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",2660* ver 3, Aug 1993, [email protected], Rocksoft Pty Ltd.,2661* ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt2662*2663* ****************************************************************************2664*/26652666static const uint16 crc16_table[256] = {26670x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,26680x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,26690x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,26700x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,26710x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,26720xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,26730x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,26740xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,26750x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,26760xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,26770x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,26780xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,26790x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,26800xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,26810x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,26820xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,26830x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,26840x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,26850x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,26860x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,26870xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,26880x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,26890xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,26900x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,26910xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,26920x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,26930xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,26940x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,26950xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,26960x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,26970xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,26980x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F782699};27002701uint162702BCMROMFN(hndcrc16)(2703uint8 *pdata, /* pointer to array of data to process */2704uint nbytes, /* number of input data bytes to process */2705uint16 crc /* either CRC16_INIT_VALUE or previous return value */2706)2707{2708while (nbytes-- > 0)2709CRC_INNER_LOOP(16, crc, *pdata++);2710return crc;2711}27122713static const uint32 crc32_table[256] = {27140x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,27150x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,27160x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,27170x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,27180x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,27190x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,27200x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,27210x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,27220x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,27230x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,27240x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,27250x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,27260x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,27270x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,27280x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,27290x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,27300x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,27310x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,27320x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,27330x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,27340x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,27350x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,27360x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,27370x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,27380x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,27390x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,27400x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,27410x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,27420x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,27430x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,27440x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,27450x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,27460xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,27470xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,27480xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,27490xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,27500xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,27510xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,27520xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,27530xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,27540xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,27550xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,27560xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,27570xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,27580xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,27590xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,27600xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,27610xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,27620x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,27630x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,27640x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,27650x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,27660x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,27670x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,27680x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,27690x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,27700xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,27710xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,27720xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,27730xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,27740xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,27750xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,27760xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,27770xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D2778};27792780/*2781* crc input is CRC32_INIT_VALUE for a fresh start, or previous return value if2782* accumulating over multiple pieces.2783*/2784uint322785BCMROMFN(hndcrc32)(uint8 *pdata, uint nbytes, uint32 crc)2786{2787uint8 *pend;2788#ifdef __mips__2789uint8 tmp[4];2790ulong *tptr = (ulong *)tmp;27912792if (nbytes > 3) {2793/* in case the beginning of the buffer isn't aligned */2794pend = (uint8 *)((uint)(pdata + 3) & ~0x3);2795nbytes -= (pend - pdata);2796while (pdata < pend)2797CRC_INNER_LOOP(32, crc, *pdata++);2798}27992800if (nbytes > 3) {2801/* handle bulk of data as 32-bit words */2802pend = pdata + (nbytes & ~0x3);2803while (pdata < pend) {2804*tptr = *(ulong *)pdata;2805pdata += sizeof(ulong *);2806CRC_INNER_LOOP(32, crc, tmp[0]);2807CRC_INNER_LOOP(32, crc, tmp[1]);2808CRC_INNER_LOOP(32, crc, tmp[2]);2809CRC_INNER_LOOP(32, crc, tmp[3]);2810}2811}28122813/* 1-3 bytes at end of buffer */2814pend = pdata + (nbytes & 0x03);2815while (pdata < pend)2816CRC_INNER_LOOP(32, crc, *pdata++);2817#else2818pend = pdata + nbytes;2819while (pdata < pend)2820CRC_INNER_LOOP(32, crc, *pdata++);2821#endif /* __mips__ */28222823return crc;2824}28252826#ifdef notdef2827#define CLEN 1499 /* CRC Length */2828#define CBUFSIZ (CLEN+4)2829#define CNBUFS 5 /* # of bufs */28302831void2832testcrc32(void)2833{2834uint j, k, l;2835uint8 *buf;2836uint len[CNBUFS];2837uint32 crcr;2838uint32 crc32tv[CNBUFS] =2839{0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110};28402841ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL);28422843/* step through all possible alignments */2844for (l = 0; l <= 4; l++) {2845for (j = 0; j < CNBUFS; j++) {2846len[j] = CLEN;2847for (k = 0; k < len[j]; k++)2848*(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff;2849}28502851for (j = 0; j < CNBUFS; j++) {2852crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE);2853ASSERT(crcr == crc32tv[j]);2854}2855}28562857MFREE(buf, CBUFSIZ*CNBUFS);2858return;2859}2860#endif /* notdef */28612862/*2863* Advance from the current 1-byte tag/1-byte length/variable-length value2864* triple, to the next, returning a pointer to the next.2865* If the current or next TLV is invalid (does not fit in given buffer length),2866* NULL is returned.2867* *buflen is not modified if the TLV elt parameter is invalid, or is decremented2868* by the TLV parameter's length if it is valid.2869*/2870bcm_tlv_t *2871BCMROMFN(bcm_next_tlv)(bcm_tlv_t *elt, int *buflen)2872{2873int len;28742875/* validate current elt */2876if (!bcm_valid_tlv(elt, *buflen))2877return NULL;28782879/* advance to next elt */2880len = elt->len;2881elt = (bcm_tlv_t*)(elt->data + len);2882*buflen -= (TLV_HDR_LEN + len);28832884/* validate next elt */2885if (!bcm_valid_tlv(elt, *buflen))2886return NULL;28872888return elt;2889}28902891/*2892* Traverse a string of 1-byte tag/1-byte length/variable-length value2893* triples, returning a pointer to the substring whose first element2894* matches tag2895*/2896bcm_tlv_t *2897BCMROMFN(bcm_parse_tlvs)(void *buf, int buflen, uint key)2898{2899bcm_tlv_t *elt;2900int totlen;29012902elt = (bcm_tlv_t*)buf;2903totlen = buflen;29042905/* find tagged parameter */2906while (totlen >= TLV_HDR_LEN) {2907int len = elt->len;29082909/* validate remaining totlen */2910if ((elt->id == key) &&2911(totlen >= (len + TLV_HDR_LEN)))2912return (elt);29132914elt = (bcm_tlv_t*)((uint8*)elt + (len + TLV_HDR_LEN));2915totlen -= (len + TLV_HDR_LEN);2916}29172918return NULL;2919}29202921/*2922* Traverse a string of 1-byte tag/1-byte length/variable-length value2923* triples, returning a pointer to the substring whose first element2924* matches tag. Stop parsing when we see an element whose ID is greater2925* than the target key.2926*/2927bcm_tlv_t *2928BCMROMFN(bcm_parse_ordered_tlvs)(void *buf, int buflen, uint key)2929{2930bcm_tlv_t *elt;2931int totlen;29322933elt = (bcm_tlv_t*)buf;2934totlen = buflen;29352936/* find tagged parameter */2937while (totlen >= TLV_HDR_LEN) {2938uint id = elt->id;2939int len = elt->len;29402941/* Punt if we start seeing IDs > than target key */2942if (id > key)2943return (NULL);29442945/* validate remaining totlen */2946if ((id == key) &&2947(totlen >= (len + TLV_HDR_LEN)))2948return (elt);29492950elt = (bcm_tlv_t*)((uint8*)elt + (len + TLV_HDR_LEN));2951totlen -= (len + TLV_HDR_LEN);2952}2953return NULL;2954}29552956#if defined(BCMDBG) || defined(BCMDBG_ERR) || defined(WLMSG_PRHDRS) || \2957defined(WLMSG_PRPKT) || defined(WLMSG_ASSOC) || defined(BCMDBG_DUMP) || \2958defined(DHD_DEBUG)2959int2960bcm_format_field(const bcm_bit_desc_ex_t *bd, uint32 flags, char* buf, int len)2961{2962int i, slen = 0;2963uint32 bit, mask;2964const char *name;2965mask = bd->mask;2966if (len < 2 || !buf)2967return 0;29682969buf[0] = '\0';29702971for (i = 0; (name = bd->bitfield[i].name) != NULL; i++) {2972bit = bd->bitfield[i].bit;2973if ((flags & mask) == bit) {2974if (len > (int)strlen(name)) {2975slen = strlen(name);2976strncpy(buf, name, slen+1);2977}2978break;2979}2980}2981return slen;2982}29832984int2985bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len)2986{2987int i;2988char* p = buf;2989char hexstr[16];2990int slen = 0, nlen = 0;2991uint32 bit;2992const char* name;29932994if (len < 2 || !buf)2995return 0;29962997buf[0] = '\0';29982999for (i = 0; flags != 0; i++) {3000bit = bd[i].bit;3001name = bd[i].name;3002if (bit == 0 && flags != 0) {3003/* print any unnamed bits */3004snprintf(hexstr, 16, "0x%X", flags);3005name = hexstr;3006flags = 0; /* exit loop */3007} else if ((flags & bit) == 0)3008continue;3009flags &= ~bit;3010nlen = strlen(name);3011slen += nlen;3012/* count btwn flag space */3013if (flags != 0)3014slen += 1;3015/* need NULL char as well */3016if (len <= slen)3017break;3018/* copy NULL char but don't count it */3019strncpy(p, name, nlen + 1);3020p += nlen;3021/* copy btwn flag space and NULL char */3022if (flags != 0)3023p += snprintf(p, 2, " ");3024}30253026/* indicate the str was too short */3027if (flags != 0) {3028if (len < 2)3029p -= 2 - len; /* overwrite last char */3030p += snprintf(p, 2, ">");3031}30323033return (int)(p - buf);3034}30353036/* print bytes formatted as hex to a string. return the resulting string length */3037int3038bcm_format_hex(char *str, const void *bytes, int len)3039{3040int i;3041char *p = str;3042const uint8 *src = (const uint8*)bytes;30433044for (i = 0; i < len; i++) {3045p += snprintf(p, 3, "%02X", *src);3046src++;3047}3048return (int)(p - str);3049}3050#endif /* BCMDBG || WLMSG_PRHDRS || WLMSG_PRPKT || WLMSG_ASSOC || BCMDBG_DUMP || DHD_DEBUG */30513052/* pretty hex print a contiguous buffer */3053void3054prhex(const char *msg, uchar *buf, uint nbytes)3055{3056char line[128], *p;3057int len = sizeof(line);3058int nchar;3059uint i;30603061if (msg && (msg[0] != '\0'))3062printf("%s:\n", msg);30633064p = line;3065for (i = 0; i < nbytes; i++) {3066if (i % 16 == 0) {3067nchar = snprintf(p, len, " %04d: ", i); /* line prefix */3068p += nchar;3069len -= nchar;3070}3071if (len > 0) {3072nchar = snprintf(p, len, "%02x ", buf[i]);3073p += nchar;3074len -= nchar;3075}30763077if (i % 16 == 15) {3078printf("%s\n", line); /* flush line */3079p = line;3080len = sizeof(line);3081}3082}30833084/* flush last partial line */3085if (p != line)3086printf("%s\n", line);3087}30883089static const char *crypto_algo_names[] = {3090"NONE",3091"WEP1",3092"TKIP",3093"WEP128",3094"AES_CCM",3095"AES_OCB_MSDU",3096"AES_OCB_MPDU",3097#ifdef BCMCCX3098"CKIP",3099"CKIP_MMH",3100"WEP_MMH",3101"NALG"3102#else3103"NALG"3104"UNDEF",3105"UNDEF",3106"UNDEF",3107#endif /* BCMCCX */3108#ifdef BCMWAPI_WPI3109"WAPI",3110#endif /* BCMWAPI_WPI */3111"UNDEF"3112};31133114const char *3115bcm_crypto_algo_name(uint algo)3116{3117return (algo < ARRAYSIZE(crypto_algo_names)) ? crypto_algo_names[algo] : "ERR";3118}31193120#ifdef BCMDBG3121void3122deadbeef(void *p, uint len)3123{3124static uint8 meat[] = { 0xde, 0xad, 0xbe, 0xef };31253126while (len-- > 0) {3127*(uint8*)p = meat[((uintptr)p) & 3];3128p = (uint8*)p + 1;3129}3130}3131#endif /* BCMDBG */31323133char *3134bcm_chipname(uint chipid, char *buf, uint len)3135{3136const char *fmt;31373138fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";3139snprintf(buf, len, fmt, chipid);3140return buf;3141}31423143/* Produce a human-readable string for boardrev */3144char *3145bcm_brev_str(uint32 brev, char *buf)3146{3147if (brev < 0x100)3148snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);3149else3150snprintf(buf, 8, "%c%03x", ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff);31513152return (buf);3153}31543155#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */31563157/* dump large strings to console */3158void3159printbig(char *buf)3160{3161uint len, max_len;3162char c;31633164len = strlen(buf);31653166max_len = BUFSIZE_TODUMP_ATONCE;31673168while (len > max_len) {3169c = buf[max_len];3170buf[max_len] = '\0';3171printf("%s", buf);3172buf[max_len] = c;31733174buf += max_len;3175len -= max_len;3176}3177/* print the remaining string */3178printf("%s\n", buf);3179return;3180}31813182/* routine to dump fields in a fileddesc structure */3183uint3184bcmdumpfields(bcmutl_rdreg_rtn read_rtn, void *arg0, uint arg1, struct fielddesc *fielddesc_array,3185char *buf, uint32 bufsize)3186{3187uint filled_len;3188int len;3189struct fielddesc *cur_ptr;31903191filled_len = 0;3192cur_ptr = fielddesc_array;31933194while (bufsize > 1) {3195if (cur_ptr->nameandfmt == NULL)3196break;3197len = snprintf(buf, bufsize, cur_ptr->nameandfmt,3198read_rtn(arg0, arg1, cur_ptr->offset));3199/* check for snprintf overflow or error */3200if (len < 0 || (uint32)len >= bufsize)3201len = bufsize - 1;3202buf += len;3203bufsize -= len;3204filled_len += len;3205cur_ptr++;3206}3207return filled_len;3208}32093210uint3211bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)3212{3213uint len;32143215len = strlen(name) + 1;32163217if ((len + datalen) > buflen)3218return 0;32193220strncpy(buf, name, buflen);32213222/* append data onto the end of the name string */3223memcpy(&buf[len], data, datalen);3224len += datalen;32253226return len;3227}32283229/* Quarter dBm units to mW3230* Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=1533231* Table is offset so the last entry is largest mW value that fits in3232* a uint16.3233*/32343235#define QDBM_OFFSET 153 /* Offset for first entry */3236#define QDBM_TABLE_LEN 40 /* Table size */32373238/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.3239* Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 23240*/3241#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */32423243/* Largest mW value that will round down to the last table entry,3244* QDBM_OFFSET + QDBM_TABLE_LEN-1.3245* Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.3246*/3247#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */32483249static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {3250/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */3251/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,3252/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,3253/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,3254/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,3255/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 630963256};32573258uint163259BCMROMFN(bcm_qdbm_to_mw)(uint8 qdbm)3260{3261uint factor = 1;3262int idx = qdbm - QDBM_OFFSET;32633264if (idx >= QDBM_TABLE_LEN) {3265/* clamp to max uint16 mW value */3266return 0xFFFF;3267}32683269/* scale the qdBm index up to the range of the table 0-403270* where an offset of 40 qdBm equals a factor of 10 mW.3271*/3272while (idx < 0) {3273idx += 40;3274factor *= 10;3275}32763277/* return the mW value scaled down to the correct factor of 10,3278* adding in factor/2 to get proper rounding.3279*/3280return ((nqdBm_to_mW_map[idx] + factor/2) / factor);3281}32823283uint83284BCMROMFN(bcm_mw_to_qdbm)(uint16 mw)3285{3286uint8 qdbm;3287int offset;3288uint mw_uint = mw;3289uint boundary;32903291/* handle boundary case */3292if (mw_uint <= 1)3293return 0;32943295offset = QDBM_OFFSET;32963297/* move mw into the range of the table */3298while (mw_uint < QDBM_TABLE_LOW_BOUND) {3299mw_uint *= 10;3300offset -= 40;3301}33023303for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) {3304boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm+1] -3305nqdBm_to_mW_map[qdbm])/2;3306if (mw_uint < boundary) break;3307}33083309qdbm += (uint8)offset;33103311return (qdbm);3312}331333143315uint3316BCMROMFN(bcm_bitcount)(uint8 *bitmap, uint length)3317{3318uint bitcount = 0, i;3319uint8 tmp;3320for (i = 0; i < length; i++) {3321tmp = bitmap[i];3322while (tmp) {3323bitcount++;3324tmp &= (tmp - 1);3325}3326}3327return bitcount;3328}33293330#ifdef BCMDRIVER33313332/* Initialization of bcmstrbuf structure */3333void3334bcm_binit(struct bcmstrbuf *b, char *buf, uint size)3335{3336b->origsize = b->size = size;3337b->origbuf = b->buf = buf;3338}33393340/* Buffer sprintf wrapper to guard against buffer overflow */3341int3342bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...)3343{3344va_list ap;3345int r;33463347va_start(ap, fmt);33483349r = vsnprintf(b->buf, b->size, fmt, ap);33503351/* Non Ansi C99 compliant returns -1,3352* Ansi compliant return r >= b->size,3353* bcmstdlib returns 0, handle all3354*/3355/* r == 0 is also the case when strlen(fmt) is zero.3356* typically the case when "" is passed as argument.3357*/3358if ((r == -1) || (r >= (int)b->size)) {3359b->size = 0;3360} else {3361b->size -= r;3362b->buf += r;3363}33643365va_end(ap);33663367return r;3368}33693370void3371bcm_bprhex(struct bcmstrbuf *b, const char *msg, bool newline, uint8 *buf, int len)3372{3373int i;33743375if (msg != NULL && msg[0] != '\0')3376bcm_bprintf(b, "%s", msg);3377for (i = 0; i < len; i ++)3378bcm_bprintf(b, "%02X", buf[i]);3379if (newline)3380bcm_bprintf(b, "\n");3381}33823383void3384bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount)3385{3386int i;33873388for (i = 0; i < num_bytes; i++) {3389num[i] += amount;3390if (num[i] >= amount)3391break;3392amount = 1;3393}3394}33953396int3397bcm_cmp_bytes(const uchar *arg1, const uchar *arg2, uint8 nbytes)3398{3399int i;34003401for (i = nbytes - 1; i >= 0; i--) {3402if (arg1[i] != arg2[i])3403return (arg1[i] - arg2[i]);3404}3405return 0;3406}34073408void3409bcm_print_bytes(const char *name, const uchar *data, int len)3410{3411int i;3412int per_line = 0;34133414printf("%s: %d \n", name ? name : "", len);3415for (i = 0; i < len; i++) {3416printf("%02x ", *data++);3417per_line++;3418if (per_line == 16) {3419per_line = 0;3420printf("\n");3421}3422}3423printf("\n");3424}3425#if defined(WLTINYDUMP) || defined(BCMDBG) || defined(WLMSG_INFORM) || \3426defined(WLMSG_ASSOC) || defined(WLMSG_PRPKT) || defined(WLMSG_WSEC)3427#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1)34283429int3430bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len)3431{3432uint i, c;3433char *p = buf;3434char *endp = buf + SSID_FMT_BUF_LEN;34353436if (ssid_len > DOT11_MAX_SSID_LEN) ssid_len = DOT11_MAX_SSID_LEN;34373438for (i = 0; i < ssid_len; i++) {3439c = (uint)ssid[i];3440if (c == '\\') {3441*p++ = '\\';3442*p++ = '\\';3443} else if (bcm_isprint((uchar)c)) {3444*p++ = (char)c;3445} else {3446p += snprintf(p, (endp - p), "\\x%02X", c);3447}3448}3449*p = '\0';3450ASSERT(p < endp);34513452return (int)(p - buf);3453}3454#endif /* WLTINYDUMP || BCMDBG || WLMSG_INFORM || WLMSG_ASSOC || WLMSG_PRPKT */34553456#endif /* BCMDRIVER */34573458/*3459* ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file and ending in a NUL.3460* also accepts nvram files which are already in the format of <var1>=<value>\0\<var2>=<value2>\03461* Removes carriage returns, empty lines, comment lines, and converts newlines to NULs.3462* Shortens buffer as needed and pads with NULs. End of buffer is marked by two NULs.3463*/34643465unsigned int3466process_nvram_vars(char *varbuf, unsigned int len)3467{3468char *dp;3469bool findNewline;3470int column;3471unsigned int buf_len, n;3472unsigned int pad = 0;34733474dp = varbuf;34753476findNewline = FALSE;3477column = 0;34783479for (n = 0; n < len; n++) {3480if (varbuf[n] == '\r')3481continue;3482if (findNewline && varbuf[n] != '\n')3483continue;3484findNewline = FALSE;3485if (varbuf[n] == '#') {3486findNewline = TRUE;3487continue;3488}3489if (varbuf[n] == '\n') {3490if (column == 0)3491continue;3492*dp++ = 0;3493column = 0;3494continue;3495}3496*dp++ = varbuf[n];3497column++;3498}3499buf_len = (unsigned int)(dp - varbuf);3500if (buf_len % 4) {3501pad = 4 - buf_len % 4;3502if (pad && (buf_len + pad <= len)) {3503buf_len += pad;3504}3505}35063507while (dp < varbuf + n)3508*dp++ = 0;35093510return buf_len;3511}35123513/* calculate a * b + c */3514void3515bcm_uint64_multiple_add(uint32* r_high, uint32* r_low, uint32 a, uint32 b, uint32 c)3516{3517#define FORMALIZE(var) {cc += (var & 0x80000000) ? 1 : 0; var &= 0x7fffffff;}3518uint32 r1, r0;3519uint32 a1, a0, b1, b0, t, cc = 0;35203521a1 = a >> 16;3522a0 = a & 0xffff;3523b1 = b >> 16;3524b0 = b & 0xffff;35253526r0 = a0 * b0;3527FORMALIZE(r0);35283529t = (a1 * b0) << 16;3530FORMALIZE(t);35313532r0 += t;3533FORMALIZE(r0);35343535t = (a0 * b1) << 16;3536FORMALIZE(t);35373538r0 += t;3539FORMALIZE(r0);35403541FORMALIZE(c);35423543r0 += c;3544FORMALIZE(r0);35453546r0 |= (cc % 2) ? 0x80000000 : 0;3547r1 = a1 * b1 + ((a1 * b0) >> 16) + ((b1 * a0) >> 16) + (cc / 2);35483549*r_high = r1;3550*r_low = r0;3551}35523553/* calculate a / b */3554void3555bcm_uint64_divide(uint32* r, uint32 a_high, uint32 a_low, uint32 b)3556{3557uint32 a1 = a_high, a0 = a_low, r0 = 0;35583559if (b < 2)3560return;35613562while (a1 != 0) {3563r0 += (0xffffffff / b) * a1;3564bcm_uint64_multiple_add(&a1, &a0, ((0xffffffff % b) + 1) % b, a1, a0);3565}35663567r0 += a0 / b;3568*r = r0;3569}35703571/* calculate a >> b; and returns only lower 32 bits */3572void3573bcm_uint64_right_shift(uint32* r, uint32 a_high, uint32 a_low, uint32 b)3574{3575uint32 a1 = a_high, a0 = a_low, r0 = 0;35763577if (b == 0) {3578r0 = a_low;3579*r = r0;3580return;3581}35823583if (b < 32) {3584a0 = a0 >> b;3585a1 = a1 & ((1 << b) - 1);3586a1 = a1 << (32 - b);3587r0 = a0 | a1;3588*r = r0;3589return;3590} else {3591r0 = a1 >> (b - 32);3592*r = r0;3593return;3594}35953596}359735983599