blob: 37b330c62dc80d9839dc7a22ff3def0cefdfce3a [file] [log] [blame]
/**
Copyright (c) 2008 - 2015 Quantenna Communications Inc
All Rights Reserved
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include "qdrv_mac.h"
#include "qdrv_wlan.h"
#include "qdrv_mac_reserve.h"
#include "qtn/topaz_fwt_sw.h"
#include <qtn/topaz_tqe.h>
#define QDRV_MAC_RESERVE_MAX 6
struct qdrv_mac_reserve_ent_s {
uint8_t addr[ETH_ALEN];
uint8_t mask[ETH_ALEN];
};
struct qdrv_mac_reserve_s {
struct qdrv_wlan *qw;
uint32_t max;
struct qdrv_mac_reserve_ent_s entry[QDRV_MAC_RESERVE_MAX];
};
static struct qdrv_mac_reserve_s qdrv_mac_reserve;
/* specialised version of compare_ether_addr() */
static inline unsigned qdrv_mac_reserve_compare_ether_addr_masked(const void *addr1,
const void *mask, const void *addr2)
{
const uint16_t *a = addr1;
const uint16_t *m = mask;
const uint16_t *b = addr2;
return ((a[0] ^ (b[0] & m[0])) | (a[1] ^ (b[1] & m[1])) | (a[2] ^ (b[2] & m[2]))) != 0;
}
int __sram_text qdrv_mac_reserved(const uint8_t *addr)
{
struct qdrv_mac_reserve_ent_s *res;
int i;
for (i = 0; i < qdrv_mac_reserve.max; i++) {
res = &qdrv_mac_reserve.entry[i];
if (!qdrv_mac_reserve_compare_ether_addr_masked(res->addr, res->mask, addr)) {
RXSTAT(qdrv_mac_reserve.qw, rx_mac_reserved);
return 1;
}
}
return 0;
}
EXPORT_SYMBOL(qdrv_mac_reserved);
void qdrv_mac_reserve_clear(void)
{
local_bh_disable();
tqe_register_mac_reserved_cbk(NULL);
qdrv_mac_reserve.max = 0;
memset(&qdrv_mac_reserve.entry, 0, sizeof(qdrv_mac_reserve.entry));
local_bh_enable();
printk("%s: mac reservation table cleared\n", __func__);
}
/*
* Reserve a MAC address for use by non-WiFi interfaces or clear all reserved MAC addresses.
*/
int qdrv_mac_reserve_set(const uint8_t *addr, const uint8_t *mask)
{
int i;
if (qdrv_mac_reserve.max > (ARRAY_SIZE(qdrv_mac_reserve.entry) - 1)) {
printk("%s: mac address reservation for %pM failed - table is full\n", __func__,
addr);
return -1;
}
if (IEEE80211_ADDR_NULL(addr) || IEEE80211_IS_MULTICAST(addr)) {
printk("%s: invalid mac address %pM\n", __func__, addr);
return -1;
}
if (IEEE80211_ADDR_NULL(mask)) {
printk("%s: invalid mask address %pM\n", __func__, mask);
return -1;
}
local_bh_disable();
for (i = 0; i < ETH_ALEN; i++) {
qdrv_mac_reserve.entry[qdrv_mac_reserve.max].addr[i] = (addr[i] & mask[i]);
qdrv_mac_reserve.entry[qdrv_mac_reserve.max].mask[i] = mask[i];
}
qdrv_mac_reserve.max++;
if (qdrv_mac_reserve.max == 1)
tqe_register_mac_reserved_cbk(qdrv_mac_reserved);
/* clear the FWT table */
fwt_sw_reset();
local_bh_enable();
printk("%s: mac address %pM/%pM reserved\n", __func__, addr, mask);
return 0;
}
/*
* Get the list of reserved MAC addresses.
*/
void qdrv_mac_reserve_show(struct seq_file *s, void *data, u32 num)
{
struct qdrv_mac_reserve_ent_s *res;
int i;
if (strcmp(data, "full") == 0) {
seq_printf(s, "%u\n",
(qdrv_mac_reserve.max > ARRAY_SIZE(qdrv_mac_reserve.entry)));
return;
}
seq_printf(s, "MAC address Mask\n");
for (i = 0; i < qdrv_mac_reserve.max; i++) {
res = &qdrv_mac_reserve.entry[i];
seq_printf(s, "%pM %pM\n", res->addr, res->mask);
}
}
void qdrv_mac_reserve_init(struct qdrv_wlan *qw)
{
qdrv_mac_reserve_clear();
qdrv_mac_reserve.qw = qw;
}