2019-06-01 10:08:37 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-23 22:09:12 -08:00
/*
* aQuantia Corporation Network Driver
* Copyright ( C ) 2014 - 2017 aQuantia Corporation . All rights reserved
*/
/* File aq_main.c: Main file for aQuantia Linux driver. */
# include "aq_main.h"
# include "aq_nic.h"
# include "aq_pci_func.h"
# include "aq_ethtool.h"
2018-11-12 15:46:00 +00:00
# include "aq_filters.h"
2017-01-23 22:09:12 -08:00
# include <linux/netdevice.h>
# include <linux/module.h>
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_VERSION ( AQ_CFG_DRV_VERSION ) ;
MODULE_AUTHOR ( AQ_CFG_DRV_AUTHOR ) ;
MODULE_DESCRIPTION ( AQ_CFG_DRV_DESC ) ;
2019-05-04 17:57:55 +08:00
static const char aq_ndev_driver_name [ ] = AQ_CFG_DRV_NAME ;
2019-04-29 10:04:45 +00:00
2018-01-15 16:41:18 +03:00
static const struct net_device_ops aq_ndev_ops ;
2019-04-29 10:04:45 +00:00
static struct workqueue_struct * aq_ndev_wq ;
void aq_ndev_schedule_work ( struct work_struct * work )
{
queue_work ( aq_ndev_wq , work ) ;
}
2018-01-15 16:41:18 +03:00
struct net_device * aq_ndev_alloc ( void )
2017-01-23 22:09:12 -08:00
{
2018-01-15 16:41:18 +03:00
struct net_device * ndev = NULL ;
struct aq_nic_s * aq_nic = NULL ;
2017-01-23 22:09:12 -08:00
2018-01-15 16:41:18 +03:00
ndev = alloc_etherdev_mq ( sizeof ( struct aq_nic_s ) , AQ_CFG_VECS_MAX ) ;
if ( ! ndev )
return NULL ;
2017-01-23 22:09:12 -08:00
2018-01-15 16:41:18 +03:00
aq_nic = netdev_priv ( ndev ) ;
aq_nic - > ndev = ndev ;
ndev - > netdev_ops = & aq_ndev_ops ;
ndev - > ethtool_ops = & aq_ethtool_ops ;
return ndev ;
2017-01-23 22:09:12 -08:00
}
static int aq_ndev_open ( struct net_device * ndev )
{
int err = 0 ;
2018-01-19 17:03:21 +03:00
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
2017-01-23 22:09:12 -08:00
err = aq_nic_init ( aq_nic ) ;
if ( err < 0 )
goto err_exit ;
2018-11-12 15:46:00 +00:00
err = aq_reapply_rxnfc_all_rules ( aq_nic ) ;
if ( err < 0 )
goto err_exit ;
2017-01-23 22:09:12 -08:00
err = aq_nic_start ( aq_nic ) ;
if ( err < 0 )
goto err_exit ;
err_exit :
if ( err < 0 )
aq_nic_deinit ( aq_nic ) ;
return err ;
}
static int aq_ndev_close ( struct net_device * ndev )
{
int err = 0 ;
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
err = aq_nic_stop ( aq_nic ) ;
if ( err < 0 )
goto err_exit ;
aq_nic_deinit ( aq_nic ) ;
err_exit :
return err ;
}
static int aq_ndev_start_xmit ( struct sk_buff * skb , struct net_device * ndev )
{
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
2017-02-20 22:36:43 +03:00
return aq_nic_xmit ( aq_nic , skb ) ;
2017-01-23 22:09:12 -08:00
}
static int aq_ndev_change_mtu ( struct net_device * ndev , int new_mtu )
{
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
2017-02-20 22:36:41 +03:00
int err = aq_nic_set_mtu ( aq_nic , new_mtu + ETH_HLEN ) ;
2017-01-23 22:09:12 -08:00
if ( err < 0 )
goto err_exit ;
2017-03-13 19:07:16 -04:00
ndev - > mtu = new_mtu ;
2017-01-23 22:09:12 -08:00
err_exit :
return err ;
}
static int aq_ndev_set_features ( struct net_device * ndev ,
netdev_features_t features )
{
2019-06-26 12:35:49 +00:00
bool is_vlan_rx_strip = ! ! ( features & NETIF_F_HW_VLAN_CTAG_RX ) ;
bool is_vlan_tx_insert = ! ! ( features & NETIF_F_HW_VLAN_CTAG_TX ) ;
2017-01-23 22:09:12 -08:00
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
2019-06-26 12:35:49 +00:00
bool need_ndev_restart = false ;
struct aq_nic_cfg_s * aq_cfg ;
2017-01-23 22:09:12 -08:00
bool is_lro = false ;
2018-11-09 11:54:03 +00:00
int err = 0 ;
2019-06-26 12:35:49 +00:00
aq_cfg = aq_nic_get_cfg ( aq_nic ) ;
2018-11-12 15:46:00 +00:00
if ( ! ( features & NETIF_F_NTUPLE ) ) {
if ( aq_nic - > ndev - > features & NETIF_F_NTUPLE ) {
err = aq_clear_rxnfc_all_rules ( aq_nic ) ;
if ( unlikely ( err ) )
goto err_exit ;
}
}
2018-11-12 15:46:09 +00:00
if ( ! ( features & NETIF_F_HW_VLAN_CTAG_FILTER ) ) {
if ( aq_nic - > ndev - > features & NETIF_F_HW_VLAN_CTAG_FILTER ) {
err = aq_filters_vlan_offload_off ( aq_nic ) ;
if ( unlikely ( err ) )
goto err_exit ;
}
}
2018-11-12 15:46:00 +00:00
2018-11-09 11:54:03 +00:00
aq_cfg - > features = features ;
2017-01-23 22:09:12 -08:00
2018-11-09 11:54:03 +00:00
if ( aq_cfg - > aq_hw_caps - > hw_features & NETIF_F_LRO ) {
2017-01-23 22:09:12 -08:00
is_lro = features & NETIF_F_LRO ;
if ( aq_cfg - > is_lro ! = is_lro ) {
aq_cfg - > is_lro = is_lro ;
2019-06-26 12:35:49 +00:00
need_ndev_restart = true ;
2017-01-23 22:09:12 -08:00
}
}
2019-06-26 12:35:49 +00:00
if ( ( aq_nic - > ndev - > features ^ features ) & NETIF_F_RXCSUM ) {
2018-11-09 11:54:03 +00:00
err = aq_nic - > aq_hw_ops - > hw_set_offload ( aq_nic - > aq_hw ,
aq_cfg ) ;
2017-01-23 22:09:12 -08:00
2019-06-26 12:35:49 +00:00
if ( unlikely ( err ) )
goto err_exit ;
}
if ( aq_cfg - > is_vlan_rx_strip ! = is_vlan_rx_strip ) {
aq_cfg - > is_vlan_rx_strip = is_vlan_rx_strip ;
need_ndev_restart = true ;
}
if ( aq_cfg - > is_vlan_tx_insert ! = is_vlan_tx_insert ) {
aq_cfg - > is_vlan_tx_insert = is_vlan_tx_insert ;
need_ndev_restart = true ;
}
if ( need_ndev_restart & & netif_running ( ndev ) ) {
aq_ndev_close ( ndev ) ;
aq_ndev_open ( ndev ) ;
}
2018-11-12 15:46:00 +00:00
err_exit :
2018-11-09 11:54:03 +00:00
return err ;
2017-01-23 22:09:12 -08:00
}
static int aq_ndev_set_mac_address ( struct net_device * ndev , void * addr )
{
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
int err = 0 ;
err = eth_mac_addr ( ndev , addr ) ;
if ( err < 0 )
goto err_exit ;
err = aq_nic_set_mac ( aq_nic , ndev ) ;
if ( err < 0 )
goto err_exit ;
err_exit :
return err ;
}
static void aq_ndev_set_multicast_settings ( struct net_device * ndev )
{
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
2018-07-05 17:01:09 +03:00
aq_nic_set_packet_filter ( aq_nic , ndev - > flags ) ;
2017-01-23 22:09:12 -08:00
2018-07-05 17:01:09 +03:00
aq_nic_set_multicast_list ( aq_nic , ndev ) ;
2017-01-23 22:09:12 -08:00
}
2018-11-12 15:46:09 +00:00
static int aq_ndo_vlan_rx_add_vid ( struct net_device * ndev , __be16 proto ,
u16 vid )
{
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
if ( ! aq_nic - > aq_hw_ops - > hw_filter_vlan_set )
return - EOPNOTSUPP ;
set_bit ( vid , aq_nic - > active_vlans ) ;
return aq_filters_vlans_update ( aq_nic ) ;
}
static int aq_ndo_vlan_rx_kill_vid ( struct net_device * ndev , __be16 proto ,
u16 vid )
{
struct aq_nic_s * aq_nic = netdev_priv ( ndev ) ;
if ( ! aq_nic - > aq_hw_ops - > hw_filter_vlan_set )
return - EOPNOTSUPP ;
clear_bit ( vid , aq_nic - > active_vlans ) ;
if ( - ENOENT = = aq_del_fvlan_by_vlan ( aq_nic , vid ) )
return aq_filters_vlans_update ( aq_nic ) ;
return 0 ;
}
2017-01-23 22:09:12 -08:00
static const struct net_device_ops aq_ndev_ops = {
. ndo_open = aq_ndev_open ,
. ndo_stop = aq_ndev_close ,
. ndo_start_xmit = aq_ndev_start_xmit ,
. ndo_set_rx_mode = aq_ndev_set_multicast_settings ,
. ndo_change_mtu = aq_ndev_change_mtu ,
. ndo_set_mac_address = aq_ndev_set_mac_address ,
2018-11-12 15:46:09 +00:00
. ndo_set_features = aq_ndev_set_features ,
. ndo_vlan_rx_add_vid = aq_ndo_vlan_rx_add_vid ,
. ndo_vlan_rx_kill_vid = aq_ndo_vlan_rx_kill_vid ,
2017-01-23 22:09:12 -08:00
} ;
2019-04-29 10:04:45 +00:00
static int __init aq_ndev_init_module ( void )
{
int ret ;
aq_ndev_wq = create_singlethread_workqueue ( aq_ndev_driver_name ) ;
if ( ! aq_ndev_wq ) {
pr_err ( " Failed to create workqueue \n " ) ;
return - ENOMEM ;
}
ret = aq_pci_func_register_driver ( ) ;
if ( ret ) {
destroy_workqueue ( aq_ndev_wq ) ;
return ret ;
}
return 0 ;
}
static void __exit aq_ndev_exit_module ( void )
{
aq_pci_func_unregister_driver ( ) ;
if ( aq_ndev_wq ) {
destroy_workqueue ( aq_ndev_wq ) ;
aq_ndev_wq = NULL ;
}
}
module_init ( aq_ndev_init_module ) ;
module_exit ( aq_ndev_exit_module ) ;