#include <sys/cdefs.h>
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
void
ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name)
{
memset(aq, 0, sizeof(*aq));
aq->aq_maxlen = maxlen;
IEEE80211_AGEQ_INIT(aq, name);
}
void
ieee80211_ageq_cleanup(struct ieee80211_ageq *aq)
{
KASSERT(aq->aq_len == 0, ("%d frames on ageq", aq->aq_len));
IEEE80211_AGEQ_DESTROY(aq);
}
static void
ageq_mfree(struct mbuf *m)
{
if (m->m_flags & M_ENCAP) {
struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif;
ieee80211_free_node(ni);
}
m->m_nextpkt = NULL;
m_freem(m);
}
void
ieee80211_ageq_mfree(struct mbuf *m)
{
struct mbuf *next;
for (; m != NULL; m = next) {
next = m->m_nextpkt;
ageq_mfree(m);
}
}
int
ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age)
{
IEEE80211_AGEQ_LOCK(aq);
if (__predict_true(aq->aq_len < aq->aq_maxlen)) {
if (aq->aq_tail == NULL) {
aq->aq_head = m;
} else {
aq->aq_tail->m_nextpkt = m;
age -= M_AGE_GET(aq->aq_head);
}
KASSERT(age >= 0, ("age %d", age));
M_AGE_SET(m, age);
m->m_nextpkt = NULL;
aq->aq_tail = m;
aq->aq_len++;
IEEE80211_AGEQ_UNLOCK(aq);
return 0;
} else {
aq->aq_drops++;
IEEE80211_AGEQ_UNLOCK(aq);
ageq_mfree(m);
return ENOSPC;
}
}
void
ieee80211_ageq_drain(struct ieee80211_ageq *aq)
{
ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL));
}
void
ieee80211_ageq_drain_node(struct ieee80211_ageq *aq,
struct ieee80211_node *ni)
{
ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni));
}
struct mbuf *
ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta)
{
struct mbuf *head, **phead;
struct mbuf *m;
phead = &head;
if (aq->aq_len != 0) {
IEEE80211_AGEQ_LOCK(aq);
while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) {
if ((aq->aq_head = m->m_nextpkt) == NULL)
aq->aq_tail = NULL;
KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
aq->aq_len--;
*phead = m;
phead = &m->m_nextpkt;
}
if (m != NULL)
M_AGE_SUB(m, quanta);
IEEE80211_AGEQ_UNLOCK(aq);
}
*phead = NULL;
return head;
}
struct mbuf *
ieee80211_ageq_remove(struct ieee80211_ageq *aq,
struct ieee80211_node *match)
{
struct mbuf *m, **prev, *ohead;
struct mbuf *head, **phead;
IEEE80211_AGEQ_LOCK(aq);
ohead = aq->aq_head;
prev = &aq->aq_head;
phead = &head;
while ((m = *prev) != NULL) {
if (match != NULL && m->m_pkthdr.rcvif != (void *) match) {
prev = &m->m_nextpkt;
continue;
}
KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
aq->aq_len--;
if (aq->aq_tail == m) {
KASSERT(m->m_nextpkt == NULL, ("not last"));
if (aq->aq_head == m) {
KASSERT(aq->aq_len == 0,
("not empty, len %d", aq->aq_len));
aq->aq_tail = NULL;
} else {
aq->aq_tail = (struct mbuf *)((uintptr_t)prev -
offsetof(struct mbuf, m_nextpkt));
}
}
*prev = m->m_nextpkt;
*phead = m;
phead = &m->m_nextpkt;
}
if (head == ohead && aq->aq_head != NULL)
M_AGE_SET(aq->aq_head, M_AGE_GET(head));
IEEE80211_AGEQ_UNLOCK(aq);
*phead = NULL;
return head;
}