/*******************************************************************************
*
* Copyright (c) 2015-2016 Intel Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenFabrics.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*******************************************************************************/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/init.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
#include <net/netevent.h>
#include <net/neighbour.h>
#include "i40iw.h"
/**
* i40iw_arp_table - manage arp table
* @iwdev: iwarp device
* @ip_addr: ip address for device
* @mac_addr: mac address ptr
* @action: modify, delete or add
*/
int i40iw_arp_table(struct i40iw_device *iwdev,
u32 *ip_addr,
bool ipv4,
u8 *mac_addr,
u32 action)
{
int arp_index;
int err;
u32 ip[4];
if (ipv4) {
memset(ip, 0, sizeof(ip));
ip[0] = *ip_addr;
} else {
memcpy(ip, ip_addr, sizeof(ip));
}
for (arp_index = 0; (u32)arp_index < iwdev->arp_table_size; arp_index++)
if (memcmp(iwdev->arp_table[arp_index].ip_addr, ip, sizeof(ip)) == 0)
break;
switch (action) {
case I40IW_ARP_ADD:
if (arp_index != iwdev->arp_table_size)
return -1;
arp_index = 0;
err = i40iw_alloc_resource(iwdev, iwdev->allocated_arps,
iwdev->arp_table_size,
(u32 *)&arp_index,
&iwdev->next_arp_index);
if (err)
return err;
memcpy(iwdev->arp_table[arp_index].ip_addr, ip, sizeof(ip));
ether_addr_copy(iwdev->arp_table[arp_index].mac_addr, mac_addr);
break;
case I40IW_ARP_RESOLVE:
if (arp_index == iwdev->arp_table_size)
return -1;
break;
case I40IW_ARP_DELETE:
if (arp_index == iwdev->arp_table_size)
return -1;
memset(iwdev->arp_table[arp_index].ip_addr, 0,
sizeof(iwdev->arp_table[arp_index].ip_addr));
eth_zero_addr(iwdev->arp_table[arp_index].mac_addr);
i40iw_free_resource(iwdev, iwdev->allocated_arps, arp_index);
break;
default:
return -1;
}
return arp_index;
}
/**
* i40iw_wr32 - write 32 bits to hw register
* @hw: hardware information including registers
* @reg: register offset
* @value: vvalue to write to register
*/
inline void i40iw_wr32(struct i40iw_hw *hw, u32 reg, u32 value)
{
writel(value, hw->hw_addr + reg);
}
/**
* i40iw_rd32 - read a 32 bit hw register
* @hw: hardware information including registers
* @reg: register offset
*
* Return value of register content
*/
inline u32 i40iw_rd32(struct i40iw_hw *hw, u32 reg)
{
return readl(hw->hw_addr + reg);
}
/**
* i40iw_inetaddr_event - system notifier for ipv4 addr events
* @notfier: not used
* @event: event for notifier
* @ptr: if address
*/
int i40iw_inetaddr_event(struct notifier_block *notifier,
unsigned long event,
void *ptr)
{
struct in_ifaddr *ifa = ptr;
struct net_device *event_netdev = ifa->ifa_dev->dev;
struct net_device *netdev;
struct net_device *upper_dev;
struct i40iw_device *iwdev;
struct i40iw_handler *hdl;
u32 local_ipaddr;
u32 action = I40IW_ARP_ADD;
hdl = i40iw_find_netdev(event_netdev);
if (!hdl)
return NOTIFY_DONE;
iwdev = &hdl->device;
if (iwdev->init_state < IP_ADDR_REGISTERED || iwdev->closing)
return NOTIFY_DONE;
netdev = iwdev->ldev->netdev;
upper_dev = netdev_master_upper_dev_get(netdev);
if (netdev != event_netdev)
return NOTIFY_DONE;
if (upper_dev) {
struct in_device *in;
rcu_read_lock();
in = __in_dev_get_rcu(upper_dev);
local_ipaddr = 0;
if (in) {
struct in_ifaddr *ifa;
ifa = rcu_dereference(in->ifa_list);
if (ifa)
local_ipaddr = ntohl(ifa->ifa_address);
}
rcu_read_unlock();
} else {
local_ipaddr = ntohl(ifa->ifa_address);
}
switch (event) {
case NETDEV_DOWN:
action = I40IW_ARP_DELETE;
/* Fall through */
case NETDEV_UP:
/* Fall through */
case NETDEV_CHANGEADDR:
/* Just skip if no need to handle ARP cache */
if (!local_ipaddr)
break;
i40iw_manage_arp_cache(iwdev,
netdev->dev_addr,
&local_ipaddr,
true,
action);
i40iw_if_notify(iwdev, netdev, &local_ipaddr, true,
(action == I40IW_ARP_ADD) ? true : false);
break;
default:
break;
}
return NOTIFY_DONE;
}
/**
* i40iw_inet6addr_event - system notifier for ipv6 addr events
* @notfier: not used
* @event: event for notifier
* @ptr: if address
*/
int i40iw_inet6addr_event(struct notifier_block *notifier,
unsigned long event,
void *ptr)
{
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
struct net_device *event_netdev = ifa->idev->dev;
struct net_
|