b3657453f1
ARP offloading should only be used in STA or P2P client mode. It is currently configured once at init. When being configured for AP ARP offloading should be turned off and when AP mode is left it can be turned back on. Cc: stable@vger.kernel.org Reviewed-by: Arend Van Spriel <arend@broadcom.com> Signed-off-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
391 lines
9.8 KiB
C
391 lines
9.8 KiB
C
/*
|
|
* Copyright (c) 2010 Broadcom Corporation
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <linux/netdevice.h>
|
|
#include <brcmu_wifi.h>
|
|
#include <brcmu_utils.h>
|
|
#include "dhd.h"
|
|
#include "dhd_bus.h"
|
|
#include "dhd_proto.h"
|
|
#include "dhd_dbg.h"
|
|
#include "fwil.h"
|
|
#include "tracepoint.h"
|
|
|
|
#define PKTFILTER_BUF_SIZE 128
|
|
#define BRCMF_DEFAULT_BCN_TIMEOUT 3
|
|
#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40
|
|
#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40
|
|
#define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00"
|
|
|
|
#ifdef DEBUG
|
|
static const char brcmf_version[] =
|
|
"Dongle Host Driver, version " BRCMF_VERSION_STR "\nCompiled on "
|
|
__DATE__ " at " __TIME__;
|
|
#else
|
|
static const char brcmf_version[] =
|
|
"Dongle Host Driver, version " BRCMF_VERSION_STR;
|
|
#endif
|
|
|
|
|
|
bool brcmf_c_prec_enq(struct device *dev, struct pktq *q,
|
|
struct sk_buff *pkt, int prec)
|
|
{
|
|
struct sk_buff *p;
|
|
int eprec = -1; /* precedence to evict from */
|
|
bool discard_oldest;
|
|
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
|
|
struct brcmf_pub *drvr = bus_if->drvr;
|
|
|
|
/* Fast case, precedence queue is not full and we are also not
|
|
* exceeding total queue length
|
|
*/
|
|
if (!pktq_pfull(q, prec) && !pktq_full(q)) {
|
|
brcmu_pktq_penq(q, prec, pkt);
|
|
return true;
|
|
}
|
|
|
|
/* Determine precedence from which to evict packet, if any */
|
|
if (pktq_pfull(q, prec))
|
|
eprec = prec;
|
|
else if (pktq_full(q)) {
|
|
p = brcmu_pktq_peek_tail(q, &eprec);
|
|
if (eprec > prec)
|
|
return false;
|
|
}
|
|
|
|
/* Evict if needed */
|
|
if (eprec >= 0) {
|
|
/* Detect queueing to unconfigured precedence */
|
|
discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec);
|
|
if (eprec == prec && !discard_oldest)
|
|
return false; /* refuse newer (incoming) packet */
|
|
/* Evict packet according to discard policy */
|
|
p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) :
|
|
brcmu_pktq_pdeq_tail(q, eprec);
|
|
if (p == NULL)
|
|
brcmf_err("brcmu_pktq_penq() failed, oldest %d\n",
|
|
discard_oldest);
|
|
|
|
brcmu_pkt_buf_free_skb(p);
|
|
}
|
|
|
|
/* Enqueue */
|
|
p = brcmu_pktq_penq(q, prec, pkt);
|
|
if (p == NULL)
|
|
brcmf_err("brcmu_pktq_penq() failed\n");
|
|
|
|
return p != NULL;
|
|
}
|
|
|
|
/* Convert user's input in hex pattern to byte-size mask */
|
|
static int brcmf_c_pattern_atoh(char *src, char *dst)
|
|
{
|
|
int i;
|
|
if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) {
|
|
brcmf_err("Mask invalid format. Needs to start with 0x\n");
|
|
return -EINVAL;
|
|
}
|
|
src = src + 2; /* Skip past 0x */
|
|
if (strlen(src) % 2 != 0) {
|
|
brcmf_err("Mask invalid format. Length must be even.\n");
|
|
return -EINVAL;
|
|
}
|
|
for (i = 0; *src != '\0'; i++) {
|
|
unsigned long res;
|
|
char num[3];
|
|
strncpy(num, src, 2);
|
|
num[2] = '\0';
|
|
if (kstrtoul(num, 16, &res))
|
|
return -EINVAL;
|
|
dst[i] = (u8)res;
|
|
src += 2;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static void
|
|
brcmf_c_pktfilter_offload_enable(struct brcmf_if *ifp, char *arg, int enable,
|
|
int master_mode)
|
|
{
|
|
unsigned long res;
|
|
char *argv;
|
|
char *arg_save = NULL, *arg_org = NULL;
|
|
s32 err;
|
|
struct brcmf_pkt_filter_enable_le enable_parm;
|
|
|
|
arg_save = kstrdup(arg, GFP_ATOMIC);
|
|
if (!arg_save)
|
|
goto fail;
|
|
|
|
arg_org = arg_save;
|
|
|
|
argv = strsep(&arg_save, " ");
|
|
|
|
if (argv == NULL) {
|
|
brcmf_err("No args provided\n");
|
|
goto fail;
|
|
}
|
|
|
|
/* Parse packet filter id. */
|
|
enable_parm.id = 0;
|
|
if (!kstrtoul(argv, 0, &res))
|
|
enable_parm.id = cpu_to_le32((u32)res);
|
|
|
|
/* Enable/disable the specified filter. */
|
|
enable_parm.enable = cpu_to_le32(enable);
|
|
|
|
err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", &enable_parm,
|
|
sizeof(enable_parm));
|
|
if (err)
|
|
brcmf_err("Set pkt_filter_enable error (%d)\n", err);
|
|
|
|
/* Control the master mode */
|
|
err = brcmf_fil_iovar_int_set(ifp, "pkt_filter_mode", master_mode);
|
|
if (err)
|
|
brcmf_err("Set pkt_filter_mode error (%d)\n", err);
|
|
|
|
fail:
|
|
kfree(arg_org);
|
|
}
|
|
|
|
static void brcmf_c_pktfilter_offload_set(struct brcmf_if *ifp, char *arg)
|
|
{
|
|
struct brcmf_pkt_filter_le *pkt_filter;
|
|
unsigned long res;
|
|
int buf_len;
|
|
s32 err;
|
|
u32 mask_size;
|
|
u32 pattern_size;
|
|
char *argv[8], *buf = NULL;
|
|
int i = 0;
|
|
char *arg_save = NULL, *arg_org = NULL;
|
|
|
|
arg_save = kstrdup(arg, GFP_ATOMIC);
|
|
if (!arg_save)
|
|
goto fail;
|
|
|
|
arg_org = arg_save;
|
|
|
|
buf = kmalloc(PKTFILTER_BUF_SIZE, GFP_ATOMIC);
|
|
if (!buf)
|
|
goto fail;
|
|
|
|
argv[i] = strsep(&arg_save, " ");
|
|
while (argv[i]) {
|
|
i++;
|
|
if (i >= 8) {
|
|
brcmf_err("Too many parameters\n");
|
|
goto fail;
|
|
}
|
|
argv[i] = strsep(&arg_save, " ");
|
|
}
|
|
|
|
if (i != 6) {
|
|
brcmf_err("Not enough args provided %d\n", i);
|
|
goto fail;
|
|
}
|
|
|
|
pkt_filter = (struct brcmf_pkt_filter_le *)buf;
|
|
|
|
/* Parse packet filter id. */
|
|
pkt_filter->id = 0;
|
|
if (!kstrtoul(argv[0], 0, &res))
|
|
pkt_filter->id = cpu_to_le32((u32)res);
|
|
|
|
/* Parse filter polarity. */
|
|
pkt_filter->negate_match = 0;
|
|
if (!kstrtoul(argv[1], 0, &res))
|
|
pkt_filter->negate_match = cpu_to_le32((u32)res);
|
|
|
|
/* Parse filter type. */
|
|
pkt_filter->type = 0;
|
|
if (!kstrtoul(argv[2], 0, &res))
|
|
pkt_filter->type = cpu_to_le32((u32)res);
|
|
|
|
/* Parse pattern filter offset. */
|
|
pkt_filter->u.pattern.offset = 0;
|
|
if (!kstrtoul(argv[3], 0, &res))
|
|
pkt_filter->u.pattern.offset = cpu_to_le32((u32)res);
|
|
|
|
/* Parse pattern filter mask. */
|
|
mask_size = brcmf_c_pattern_atoh(argv[4],
|
|
(char *)pkt_filter->u.pattern.mask_and_pattern);
|
|
|
|
/* Parse pattern filter pattern. */
|
|
pattern_size = brcmf_c_pattern_atoh(argv[5],
|
|
(char *)&pkt_filter->u.pattern.mask_and_pattern[mask_size]);
|
|
|
|
if (mask_size != pattern_size) {
|
|
brcmf_err("Mask and pattern not the same size\n");
|
|
goto fail;
|
|
}
|
|
|
|
pkt_filter->u.pattern.size_bytes = cpu_to_le32(mask_size);
|
|
buf_len = offsetof(struct brcmf_pkt_filter_le,
|
|
u.pattern.mask_and_pattern);
|
|
buf_len += mask_size + pattern_size;
|
|
|
|
err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", pkt_filter,
|
|
buf_len);
|
|
if (err)
|
|
brcmf_err("Set pkt_filter_add error (%d)\n", err);
|
|
|
|
fail:
|
|
kfree(arg_org);
|
|
|
|
kfree(buf);
|
|
}
|
|
|
|
int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
|
|
{
|
|
s8 eventmask[BRCMF_EVENTING_MASK_LEN];
|
|
u8 buf[BRCMF_DCMD_SMLEN];
|
|
char *ptr;
|
|
s32 err;
|
|
struct brcmf_bus_dcmd *cmdlst;
|
|
struct list_head *cur, *q;
|
|
|
|
/* retreive mac address */
|
|
err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
|
|
sizeof(ifp->mac_addr));
|
|
if (err < 0) {
|
|
brcmf_err("Retreiving cur_etheraddr failed, %d\n",
|
|
err);
|
|
goto done;
|
|
}
|
|
memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
|
|
|
|
/* query for 'ver' to get version info from firmware */
|
|
memset(buf, 0, sizeof(buf));
|
|
strcpy(buf, "ver");
|
|
err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));
|
|
if (err < 0) {
|
|
brcmf_err("Retreiving version information failed, %d\n",
|
|
err);
|
|
goto done;
|
|
}
|
|
ptr = (char *)buf;
|
|
strsep(&ptr, "\n");
|
|
/* Print fw version info */
|
|
brcmf_err("Firmware version = %s\n", buf);
|
|
|
|
/*
|
|
* Setup timeout if Beacons are lost and roam is off to report
|
|
* link down
|
|
*/
|
|
err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout",
|
|
BRCMF_DEFAULT_BCN_TIMEOUT);
|
|
if (err) {
|
|
brcmf_err("bcn_timeout error (%d)\n", err);
|
|
goto done;
|
|
}
|
|
|
|
/* Enable/Disable build-in roaming to allowed ext supplicant to take
|
|
* of romaing
|
|
*/
|
|
err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
|
|
if (err) {
|
|
brcmf_err("roam_off error (%d)\n", err);
|
|
goto done;
|
|
}
|
|
|
|
/* Setup event_msgs, enable E_IF */
|
|
err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
|
|
BRCMF_EVENTING_MASK_LEN);
|
|
if (err) {
|
|
brcmf_err("Get event_msgs error (%d)\n", err);
|
|
goto done;
|
|
}
|
|
setbit(eventmask, BRCMF_E_IF);
|
|
err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask,
|
|
BRCMF_EVENTING_MASK_LEN);
|
|
if (err) {
|
|
brcmf_err("Set event_msgs error (%d)\n", err);
|
|
goto done;
|
|
}
|
|
|
|
/* Setup default scan channel time */
|
|
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
|
|
BRCMF_DEFAULT_SCAN_CHANNEL_TIME);
|
|
if (err) {
|
|
brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n",
|
|
err);
|
|
goto done;
|
|
}
|
|
|
|
/* Setup default scan unassoc time */
|
|
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
|
|
BRCMF_DEFAULT_SCAN_UNASSOC_TIME);
|
|
if (err) {
|
|
brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n",
|
|
err);
|
|
goto done;
|
|
}
|
|
|
|
/* Setup packet filter */
|
|
brcmf_c_pktfilter_offload_set(ifp, BRCMF_DEFAULT_PACKET_FILTER);
|
|
brcmf_c_pktfilter_offload_enable(ifp, BRCMF_DEFAULT_PACKET_FILTER,
|
|
0, true);
|
|
|
|
/* set bus specific command if there is any */
|
|
list_for_each_safe(cur, q, &ifp->drvr->bus_if->dcmd_list) {
|
|
cmdlst = list_entry(cur, struct brcmf_bus_dcmd, list);
|
|
if (cmdlst->name && cmdlst->param && cmdlst->param_len) {
|
|
brcmf_fil_iovar_data_set(ifp, cmdlst->name,
|
|
cmdlst->param,
|
|
cmdlst->param_len);
|
|
}
|
|
list_del(cur);
|
|
kfree(cmdlst);
|
|
}
|
|
done:
|
|
return err;
|
|
}
|
|
|
|
#ifdef CONFIG_BRCM_TRACING
|
|
void __brcmf_err(const char *func, const char *fmt, ...)
|
|
{
|
|
struct va_format vaf = {
|
|
.fmt = fmt,
|
|
};
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
vaf.va = &args;
|
|
pr_err("%s: %pV", func, &vaf);
|
|
trace_brcmf_err(func, &vaf);
|
|
va_end(args);
|
|
}
|
|
#endif
|
|
#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG)
|
|
void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)
|
|
{
|
|
struct va_format vaf = {
|
|
.fmt = fmt,
|
|
};
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
vaf.va = &args;
|
|
if (brcmf_msg_level & level)
|
|
pr_debug("%s %pV", func, &vaf);
|
|
trace_brcmf_dbg(level, func, &vaf);
|
|
va_end(args);
|
|
}
|
|
#endif
|