📄 wlc_scb.c
字号:
/** * Common interface to the 802.11 Station Control Block (scb) * structure. * This file aims to encapsulating the scb structure. * Copyright 2005-2006, Broadcom Corporation * All Rights Reserved. * * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. * * $Id$*/#include <wlc_cfg.h>#include <typedefs.h>#include <bcmdefs.h>#include <osl.h>#include <sbutils.h>#include <bcmendian.h>#include <bcmutils.h>#include <proto/802.11.h>#include <proto/wpa.h>#include <sbconfig.h>#include <pcicfg.h>#include <bcmsrom.h>#include <wlioctl.h>#include <epivers.h>#include <proto/eapol.h>#include <bcmwpa.h>#include <sbhndpio.h>#include <sbhnddma.h>#include <hnddma.h>#include <d11.h>#include <wlc_rate.h>#include <wlc_rate_sel.h>#include <wlc_key.h>#include <wlc_channel.h>#include <wlc_pub.h>#include <wlc_bsscfg.h>#include <wlc_pio.h>#include <wlc.h>#include <wlc_scb.h>#include <wlc_phy.h>#include <wl_export.h>#include <wlc_channel.h>#define SCB_MAX_CUBBY 10 /* max number of cubbies in container *//* structure for storing per-cubby client info */typedef struct cubby_info { scb_cubby_init_t fn_init; /* fn called during scb malloc */ scb_cubby_deinit_t fn_deinit; /* fn called during scb free */ scb_cubby_dump_t fn_dump; /* fn called during scb dump */ void *context; /* context to be passed to all cb fns */} cubby_info_t;/* structure for storing public and private global scb module state */typedef struct scb_module_priv { scb_module_t scbpub; /* public part of scb */ uint16 nscb; /* total number of allocated scbs */ struct scb *scbhash[MAXBANDS][NSCBHASH]; /* station control block hash */ uint scbtotsize; /* total scb size including container */ uint ncubby; /* current num of cubbies */ cubby_info_t cubby_info[SCB_MAX_CUBBY]; /* cubby client info */ wlc_pub_t *pub; /* public part of wlc */} scb_module_priv_t;#define SCBSTATE_PRIV(s) ((scb_module_priv_t *)(s))#define SCBSTATE_PUB(s) (&(s)->scbpub)/* station control block - one per remote MAC address */struct scb_info { struct scb scbpub; /* public portion of scb */ struct scb_info *hashnext; /* pointer to next scb under same hash entry */ struct scb_info *next; /* pointer to next allocated scb */ struct wlcband *band; /* pointer to our associated band */};/* Helper macro for txpath in scb *//* A feature in Tx path goes through following states: * Unregisterd -> Registered [Global state] * Registerd -> Configured -> Active -> Configured [Per-scb state] *//* Is the feature configured? */#define SCB_TXMOD_CONFIGURED(scb, fid) (scb->tx_path[(fid)].configured)/* Set the next feature of given feature */#define SCB_TXMOD_SET(scb, fid, _next_fid) { \ scb->tx_path[fid].next_tx_fn = wlc->txmod_fns[_next_fid].tx_fn; \ scb->tx_path[fid].next_handle = wlc->txmod_fns[_next_fid].ctx; \ }static struct scb *wlc_scbvictim(wlc_info_t *wlc);scb_module_t *BCMINITFN(wlc_scb_attach)(wlc_pub_t *pub){ scb_module_priv_t *scbstate; if ((scbstate = MALLOC(pub->osh, sizeof(scb_module_priv_t))) == NULL) return NULL; bzero((char *)scbstate, sizeof(scb_module_priv_t)); scbstate->pub = pub; scbstate->scbtotsize = sizeof(struct scb_info); return SCBSTATE_PUB(scbstate);}voidBCMUNINITFN(wlc_scb_detach)(scb_module_t *scbstate){ if (!scbstate) return; ASSERT(SCBSTATE_PRIV(scbstate)->nscb == 0); MFREE(SCBSTATE_PRIV(scbstate)->pub->osh, scbstate, sizeof(scb_module_priv_t));}/* Methods for iterating along a list of scb *//* Direct access to the next */struct scb *wlc_scb_getnext(struct scb *scb){ if (scb) return (struct scb *)(((struct scb_info *)scb)->next); return NULL;}/* Initialize an iterator keeping memory of the next scb as it moves along the list */voidwlc_scb_iterinit(struct scb_iter *scbiter, struct scb *scb){ ASSERT(scbiter); /* Prefetch next scb, so caller can free an scb before going on to the next */ if (scb) scbiter->next = (struct scb *)(((struct scb_info *)scb)->next); else scbiter->next = NULL;}/* move the iterator */struct scb *wlc_scb_iternext(struct scb_iter *scbiter){ struct scb *scb; ASSERT(scbiter); if ((scb = scbiter->next)) scbiter->next = (struct scb *)(((struct scb_info *)scb)->next); else scbiter->next = NULL; return scb;}/* * Accessors, nagative values are errors. */intwlc_scb_cubby_reserve(wlc_info_t *wlc, uint size, scb_cubby_init_t fn_init, scb_cubby_deinit_t fn_deinit, scb_cubby_dump_t fn_dump, void *context){ uint offset; scb_module_priv_t *scbstate = SCBSTATE_PRIV(wlc->scbstate); cubby_info_t *cubby_info; ASSERT(scbstate->nscb == 0); ASSERT((scbstate->scbtotsize % PTRSZ) == 0); ASSERT(scbstate->ncubby < SCB_MAX_CUBBY); if (scbstate->ncubby >= SCB_MAX_CUBBY) { ASSERT(0); return -1; } cubby_info = &scbstate->cubby_info[scbstate->ncubby++]; cubby_info->fn_init = fn_init; cubby_info->fn_deinit = fn_deinit; cubby_info->fn_dump = fn_dump; cubby_info->context = context; offset = scbstate->scbtotsize; /* roundup to pointer boundary */ scbstate->scbtotsize = ROUNDUP(scbstate->scbtotsize + size, PTRSZ); return offset;}struct wlcband *wlc_scbband(struct scb *scb){ return ((struct scb_info *)scb)->band;}struct scb *wlc_scballoc(wlc_info_t *wlc){ struct scb_info *scbinfo = NULL; struct scb *scb = NULL; struct scb *oldscb; struct rateset *rs; scb_module_priv_t *scbstate = SCBSTATE_PRIV(wlc->scbstate); cubby_info_t *cubby_info; uint i; if (scbstate->nscb < MAXSCB && (scbinfo = MALLOC(wlc->pub.osh, scbstate->scbtotsize))) scbstate->nscb++; if (!scbinfo) { /* free the oldest entry */ if (!(oldscb = wlc_scbvictim(wlc))) return NULL; if (!wlc_scbfree(wlc, oldscb)) return NULL; ASSERT(scbstate->nscb < MAXSCB); /* allocate memory for scb */ if (!(scbinfo = MALLOC(wlc->pub.osh, scbstate->scbtotsize))) return NULL; scbstate->nscb++; } bzero((char*)scbinfo, scbstate->scbtotsize); scb = (struct scb *)scbinfo; scb->used = wlc->now; scbinfo->band = wlc->band; scb->bsscfg = &wlc->cfg; for (i = 0; i < scbstate->ncubby; i++) { cubby_info = &scbstate->cubby_info[i]; if (cubby_info->fn_init) cubby_info->fn_init(cubby_info->context, scb); } /* use current, target, or per-band default rateset? */ if (wlc->pub.up && VALID_CHANNEL(wlc, wlc->target_bss.channel)) if (wlc->pub.associated) rs = &wlc->pub.current_bss.rateset; else rs = &wlc->target_bss.rateset; else rs = &wlc->band->defrateset; /* * Initialize the per-scb rateset: * - if we are AP, start with only the basic subset of the * network rates. It will be updated when receive the next * probe request or association request. * - if we are IBSS and gmode, special case: * start with B-only subset of network rates and probe for ofdm rates * - else start with the network rates. * It will be updated on join attempts. */ /* initialize the scb rateset */ if (!wlc->pub.BSS && wlc->band->gmode) { wlc_rateset_filter(rs, &scb->rateset, FALSE, TRUE, RATE_MASK); /* if resulting set is empty, then take all network rates instead */ if (scb->rateset.count == 0) wlc_rateset_filter(rs, &scb->rateset, FALSE, FALSE, RATE_MASK); } else wlc_rateset_filter(rs, &scb->rateset, FALSE, FALSE, RATE_MASK); wlc_scb_rate_init(wlc, scb); /* update scb link list */ scbinfo->next = (struct scb_info *)SCBSTATE_PUB(scbstate)->scb; SCBSTATE_PUB(scbstate)->scb = scb; return scb;}boolwlc_scbfree(wlc_info_t *wlc, struct scb *scbd){ int index; struct scb_info *remove = (struct scb_info *)scbd; struct scb_info *scbinfo; scb_module_priv_t *scbstate = SCBSTATE_PRIV(wlc->scbstate); cubby_info_t *cubby_info; uint i; uint8 prio; if (scbd->permanent) return FALSE; for (i = 0; i < scbstate->ncubby; i++) { cubby_info = &scbstate->cubby_info[i]; if (cubby_info->fn_deinit) cubby_info->fn_deinit(cubby_info->context, scbd); } /* free the per station key if one exists */ if (scbd->key) { WL_WSEC(("wl%d: %s: deleting pairwise key for %s\n", wlc->pub.unit, __FUNCTION__, bcm_ether_ntoa(&scbd->ea, eabuf))); ASSERT(!bcmp((char*)&scbd->key->ea, (char*)&scbd->ea, ETHER_ADDR_LEN)); wlc_scb_key_delete(wlc, scbd); } /* free any frame reassembly buffer */ for (prio = 0; prio < NUMPRIO; prio++) { if (scbd->fragbuf[prio]) { PKTFREE(wlc->pub.osh, scbd->fragbuf[prio], FALSE); scbd->fragbuf[prio] = NULL; scbd->fragresid[prio] = 0; } } scbd->state = 0; index = SCBHASHINDEX(scbd->ea.octet); /* delete it from the hash */ scbinfo = (struct scb_info *)(scbstate->scbhash[remove->band->bandunit][index]); /* special case for the first */ if (scbinfo == remove) { scbstate->scbhash[remove->band->bandunit][index] = (struct scb *)scbinfo->hashnext; } else { for (; scbinfo; scbinfo = scbinfo->hashnext) { if (scbinfo->hashnext == remove) { scbinfo->hashnext = remove->hashnext; break; } } ASSERT(scbinfo); } /* delete it from the link list */ scbinfo = (struct scb_info *)(SCBSTATE_PUB(scbstate)->scb); if (scbinfo == remove) { SCBSTATE_PUB(scbstate)->scb = wlc_scb_getnext(scbd); } else { while (scbinfo) { if (scbinfo->next == remove) { scbinfo->next = remove->next; break; } scbinfo = scbinfo->next; } ASSERT(scbinfo); } /* free scb memory */ MFREE(wlc->pub.osh, remove, scbstate->scbtotsize); /* update total allocated scb number */ scbstate->nscb--; return TRUE;}/* free all scbs, unless permanent. Force indicates reclaim permanent as well */voidwlc_scbclear(struct wlc_info *wlc, bool force){ struct scb_iter scbiter; struct scb *scb; if (wlc->scbstate == NULL) return; FOREACHSCB(&scbiter, wlc->scbstate->scb, scb) { if (force) scb->permanent = FALSE; wlc_scbfree(wlc, scb); } if (force) ASSERT(SCBSTATE_PRIV(wlc->scbstate)->nscb == 0);}static struct scb *wlc_scbvictim(wlc_info_t *wlc){ uint oldest; struct scb *scb; struct scb *oldscb; uint now, age; struct scb_iter scbiter; if (!wlc->scbstate->scb) return NULL; /* free the oldest scb */ now = wlc->now; oldest = 0; oldscb = NULL; FOREACHSCB(&scbiter, wlc->scbstate->scb, scb) { if (!scb->permanent && ((age = (now - scb->used)) >= oldest)) { oldest = age; oldscb = scb; } } /* handle extreme case: all are permanent */ if (oldscb == NULL) return NULL; WL_ASSOC(("scb free %s due to aging %d\n", bcm_ether_ntoa(&oldscb->ea, eabuf), oldest)); return oldscb;}voidwlc_scb_setkey(struct scb *scb, wsec_key_t *key){ scb->key = key;}/* "|" operation. */voidwlc_scb_setstatebit(struct scb *scb, uint8 state){ WL_ASSOC(("set state %x\n", state)); ASSERT(scb); if (state & AUTHENTICATED) { scb->state &= ~PENDING_AUTH; } if (state & ASSOCIATED) { ASSERT(scb->state & AUTHENTICATED); scb->state &= ~PENDING_ASSOC; } if (state & AUTHORIZED) { ASSERT((scb->state & ASSOCIATED)); } scb->state |= state; WL_ASSOC(("wlc_scb : state = %x\n", scb->state));}/* "& ~" operation */voidwlc_scb_clearstatebit(struct scb *scb, uint8 state){ ASSERT(scb); WL_ASSOC(("clear state %x\n", state)); scb->state &= ~state; WL_ASSOC(("wlc_scb : state = %x\n", scb->state));}/* "|" operation . idx = position of the bsscfg in the wlc array of multi ssids.*/voidwlc_scb_setstatebit_bsscfg(struct scb *scb, uint8 state, int idx){ ASSERT(scb); WL_ASSOC(("set state : %x bsscfg idx : %d\n", state, idx)); if (state & ASSOCIATED) { ASSERT(SCB_AUTHENTICATED_BSSCFG(scb, idx)); /* clear all bits (idx is set below) */ memset(&scb->auth_bsscfg, 0, SCB_BSSCFG_BITSIZE); scb->state &= ~PENDING_ASSOC; } if (state & AUTHORIZED) { ASSERT(SCB_ASSOCIATED_BSSCFG(scb, idx)); } setbit(&scb->auth_bsscfg, idx); scb->state |= state;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -