Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/usb/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 242 kB image not shown  

Quelle  r8152.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (c) 2014 Realtek Semiconductor Corp. All rights reserved.
 */


#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/usb.h>
#include <linux/crc32.h>
#include <linux/if_vlan.h>
#include <linux/uaccess.h>
#include <linux/list.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <net/ip6_checksum.h>
#include <uapi/linux/mdio.h>
#include <linux/mdio.h>
#include <linux/usb/cdc.h>
#include <linux/suspend.h>
#include <linux/atomic.h>
#include <linux/acpi.h>
#include <linux/firmware.h>
#include <crypto/sha2.h>
#include <linux/usb/r8152.h>
#include <net/gso.h>

/* Information for net-next */
#define NETNEXT_VERSION  "12"

/* Information for net */
#define NET_VERSION  "13"

#define DRIVER_VERSION  "v1." NETNEXT_VERSION "." NET_VERSION
#define DRIVER_AUTHOR "Realtek linux nic maintainers "
#define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters"
#define MODULENAME "r8152"

#define R8152_PHY_ID  32

#define PLA_IDR   0xc000
#define PLA_RCR   0xc010
#define PLA_RCR1  0xc012
#define PLA_RMS   0xc016
#define PLA_RXFIFO_CTRL0 0xc0a0
#define PLA_RXFIFO_FULL  0xc0a2
#define PLA_RXFIFO_CTRL1 0xc0a4
#define PLA_RX_FIFO_FULL 0xc0a6
#define PLA_RXFIFO_CTRL2 0xc0a8
#define PLA_RX_FIFO_EMPTY 0xc0aa
#define PLA_DMY_REG0  0xc0b0
#define PLA_FMC   0xc0b4
#define PLA_CFG_WOL  0xc0b6
#define PLA_TEREDO_CFG  0xc0bc
#define PLA_TEREDO_WAKE_BASE 0xc0c4
#define PLA_MAR   0xcd00
#define PLA_BACKUP  0xd000
#define PLA_BDC_CR  0xd1a0
#define PLA_TEREDO_TIMER 0xd2cc
#define PLA_REALWOW_TIMER 0xd2e8
#define PLA_UPHY_TIMER  0xd388
#define PLA_SUSPEND_FLAG 0xd38a
#define PLA_INDICATE_FALG 0xd38c
#define PLA_MACDBG_PRE  0xd38c /* RTL_VER_04 only */
#define PLA_MACDBG_POST  0xd38e /* RTL_VER_04 only */
#define PLA_EXTRA_STATUS 0xd398
#define PLA_GPHY_CTRL  0xd3ae
#define PLA_POL_GPIO_CTRL 0xdc6a
#define PLA_EFUSE_DATA  0xdd00
#define PLA_EFUSE_CMD  0xdd02
#define PLA_LEDSEL  0xdd90
#define PLA_LED_FEATURE  0xdd92
#define PLA_PHYAR  0xde00
#define PLA_BOOT_CTRL  0xe004
#define PLA_LWAKE_CTRL_REG 0xe007
#define PLA_GPHY_INTR_IMR 0xe022
#define PLA_EEE_CR  0xe040
#define PLA_EEE_TXTWSYS  0xe04c
#define PLA_EEE_TXTWSYS_2P5G 0xe058
#define PLA_EEEP_CR  0xe080
#define PLA_MAC_PWR_CTRL 0xe0c0
#define PLA_MAC_PWR_CTRL2 0xe0ca
#define PLA_MAC_PWR_CTRL3 0xe0cc
#define PLA_MAC_PWR_CTRL4 0xe0ce
#define PLA_WDT6_CTRL  0xe428
#define PLA_TCR0  0xe610
#define PLA_TCR1  0xe612
#define PLA_MTPS  0xe615
#define PLA_TXFIFO_CTRL  0xe618
#define PLA_TXFIFO_FULL  0xe61a
#define PLA_RSTTALLY  0xe800
#define PLA_CR   0xe813
#define PLA_CRWECR  0xe81c
#define PLA_CONFIG12  0xe81e /* CONFIG1, CONFIG2 */
#define PLA_CONFIG34  0xe820 /* CONFIG3, CONFIG4 */
#define PLA_CONFIG5  0xe822
#define PLA_PHY_PWR  0xe84c
#define PLA_OOB_CTRL  0xe84f
#define PLA_CPCR  0xe854
#define PLA_MISC_0  0xe858
#define PLA_MISC_1  0xe85a
#define PLA_OCP_GPHY_BASE 0xe86c
#define PLA_TALLYCNT  0xe890
#define PLA_SFF_STS_7  0xe8de
#define PLA_PHYSTATUS  0xe908
#define PLA_CONFIG6  0xe90a /* CONFIG6 */
#define PLA_USB_CFG  0xe952
#define PLA_BP_BA  0xfc26
#define PLA_BP_0  0xfc28
#define PLA_BP_1  0xfc2a
#define PLA_BP_2  0xfc2c
#define PLA_BP_3  0xfc2e
#define PLA_BP_4  0xfc30
#define PLA_BP_5  0xfc32
#define PLA_BP_6  0xfc34
#define PLA_BP_7  0xfc36
#define PLA_BP_EN  0xfc38

#define USB_USB2PHY  0xb41e
#define USB_SSPHYLINK1  0xb426
#define USB_SSPHYLINK2  0xb428
#define USB_L1_CTRL  0xb45e
#define USB_U2P3_CTRL  0xb460
#define USB_CSR_DUMMY1  0xb464
#define USB_CSR_DUMMY2  0xb466
#define USB_DEV_STAT  0xb808
#define USB_CONNECT_TIMER 0xcbf8
#define USB_MSC_TIMER  0xcbfc
#define USB_BURST_SIZE  0xcfc0
#define USB_FW_FIX_EN0  0xcfca
#define USB_FW_FIX_EN1  0xcfcc
#define USB_LPM_CONFIG  0xcfd8
#define USB_ECM_OPTION  0xcfee
#define USB_CSTMR  0xcfef /* RTL8153A */
#define USB_MISC_2  0xcfff
#define USB_ECM_OP  0xd26b
#define USB_GPHY_CTRL  0xd284
#define USB_SPEED_OPTION 0xd32a
#define USB_FW_CTRL  0xd334 /* RTL8153B */
#define USB_FC_TIMER  0xd340
#define USB_USB_CTRL  0xd406
#define USB_PHY_CTRL  0xd408
#define USB_TX_AGG  0xd40a
#define USB_RX_BUF_TH  0xd40c
#define USB_USB_TIMER  0xd428
#define USB_RX_EARLY_TIMEOUT 0xd42c
#define USB_RX_EARLY_SIZE 0xd42e
#define USB_PM_CTRL_STATUS 0xd432 /* RTL8153A */
#define USB_RX_EXTRA_AGGR_TMR 0xd432 /* RTL8153B */
#define USB_TX_DMA  0xd434
#define USB_UPT_RXDMA_OWN 0xd437
#define USB_UPHY3_MDCMDIO 0xd480
#define USB_TOLERANCE  0xd490
#define USB_LPM_CTRL  0xd41a
#define USB_BMU_RESET  0xd4b0
#define USB_BMU_CONFIG  0xd4b4
#define USB_U1U2_TIMER  0xd4da
#define USB_FW_TASK  0xd4e8 /* RTL8153B */
#define USB_RX_AGGR_NUM  0xd4ee
#define USB_UPS_CTRL  0xd800
#define USB_POWER_CUT  0xd80a
#define USB_MISC_0  0xd81a
#define USB_MISC_1  0xd81f
#define USB_AFE_CTRL2  0xd824
#define USB_UPHY_XTAL  0xd826
#define USB_UPS_CFG  0xd842
#define USB_UPS_FLAGS  0xd848
#define USB_WDT1_CTRL  0xe404
#define USB_WDT11_CTRL  0xe43c
#define USB_BP_BA  PLA_BP_BA
#define USB_BP_0  PLA_BP_0
#define USB_BP_1  PLA_BP_1
#define USB_BP_2  PLA_BP_2
#define USB_BP_3  PLA_BP_3
#define USB_BP_4  PLA_BP_4
#define USB_BP_5  PLA_BP_5
#define USB_BP_6  PLA_BP_6
#define USB_BP_7  PLA_BP_7
#define USB_BP_EN  PLA_BP_EN /* RTL8153A */
#define USB_BP_8  0xfc38  /* RTL8153B */
#define USB_BP_9  0xfc3a
#define USB_BP_10  0xfc3c
#define USB_BP_11  0xfc3e
#define USB_BP_12  0xfc40
#define USB_BP_13  0xfc42
#define USB_BP_14  0xfc44
#define USB_BP_15  0xfc46
#define USB_BP2_EN  0xfc48

/* OCP Registers */
#define OCP_ALDPS_CONFIG 0x2010
#define OCP_EEE_CONFIG1  0x2080
#define OCP_EEE_CONFIG2  0x2092
#define OCP_EEE_CONFIG3  0x2094
#define OCP_BASE_MII  0xa400
#define OCP_EEE_AR  0xa41a
#define OCP_EEE_DATA  0xa41c
#define OCP_PHY_STATUS  0xa420
#define OCP_INTR_EN  0xa424
#define OCP_NCTL_CFG  0xa42c
#define OCP_POWER_CFG  0xa430
#define OCP_EEE_CFG  0xa432
#define OCP_SRAM_ADDR  0xa436
#define OCP_SRAM_DATA  0xa438
#define OCP_DOWN_SPEED  0xa442
#define OCP_EEE_ABLE  0xa5c4
#define OCP_EEE_ADV  0xa5d0
#define OCP_EEE_LPABLE  0xa5d2
#define OCP_10GBT_CTRL  0xa5d4
#define OCP_10GBT_STAT  0xa5d6
#define OCP_EEE_ADV2  0xa6d4
#define OCP_PHY_STATE  0xa708  /* nway state for 8153 */
#define OCP_PHY_PATCH_STAT 0xb800
#define OCP_PHY_PATCH_CMD 0xb820
#define OCP_PHY_LOCK  0xb82e
#define OCP_ADC_IOFFSET  0xbcfc
#define OCP_ADC_CFG  0xbc06
#define OCP_SYSCLK_CFG  0xc416

/* SRAM Register */
#define SRAM_GREEN_CFG  0x8011
#define SRAM_LPF_CFG  0x8012
#define SRAM_GPHY_FW_VER 0x801e
#define SRAM_10M_AMP1  0x8080
#define SRAM_10M_AMP2  0x8082
#define SRAM_IMPEDANCE  0x8084
#define SRAM_PHY_LOCK  0xb82e

/* PLA_RCR */
#define RCR_AAP   0x00000001
#define RCR_APM   0x00000002
#define RCR_AM   0x00000004
#define RCR_AB   0x00000008
#define RCR_ACPT_ALL  (RCR_AAP | RCR_APM | RCR_AM | RCR_AB)
#define SLOT_EN   BIT(11)

/* PLA_RCR1 */
#define OUTER_VLAN  BIT(7)
#define INNER_VLAN  BIT(6)

/* PLA_RXFIFO_CTRL0 */
#define RXFIFO_THR1_NORMAL 0x00080002
#define RXFIFO_THR1_OOB  0x01800003

/* PLA_RXFIFO_FULL */
#define RXFIFO_FULL_MASK 0xfff

/* PLA_RXFIFO_CTRL1 */
#define RXFIFO_THR2_FULL 0x00000060
#define RXFIFO_THR2_HIGH 0x00000038
#define RXFIFO_THR2_OOB  0x0000004a
#define RXFIFO_THR2_NORMAL 0x00a0

/* PLA_RXFIFO_CTRL2 */
#define RXFIFO_THR3_FULL 0x00000078
#define RXFIFO_THR3_HIGH 0x00000048
#define RXFIFO_THR3_OOB  0x0000005a
#define RXFIFO_THR3_NORMAL 0x0110

/* PLA_TXFIFO_CTRL */
#define TXFIFO_THR_NORMAL 0x00400008
#define TXFIFO_THR_NORMAL2 0x01000008

/* PLA_DMY_REG0 */
#define ECM_ALDPS  0x0002

/* PLA_FMC */
#define FMC_FCR_MCU_EN  0x0001

/* PLA_EEEP_CR */
#define EEEP_CR_EEEP_TX  0x0002

/* PLA_WDT6_CTRL */
#define WDT6_SET_MODE  0x0010

/* PLA_TCR0 */
#define TCR0_TX_EMPTY  0x0800
#define TCR0_AUTO_FIFO  0x0080

/* PLA_TCR1 */
#define VERSION_MASK  0x7cf0
#define IFG_MASK  (BIT(3) | BIT(9) | BIT(8))
#define IFG_144NS  BIT(9)
#define IFG_96NS  (BIT(9) | BIT(8))

/* PLA_MTPS */
#define MTPS_JUMBO  (12 * 1024 / 64)
#define MTPS_DEFAULT  (6 * 1024 / 64)

/* PLA_RSTTALLY */
#define TALLY_RESET  0x0001

/* PLA_CR */
#define CR_RST   0x10
#define CR_RE   0x08
#define CR_TE   0x04

/* PLA_CRWECR */
#define CRWECR_NORAML  0x00
#define CRWECR_CONFIG  0xc0

/* PLA_OOB_CTRL */
#define NOW_IS_OOB  0x80
#define TXFIFO_EMPTY  0x20
#define RXFIFO_EMPTY  0x10
#define LINK_LIST_READY  0x02
#define DIS_MCU_CLROOB  0x01
#define FIFO_EMPTY  (TXFIFO_EMPTY | RXFIFO_EMPTY)

/* PLA_MISC_1 */
#define RXDY_GATED_EN  0x0008

/* PLA_SFF_STS_7 */
#define RE_INIT_LL  0x8000
#define MCU_BORW_EN  0x4000

/* PLA_CPCR */
#define FLOW_CTRL_EN  BIT(0)
#define CPCR_RX_VLAN  0x0040

/* PLA_CFG_WOL */
#define MAGIC_EN  0x0001

/* PLA_TEREDO_CFG */
#define TEREDO_SEL  0x8000
#define TEREDO_WAKE_MASK 0x7f00
#define TEREDO_RS_EVENT_MASK 0x00fe
#define OOB_TEREDO_EN  0x0001

/* PLA_BDC_CR */
#define ALDPS_PROXY_MODE 0x0001

/* PLA_EFUSE_CMD */
#define EFUSE_READ_CMD  BIT(15)
#define EFUSE_DATA_BIT16 BIT(7)

/* PLA_CONFIG34 */
#define LINK_ON_WAKE_EN  0x0010
#define LINK_OFF_WAKE_EN 0x0008

/* PLA_CONFIG6 */
#define LANWAKE_CLR_EN  BIT(0)

/* PLA_USB_CFG */
#define EN_XG_LIP  BIT(1)
#define EN_G_LIP  BIT(2)

/* PLA_CONFIG5 */
#define BWF_EN   0x0040
#define MWF_EN   0x0020
#define UWF_EN   0x0010
#define LAN_WAKE_EN  0x0002

/* PLA_LED_FEATURE */
#define LED_MODE_MASK  0x0700

/* PLA_PHY_PWR */
#define TX_10M_IDLE_EN  0x0080
#define PFM_PWM_SWITCH  0x0040
#define TEST_IO_OFF  BIT(4)

/* PLA_MAC_PWR_CTRL */
#define D3_CLK_GATED_EN  0x00004000
#define MCU_CLK_RATIO  0x07010f07
#define MCU_CLK_RATIO_MASK 0x0f0f0f0f
#define ALDPS_SPDWN_RATIO 0x0f87

/* PLA_MAC_PWR_CTRL2 */
#define EEE_SPDWN_RATIO  0x8007
#define MAC_CLK_SPDWN_EN BIT(15)
#define EEE_SPDWN_RATIO_MASK 0xff

/* PLA_MAC_PWR_CTRL3 */
#define PLA_MCU_SPDWN_EN BIT(14)
#define PKT_AVAIL_SPDWN_EN 0x0100
#define SUSPEND_SPDWN_EN 0x0004
#define U1U2_SPDWN_EN  0x0002
#define L1_SPDWN_EN  0x0001

/* PLA_MAC_PWR_CTRL4 */
#define PWRSAVE_SPDWN_EN 0x1000
#define RXDV_SPDWN_EN  0x0800
#define TX10MIDLE_EN  0x0100
#define IDLE_SPDWN_EN  BIT(6)
#define TP100_SPDWN_EN  0x0020
#define TP500_SPDWN_EN  0x0010
#define TP1000_SPDWN_EN  0x0008
#define EEE_SPDWN_EN  0x0001

/* PLA_GPHY_INTR_IMR */
#define GPHY_STS_MSK  0x0001
#define SPEED_DOWN_MSK  0x0002
#define SPDWN_RXDV_MSK  0x0004
#define SPDWN_LINKCHG_MSK 0x0008

/* PLA_PHYAR */
#define PHYAR_FLAG  0x80000000

/* PLA_EEE_CR */
#define EEE_RX_EN  0x0001
#define EEE_TX_EN  0x0002

/* PLA_BOOT_CTRL */
#define AUTOLOAD_DONE  0x0002

/* PLA_LWAKE_CTRL_REG */
#define LANWAKE_PIN  BIT(7)

/* PLA_SUSPEND_FLAG */
#define LINK_CHG_EVENT  BIT(0)

/* PLA_INDICATE_FALG */
#define UPCOMING_RUNTIME_D3 BIT(0)

/* PLA_MACDBG_PRE and PLA_MACDBG_POST */
#define DEBUG_OE  BIT(0)
#define DEBUG_LTSSM  0x0082

/* PLA_EXTRA_STATUS */
#define CUR_LINK_OK  BIT(15)
#define U3P3_CHECK_EN  BIT(7) /* RTL_VER_05 only */
#define LINK_CHANGE_FLAG BIT(8)
#define POLL_LINK_CHG  BIT(0)

/* PLA_GPHY_CTRL */
#define GPHY_FLASH  BIT(1)

/* PLA_POL_GPIO_CTRL */
#define DACK_DET_EN  BIT(15)
#define POL_GPHY_PATCH  BIT(4)

/* USB_USB2PHY */
#define USB2PHY_SUSPEND  0x0001
#define USB2PHY_L1  0x0002

/* USB_SSPHYLINK1 */
#define DELAY_PHY_PWR_CHG BIT(1)

/* USB_SSPHYLINK2 */
#define pwd_dn_scale_mask 0x3ffe
#define pwd_dn_scale(x)  ((x) << 1)

/* USB_CSR_DUMMY1 */
#define DYNAMIC_BURST  0x0001

/* USB_CSR_DUMMY2 */
#define EP4_FULL_FC  0x0001

/* USB_DEV_STAT */
#define STAT_SPEED_MASK  0x0006
#define STAT_SPEED_HIGH  0x0000
#define STAT_SPEED_FULL  0x0002

/* USB_FW_FIX_EN0 */
#define FW_FIX_SUSPEND  BIT(14)

/* USB_FW_FIX_EN1 */
#define FW_IP_RESET_EN  BIT(9)

/* USB_LPM_CONFIG */
#define LPM_U1U2_EN  BIT(0)

/* USB_TX_AGG */
#define TX_AGG_MAX_THRESHOLD 0x03

/* USB_RX_BUF_TH */
#define RX_THR_SUPPER  0x0c350180
#define RX_THR_HIGH  0x7a120180
#define RX_THR_SLOW  0xffff0180
#define RX_THR_B  0x00010001

/* USB_TX_DMA */
#define TEST_MODE_DISABLE 0x00000001
#define TX_SIZE_ADJUST1  0x00000100

/* USB_BMU_RESET */
#define BMU_RESET_EP_IN  0x01
#define BMU_RESET_EP_OUT 0x02

/* USB_BMU_CONFIG */
#define ACT_ODMA  BIT(1)

/* USB_UPT_RXDMA_OWN */
#define OWN_UPDATE  BIT(0)
#define OWN_CLEAR  BIT(1)

/* USB_FW_TASK */
#define FC_PATCH_TASK  BIT(1)

/* USB_RX_AGGR_NUM */
#define RX_AGGR_NUM_MASK 0x1ff

/* USB_UPS_CTRL */
#define POWER_CUT  0x0100

/* USB_PM_CTRL_STATUS */
#define RESUME_INDICATE  0x0001

/* USB_ECM_OPTION */
#define BYPASS_MAC_RESET BIT(5)

/* USB_CSTMR */
#define FORCE_SUPER  BIT(0)

/* USB_MISC_2 */
#define UPS_FORCE_PWR_DOWN BIT(0)

/* USB_ECM_OP */
#define EN_ALL_SPEED  BIT(0)

/* USB_GPHY_CTRL */
#define GPHY_PATCH_DONE  BIT(2)
#define BYPASS_FLASH  BIT(5)
#define BACKUP_RESTRORE  BIT(6)

/* USB_SPEED_OPTION */
#define RG_PWRDN_EN  BIT(8)
#define ALL_SPEED_OFF  BIT(9)

/* USB_FW_CTRL */
#define FLOW_CTRL_PATCH_OPT BIT(1)
#define AUTO_SPEEDUP  BIT(3)
#define FLOW_CTRL_PATCH_2 BIT(8)

/* USB_FC_TIMER */
#define CTRL_TIMER_EN  BIT(15)

/* USB_USB_CTRL */
#define CDC_ECM_EN  BIT(3)
#define RX_AGG_DISABLE  0x0010
#define RX_ZERO_EN  0x0080

/* USB_U2P3_CTRL */
#define U2P3_ENABLE  0x0001
#define RX_DETECT8  BIT(3)

/* USB_POWER_CUT */
#define PWR_EN   0x0001
#define PHASE2_EN  0x0008
#define UPS_EN   BIT(4)
#define USP_PREWAKE  BIT(5)

/* USB_MISC_0 */
#define PCUT_STATUS  0x0001

/* USB_RX_EARLY_TIMEOUT */
#define COALESCE_SUPER   85000U
#define COALESCE_HIGH  250000U
#define COALESCE_SLOW  524280U

/* USB_WDT1_CTRL */
#define WTD1_EN   BIT(0)

/* USB_WDT11_CTRL */
#define TIMER11_EN  0x0001

/* USB_LPM_CTRL */
/* bit 4 ~ 5: fifo empty boundary */
#define FIFO_EMPTY_1FB  0x30 /* 0x1fb * 64 = 32448 bytes */
/* bit 2 ~ 3: LMP timer */
#define LPM_TIMER_MASK  0x0c
#define LPM_TIMER_500MS  0x04 /* 500 ms */
#define LPM_TIMER_500US  0x0c /* 500 us */
#define ROK_EXIT_LPM  0x02

/* USB_AFE_CTRL2 */
#define SEN_VAL_MASK  0xf800
#define SEN_VAL_NORMAL  0xa000
#define SEL_RXIDLE  0x0100

/* USB_UPHY_XTAL */
#define OOBS_POLLING  BIT(8)

/* USB_UPS_CFG */
#define SAW_CNT_1MS_MASK 0x0fff
#define MID_REVERSE  BIT(5) /* RTL8156A */

/* USB_UPS_FLAGS */
#define UPS_FLAGS_R_TUNE  BIT(0)
#define UPS_FLAGS_EN_10M_CKDIV  BIT(1)
#define UPS_FLAGS_250M_CKDIV  BIT(2)
#define UPS_FLAGS_EN_ALDPS  BIT(3)
#define UPS_FLAGS_CTAP_SHORT_DIS BIT(4)
#define UPS_FLAGS_SPEED_MASK  (0xf << 16)
#define ups_flags_speed(x)  ((x) << 16)
#define UPS_FLAGS_EN_EEE  BIT(20)
#define UPS_FLAGS_EN_500M_EEE  BIT(21)
#define UPS_FLAGS_EN_EEE_CKDIV  BIT(22)
#define UPS_FLAGS_EEE_PLLOFF_100 BIT(23)
#define UPS_FLAGS_EEE_PLLOFF_GIGA BIT(24)
#define UPS_FLAGS_EEE_CMOD_LV_EN BIT(25)
#define UPS_FLAGS_EN_GREEN  BIT(26)
#define UPS_FLAGS_EN_FLOW_CTR  BIT(27)

enum spd_duplex {
 NWAY_10M_HALF,
 NWAY_10M_FULL,
 NWAY_100M_HALF,
 NWAY_100M_FULL,
 NWAY_1000M_FULL,
 FORCE_10M_HALF,
 FORCE_10M_FULL,
 FORCE_100M_HALF,
 FORCE_100M_FULL,
 FORCE_1000M_FULL,
 NWAY_2500M_FULL,
};

/* OCP_ALDPS_CONFIG */
#define ENPWRSAVE  0x8000
#define ENPDNPS   0x0200
#define LINKENA   0x0100
#define DIS_SDSAVE  0x0010

/* OCP_PHY_STATUS */
#define PHY_STAT_MASK  0x0007
#define PHY_STAT_EXT_INIT 2
#define PHY_STAT_LAN_ON  3
#define PHY_STAT_PWRDN  5

/* OCP_INTR_EN */
#define INTR_SPEED_FORCE BIT(3)

/* OCP_NCTL_CFG */
#define PGA_RETURN_EN  BIT(1)

/* OCP_POWER_CFG */
#define EEE_CLKDIV_EN  0x8000
#define EN_ALDPS  0x0004
#define EN_10M_PLLOFF  0x0001

/* OCP_EEE_CONFIG1 */
#define RG_TXLPI_MSK_HFDUP 0x8000
#define RG_MATCLR_EN  0x4000
#define EEE_10_CAP  0x2000
#define EEE_NWAY_EN  0x1000
#define TX_QUIET_EN  0x0200
#define RX_QUIET_EN  0x0100
#define sd_rise_time_mask 0x0070
#define sd_rise_time(x)  (min(x, 7) << 4) /* bit 4 ~ 6 */
#define RG_RXLPI_MSK_HFDUP 0x0008
#define SDFALLTIME  0x0007 /* bit 0 ~ 2 */

/* OCP_EEE_CONFIG2 */
#define RG_LPIHYS_NUM  0x7000 /* bit 12 ~ 15 */
#define RG_DACQUIET_EN  0x0400
#define RG_LDVQUIET_EN  0x0200
#define RG_CKRSEL  0x0020
#define RG_EEEPRG_EN  0x0010

/* OCP_EEE_CONFIG3 */
#define fast_snr_mask  0xff80
#define fast_snr(x)  (min(x, 0x1ff) << 7) /* bit 7 ~ 15 */
#define RG_LFS_SEL  0x0060 /* bit 6 ~ 5 */
#define MSK_PH   0x0006 /* bit 0 ~ 3 */

/* OCP_EEE_AR */
/* bit[15:14] function */
#define FUN_ADDR  0x0000
#define FUN_DATA  0x4000
/* bit[4:0] device addr */

/* OCP_EEE_CFG */
#define CTAP_SHORT_EN  0x0040
#define EEE10_EN  0x0010

/* OCP_DOWN_SPEED */
#define EN_EEE_CMODE  BIT(14)
#define EN_EEE_1000  BIT(13)
#define EN_EEE_100  BIT(12)
#define EN_10M_CLKDIV  BIT(11)
#define EN_10M_BGOFF  0x0080

/* OCP_10GBT_CTRL */
#define RTL_ADV2_5G_F_R  BIT(5) /* Advertise 2.5GBASE-T fast-retrain */

/* OCP_PHY_STATE */
#define TXDIS_STATE  0x01
#define ABD_STATE  0x02

/* OCP_PHY_PATCH_STAT */
#define PATCH_READY  BIT(6)

/* OCP_PHY_PATCH_CMD */
#define PATCH_REQUEST  BIT(4)

/* OCP_PHY_LOCK */
#define PATCH_LOCK  BIT(0)

/* OCP_ADC_CFG */
#define CKADSEL_L  0x0100
#define ADC_EN   0x0080
#define EN_EMI_L  0x0040

/* OCP_SYSCLK_CFG */
#define sysclk_div_expo(x) (min(x, 5) << 8)
#define clk_div_expo(x)  (min(x, 5) << 4)

/* SRAM_GREEN_CFG */
#define GREEN_ETH_EN  BIT(15)
#define R_TUNE_EN  BIT(11)

/* SRAM_LPF_CFG */
#define LPF_AUTO_TUNE  0x8000

/* SRAM_10M_AMP1 */
#define GDAC_IB_UPALL  0x0008

/* SRAM_10M_AMP2 */
#define AMP_DN   0x0200

/* SRAM_IMPEDANCE */
#define RX_DRIVING_MASK  0x6000

/* SRAM_PHY_LOCK */
#define PHY_PATCH_LOCK  0x0001

/* MAC PASSTHRU */
#define AD_MASK   0xfee0
#define BND_MASK  0x0004
#define BD_MASK   0x0001
#define EFUSE   0xcfdb
#define PASS_THRU_MASK  0x1

#define BP4_SUPER_ONLY  0x1578 /* RTL_VER_04 only */

enum rtl_register_content {
 _2500bps = BIT(10),
 _1250bps = BIT(9),
 _500bps  = BIT(8),
 _tx_flow = BIT(6),
 _rx_flow = BIT(5),
 _1000bps = 0x10,
 _100bps  = 0x08,
 _10bps  = 0x04,
 LINK_STATUS = 0x02,
 FULL_DUP = 0x01,
};

#define is_speed_2500(_speed) (((_speed) & (_2500bps | LINK_STATUS)) == (_2500bps | LINK_STATUS))
#define is_flow_control(_speed) (((_speed) & (_tx_flow | _rx_flow)) == (_tx_flow | _rx_flow))

#define RTL8152_MAX_TX  4
#define RTL8152_MAX_RX  10
#define INTBUFSIZE  2
#define TX_ALIGN  4
#define RX_ALIGN  8

#define RTL8152_RX_MAX_PENDING 4096
#define RTL8152_RXFG_HEADSZ 256

#define INTR_LINK  0x0004

#define RTL8152_RMS  (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
#define RTL8153_RMS  RTL8153_MAX_PACKET
#define RTL8152_TX_TIMEOUT (5 * HZ)
#define mtu_to_size(m)  ((m) + VLAN_ETH_HLEN + ETH_FCS_LEN)
#define size_to_mtu(s)  ((s) - VLAN_ETH_HLEN - ETH_FCS_LEN)
#define rx_reserved_size(x) (mtu_to_size(x) + sizeof(struct rx_desc) + RX_ALIGN)

/* rtl8152 flags */
enum rtl8152_flags {
 RTL8152_INACCESSIBLE = 0,
 RTL8152_SET_RX_MODE,
 WORK_ENABLE,
 RTL8152_LINK_CHG,
 SELECTIVE_SUSPEND,
 PHY_RESET,
 SCHEDULE_TASKLET,
 GREEN_ETHERNET,
 RX_EPROTO,
 IN_PRE_RESET,
 PROBED_WITH_NO_ERRORS,
 PROBE_SHOULD_RETRY,
};

#define DEVICE_ID_LENOVO_USB_C_TRAVEL_HUB  0x721e
#define DEVICE_ID_THINKPAD_ONELINK_PLUS_DOCK  0x3054
#define DEVICE_ID_THINKPAD_THUNDERBOLT3_DOCK_GEN2 0x3082
#define DEVICE_ID_THINKPAD_USB_C_DONGLE   0x720c
#define DEVICE_ID_THINKPAD_USB_C_DOCK_GEN2  0xa387
#define DEVICE_ID_THINKPAD_USB_C_DOCK_GEN3  0x3062
#define DEVICE_ID_THINKPAD_HYBRID_USB_C_DOCK  0xa359

struct tally_counter {
 __le64 tx_packets;
 __le64 rx_packets;
 __le64 tx_errors;
 __le32 rx_errors;
 __le16 rx_missed;
 __le16 align_errors;
 __le32 tx_one_collision;
 __le32 tx_multi_collision;
 __le64 rx_unicast;
 __le64 rx_broadcast;
 __le32 rx_multicast;
 __le16 tx_aborted;
 __le16 tx_underrun;
};

struct rx_desc {
 __le32 opts1;
#define RX_LEN_MASK   0x7fff

 __le32 opts2;
#define RD_UDP_CS   BIT(23)
#define RD_TCP_CS   BIT(22)
#define RD_IPV6_CS   BIT(20)
#define RD_IPV4_CS   BIT(19)

 __le32 opts3;
#define IPF    BIT(23) /* IP checksum fail */
#define UDPF    BIT(22) /* UDP checksum fail */
#define TCPF    BIT(21) /* TCP checksum fail */
#define RX_VLAN_TAG   BIT(16)

 __le32 opts4;
 __le32 opts5;
 __le32 opts6;
};

struct tx_desc {
 __le32 opts1;
#define TX_FS   BIT(31) /* First segment of a packet */
#define TX_LS   BIT(30) /* Final segment of a packet */
#define GTSENDV4  BIT(28)
#define GTSENDV6  BIT(27)
#define GTTCPHO_SHIFT  18
#define GTTCPHO_MAX  0x7fU
#define TX_LEN_MAX  0x3ffffU

 __le32 opts2;
#define UDP_CS   BIT(31) /* Calculate UDP/IP checksum */
#define TCP_CS   BIT(30) /* Calculate TCP/IP checksum */
#define IPV4_CS   BIT(29) /* Calculate IPv4 checksum */
#define IPV6_CS   BIT(28) /* Calculate IPv6 checksum */
#define MSS_SHIFT  17
#define MSS_MAX   0x7ffU
#define TCPHO_SHIFT  17
#define TCPHO_MAX  0x7ffU
#define TX_VLAN_TAG  BIT(16)
};

struct r8152;

struct rx_agg {
 struct list_head list, info_list;
 struct urb *urb;
 struct r8152 *context;
 struct page *page;
 void *buffer;
};

struct tx_agg {
 struct list_head list;
 struct urb *urb;
 struct r8152 *context;
 void *buffer;
 void *head;
 u32 skb_num;
 u32 skb_len;
};

struct r8152 {
 unsigned long flags;
 struct usb_device *udev;
 struct napi_struct napi;
 struct usb_interface *intf;
 struct net_device *netdev;
 struct urb *intr_urb;
 struct tx_agg tx_info[RTL8152_MAX_TX];
 struct list_head rx_info, rx_used;
 struct list_head rx_done, tx_free;
 struct sk_buff_head tx_queue, rx_queue;
 spinlock_t rx_lock, tx_lock;
 struct delayed_work schedule, hw_phy_work;
 struct mii_if_info mii;
 struct mutex control; /* use for hw setting */
#ifdef CONFIG_PM_SLEEP
 struct notifier_block pm_notifier;
#endif
 struct tasklet_struct tx_tl;

 struct rtl_ops {
  void (*init)(struct r8152 *tp);
  int (*enable)(struct r8152 *tp);
  void (*disable)(struct r8152 *tp);
  void (*up)(struct r8152 *tp);
  void (*down)(struct r8152 *tp);
  void (*unload)(struct r8152 *tp);
  int (*eee_get)(struct r8152 *tp, struct ethtool_keee *eee);
  int (*eee_set)(struct r8152 *tp, struct ethtool_keee *eee);
  bool (*in_nway)(struct r8152 *tp);
  void (*hw_phy_cfg)(struct r8152 *tp);
  void (*autosuspend_en)(struct r8152 *tp, bool enable);
  void (*change_mtu)(struct r8152 *tp);
 } rtl_ops;

 struct ups_info {
  u32 r_tune:1;
  u32 _10m_ckdiv:1;
  u32 _250m_ckdiv:1;
  u32 aldps:1;
  u32 lite_mode:2;
  u32 speed_duplex:4;
  u32 eee:1;
  u32 eee_lite:1;
  u32 eee_ckdiv:1;
  u32 eee_plloff_100:1;
  u32 eee_plloff_giga:1;
  u32 eee_cmod_lv:1;
  u32 green:1;
  u32 flow_control:1;
  u32 ctap_short_off:1;
 } ups_info;

#define RTL_VER_SIZE  32

 struct rtl_fw {
  const char *fw_name;
  const struct firmware *fw;

  char version[RTL_VER_SIZE];
  int (*pre_fw)(struct r8152 *tp);
  int (*post_fw)(struct r8152 *tp);

  bool retry;
 } rtl_fw;

 atomic_t rx_count;

 bool eee_en;
 int intr_interval;
 u32 saved_wolopts;
 u32 msg_enable;
 u32 tx_qlen;
 u32 coalesce;
 u32 advertising;
 u32 rx_buf_sz;
 u32 rx_copybreak;
 u32 rx_pending;
 u32 fc_pause_on, fc_pause_off;

 unsigned int pipe_in, pipe_out, pipe_intr, pipe_ctrl_in, pipe_ctrl_out;

 u32 support_2500full:1;
 u32 lenovo_macpassthru:1;
 u32 dell_tb_rx_agg_bug:1;
 u16 ocp_base;
 u16 speed;
 u16 eee_adv;
 u8 *intr_buff;
 u8 version;
 u8 duplex;
 u8 autoneg;

 unsigned int reg_access_reset_count;
};

/**
 * struct fw_block - block type and total length
 * @type: type of the current block, such as RTL_FW_END, RTL_FW_PLA,
 * RTL_FW_USB and so on.
 * @length: total length of the current block.
 */

struct fw_block {
 __le32 type;
 __le32 length;
} __packed;

/**
 * struct fw_header - header of the firmware file
 * @checksum: checksum of sha256 which is calculated from the whole file
 * except the checksum field of the file. That is, calculate sha256
 * from the version field to the end of the file.
 * @version: version of this firmware.
 * @blocks: the first firmware block of the file
 */

struct fw_header {
 u8 checksum[32];
 char version[RTL_VER_SIZE];
 struct fw_block blocks[];
} __packed;

enum rtl8152_fw_flags {
 FW_FLAGS_USB = 0,
 FW_FLAGS_PLA,
 FW_FLAGS_START,
 FW_FLAGS_STOP,
 FW_FLAGS_NC,
 FW_FLAGS_NC1,
 FW_FLAGS_NC2,
 FW_FLAGS_UC2,
 FW_FLAGS_UC,
 FW_FLAGS_SPEED_UP,
 FW_FLAGS_VER,
};

enum rtl8152_fw_fixup_cmd {
 FW_FIXUP_AND = 0,
 FW_FIXUP_OR,
 FW_FIXUP_NOT,
 FW_FIXUP_XOR,
};

struct fw_phy_set {
 __le16 addr;
 __le16 data;
} __packed;

struct fw_phy_speed_up {
 struct fw_block blk_hdr;
 __le16 fw_offset;
 __le16 version;
 __le16 fw_reg;
 __le16 reserved;
 char info[];
} __packed;

struct fw_phy_ver {
 struct fw_block blk_hdr;
 struct fw_phy_set ver;
 __le32 reserved;
} __packed;

struct fw_phy_fixup {
 struct fw_block blk_hdr;
 struct fw_phy_set setting;
 __le16 bit_cmd;
 __le16 reserved;
} __packed;

struct fw_phy_union {
 struct fw_block blk_hdr;
 __le16 fw_offset;
 __le16 fw_reg;
 struct fw_phy_set pre_set[2];
 struct fw_phy_set bp[8];
 struct fw_phy_set bp_en;
 u8 pre_num;
 u8 bp_num;
 char info[];
} __packed;

/**
 * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB.
 * The layout of the firmware block is:
 * <struct fw_mac> + <info> + <firmware data>.
 * @blk_hdr: firmware descriptor (type, length)
 * @fw_offset: offset of the firmware binary data. The start address of
 * the data would be the address of struct fw_mac + @fw_offset.
 * @fw_reg: the register to load the firmware. Depends on chip.
 * @bp_ba_addr: the register to write break point base address. Depends on
 * chip.
 * @bp_ba_value: break point base address. Depends on chip.
 * @bp_en_addr: the register to write break point enabled mask. Depends
 * on chip.
 * @bp_en_value: break point enabled mask. Depends on the firmware.
 * @bp_start: the start register of break points. Depends on chip.
 * @bp_num: the break point number which needs to be set for this firmware.
 * Depends on the firmware.
 * @bp: break points. Depends on firmware.
 * @reserved: reserved space (unused)
 * @fw_ver_reg: the register to store the fw version.
 * @fw_ver_data: the firmware version of the current type.
 * @info: additional information for debugging, and is followed by the
 * binary data of firmware.
 */

struct fw_mac {
 struct fw_block blk_hdr;
 __le16 fw_offset;
 __le16 fw_reg;
 __le16 bp_ba_addr;
 __le16 bp_ba_value;
 __le16 bp_en_addr;
 __le16 bp_en_value;
 __le16 bp_start;
 __le16 bp_num;
 __le16 bp[16]; /* any value determined by firmware */
 __le32 reserved;
 __le16 fw_ver_reg;
 u8 fw_ver_data;
 char info[];
} __packed;

/**
 * struct fw_phy_patch_key - a firmware block used by RTL_FW_PHY_START.
 * This is used to set patch key when loading the firmware of PHY.
 * @blk_hdr: firmware descriptor (type, length)
 * @key_reg: the register to write the patch key.
 * @key_data: patch key.
 * @reserved: reserved space (unused)
 */

struct fw_phy_patch_key {
 struct fw_block blk_hdr;
 __le16 key_reg;
 __le16 key_data;
 __le32 reserved;
} __packed;

/**
 * struct fw_phy_nc - a firmware block used by RTL_FW_PHY_NC.
 * The layout of the firmware block is:
 * <struct fw_phy_nc> + <info> + <firmware data>.
 * @blk_hdr: firmware descriptor (type, length)
 * @fw_offset: offset of the firmware binary data. The start address of
 * the data would be the address of struct fw_phy_nc + @fw_offset.
 * @fw_reg: the register to load the firmware. Depends on chip.
 * @ba_reg: the register to write the base address. Depends on chip.
 * @ba_data: base address. Depends on chip.
 * @patch_en_addr: the register of enabling patch mode. Depends on chip.
 * @patch_en_value: patch mode enabled mask. Depends on the firmware.
 * @mode_reg: the regitster of switching the mode.
 * @mode_pre: the mode needing to be set before loading the firmware.
 * @mode_post: the mode to be set when finishing to load the firmware.
 * @reserved: reserved space (unused)
 * @bp_start: the start register of break points. Depends on chip.
 * @bp_num: the break point number which needs to be set for this firmware.
 * Depends on the firmware.
 * @bp: break points. Depends on firmware.
 * @info: additional information for debugging, and is followed by the
 * binary data of firmware.
 */

struct fw_phy_nc {
 struct fw_block blk_hdr;
 __le16 fw_offset;
 __le16 fw_reg;
 __le16 ba_reg;
 __le16 ba_data;
 __le16 patch_en_addr;
 __le16 patch_en_value;
 __le16 mode_reg;
 __le16 mode_pre;
 __le16 mode_post;
 __le16 reserved;
 __le16 bp_start;
 __le16 bp_num;
 __le16 bp[4];
 char info[];
} __packed;

enum rtl_fw_type {
 RTL_FW_END = 0,
 RTL_FW_PLA,
 RTL_FW_USB,
 RTL_FW_PHY_START,
 RTL_FW_PHY_STOP,
 RTL_FW_PHY_NC,
 RTL_FW_PHY_FIXUP,
 RTL_FW_PHY_UNION_NC,
 RTL_FW_PHY_UNION_NC1,
 RTL_FW_PHY_UNION_NC2,
 RTL_FW_PHY_UNION_UC2,
 RTL_FW_PHY_UNION_UC,
 RTL_FW_PHY_UNION_MISC,
 RTL_FW_PHY_SPEED_UP,
 RTL_FW_PHY_VER,
};

enum rtl_version {
 RTL_VER_UNKNOWN = 0,
 RTL_VER_01,
 RTL_VER_02,
 RTL_VER_03,
 RTL_VER_04,
 RTL_VER_05,
 RTL_VER_06,
 RTL_VER_07,
 RTL_VER_08,
 RTL_VER_09,

 RTL_TEST_01,
 RTL_VER_10,
 RTL_VER_11,
 RTL_VER_12,
 RTL_VER_13,
 RTL_VER_14,
 RTL_VER_15,

 RTL_VER_MAX
};

enum tx_csum_stat {
 TX_CSUM_SUCCESS = 0,
 TX_CSUM_TSO,
 TX_CSUM_NONE
};

#define RTL_ADVERTISED_10_HALF   BIT(0)
#define RTL_ADVERTISED_10_FULL   BIT(1)
#define RTL_ADVERTISED_100_HALF   BIT(2)
#define RTL_ADVERTISED_100_FULL   BIT(3)
#define RTL_ADVERTISED_1000_HALF  BIT(4)
#define RTL_ADVERTISED_1000_FULL  BIT(5)
#define RTL_ADVERTISED_2500_FULL  BIT(6)

/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
 * The RTL chips use a 64 element hash table based on the Ethernet CRC.
 */

static const int multicast_filter_limit = 32;
static unsigned int agg_buf_sz = 16384;

#define RTL_LIMITED_TSO_SIZE (size_to_mtu(agg_buf_sz) - sizeof(struct tx_desc))

/* If register access fails then we block access and issue a reset. If this
 * happens too many times in a row without a successful access then we stop
 * trying to reset and just leave access blocked.
 */

#define REGISTER_ACCESS_MAX_RESETS 3

static void rtl_set_inaccessible(struct r8152 *tp)
{
 set_bit(RTL8152_INACCESSIBLE, &tp->flags);
 smp_mb__after_atomic();
}

static void rtl_set_accessible(struct r8152 *tp)
{
 clear_bit(RTL8152_INACCESSIBLE, &tp->flags);
 smp_mb__after_atomic();
}

static
int r8152_control_msg(struct r8152 *tp, unsigned int pipe, __u8 request,
        __u8 requesttype, __u16 value, __u16 index, void *data,
        __u16 size, const char *msg_tag)
{
 struct usb_device *udev = tp->udev;
 int ret;

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return -ENODEV;

 ret = usb_control_msg(udev, pipe, request, requesttype,
         value, index, data, size,
         USB_CTRL_GET_TIMEOUT);

 /* No need to issue a reset to report an error if the USB device got
 * unplugged; just return immediately.
 */

 if (ret == -ENODEV)
  return ret;

 /* If the write was successful then we're done */
 if (ret >= 0) {
  tp->reg_access_reset_count = 0;
  return ret;
 }

 dev_err(&udev->dev,
  "Failed to %s %d bytes at %#06x/%#06x (%d)\n",
  msg_tag, size, value, index, ret);

 /* Block all future register access until we reset. Much of the code
 * in the driver doesn't check for errors. Notably, many parts of the
 * driver do a read/modify/write of a register value without
 * confirming that the read succeeded. Writing back modified garbage
 * like this can fully wedge the adapter, requiring a power cycle.
 */

 rtl_set_inaccessible(tp);

 /* If probe hasn't yet finished, then we'll request a retry of the
 * whole probe routine if we get any control transfer errors. We
 * never have to clear this bit since we free/reallocate the whole "tp"
 * structure if we retry probe.
 */

 if (!test_bit(PROBED_WITH_NO_ERRORS, &tp->flags)) {
  set_bit(PROBE_SHOULD_RETRY, &tp->flags);
  return ret;
 }

 /* Failing to access registers in pre-reset is not surprising since we
 * wouldn't be resetting if things were behaving normally. The register
 * access we do in pre-reset isn't truly mandatory--we're just reusing
 * the disable() function and trying to be nice by powering the
 * adapter down before resetting it. Thus, if we're in pre-reset,
 * we'll return right away and not try to queue up yet another reset.
 * We know the post-reset is already coming.
 */

 if (test_bit(IN_PRE_RESET, &tp->flags))
  return ret;

 if (tp->reg_access_reset_count < REGISTER_ACCESS_MAX_RESETS) {
  usb_queue_reset_device(tp->intf);
  tp->reg_access_reset_count++;
 } else if (tp->reg_access_reset_count == REGISTER_ACCESS_MAX_RESETS) {
  dev_err(&udev->dev,
   "Tried to reset %d times; giving up.\n",
   REGISTER_ACCESS_MAX_RESETS);
 }

 return ret;
}

static
int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
{
 int ret;
 void *tmp;

 tmp = kmalloc(size, GFP_KERNEL);
 if (!tmp)
  return -ENOMEM;

 ret = r8152_control_msg(tp, tp->pipe_ctrl_in,
    RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
    value, index, tmp, size, "read");

 if (ret < 0)
  memset(data, 0xff, size);
 else
  memcpy(data, tmp, size);

 kfree(tmp);

 return ret;
}

static
int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
{
 int ret;
 void *tmp;

 tmp = kmemdup(data, size, GFP_KERNEL);
 if (!tmp)
  return -ENOMEM;

 ret = r8152_control_msg(tp, tp->pipe_ctrl_out,
    RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
    value, index, tmp, size, "write");

 kfree(tmp);

 return ret;
}

static void rtl_set_unplug(struct r8152 *tp)
{
 if (tp->udev->state == USB_STATE_NOTATTACHED)
  rtl_set_inaccessible(tp);
}

static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,
       void *data, u16 type)
{
 u16 limit = 64;
 int ret = 0;

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return -ENODEV;

 /* both size and indix must be 4 bytes align */
 if ((size & 3) || !size || (index & 3) || !data)
  return -EPERM;

 if ((u32)index + (u32)size > 0xffff)
  return -EPERM;

 while (size) {
  if (size > limit) {
   ret = get_registers(tp, index, type, limit, data);
   if (ret < 0)
    break;

   index += limit;
   data += limit;
   size -= limit;
  } else {
   ret = get_registers(tp, index, type, size, data);
   if (ret < 0)
    break;

   index += size;
   data += size;
   size = 0;
   break;
  }
 }

 if (ret == -ENODEV)
  rtl_set_unplug(tp);

 return ret;
}

static int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen,
        u16 size, void *data, u16 type)
{
 int ret;
 u16 byteen_start, byteen_end, byen;
 u16 limit = 512;

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return -ENODEV;

 /* both size and indix must be 4 bytes align */
 if ((size & 3) || !size || (index & 3) || !data)
  return -EPERM;

 if ((u32)index + (u32)size > 0xffff)
  return -EPERM;

 byteen_start = byteen & BYTE_EN_START_MASK;
 byteen_end = byteen & BYTE_EN_END_MASK;

 byen = byteen_start | (byteen_start << 4);

 /* Split the first DWORD if the byte_en is not 0xff */
 if (byen != BYTE_EN_DWORD) {
  ret = set_registers(tp, index, type | byen, 4, data);
  if (ret < 0)
   goto error1;

  index += 4;
  data += 4;
  size -= 4;
 }

 if (size) {
  byen = byteen_end | (byteen_end >> 4);

  /* Split the last DWORD if the byte_en is not 0xff */
  if (byen != BYTE_EN_DWORD)
   size -= 4;

  while (size) {
   if (size > limit) {
    ret = set_registers(tp, index,
          type | BYTE_EN_DWORD,
          limit, data);
    if (ret < 0)
     goto error1;

    index += limit;
    data += limit;
    size -= limit;
   } else {
    ret = set_registers(tp, index,
          type | BYTE_EN_DWORD,
          size, data);
    if (ret < 0)
     goto error1;

    index += size;
    data += size;
    size = 0;
    break;
   }
  }

  /* Set the last DWORD */
  if (byen != BYTE_EN_DWORD)
   ret = set_registers(tp, index, type | byen, 4, data);
 }

error1:
 if (ret == -ENODEV)
  rtl_set_unplug(tp);

 return ret;
}

static inline
int pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data)
{
 return generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA);
}

static inline
int pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data)
{
 return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_PLA);
}

static inline
int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data)
{
 return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_USB);
}

static u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index)
{
 __le32 data;

 generic_ocp_read(tp, index, sizeof(data), &data, type);

 return __le32_to_cpu(data);
}

static void ocp_write_dword(struct r8152 *tp, u16 type, u16 index, u32 data)
{
 __le32 tmp = __cpu_to_le32(data);

 generic_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(tmp), &tmp, type);
}

static u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index)
{
 u32 data;
 __le32 tmp;
 u16 byen = BYTE_EN_WORD;
 u8 shift = index & 2;

 index &= ~3;
 byen <<= shift;

 generic_ocp_read(tp, index, sizeof(tmp), &tmp, type | byen);

 data = __le32_to_cpu(tmp);
 data >>= (shift * 8);
 data &= 0xffff;

 return (u16)data;
}

static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data)
{
 u32 mask = 0xffff;
 __le32 tmp;
 u16 byen = BYTE_EN_WORD;
 u8 shift = index & 2;

 data &= mask;

 if (index & 2) {
  byen <<= shift;
  mask <<= (shift * 8);
  data <<= (shift * 8);
  index &= ~3;
 }

 tmp = __cpu_to_le32(data);

 generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type);
}

static u8 ocp_read_byte(struct r8152 *tp, u16 type, u16 index)
{
 u32 data;
 __le32 tmp;
 u8 shift = index & 3;

 index &= ~3;

 generic_ocp_read(tp, index, sizeof(tmp), &tmp, type);

 data = __le32_to_cpu(tmp);
 data >>= (shift * 8);
 data &= 0xff;

 return (u8)data;
}

static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data)
{
 u32 mask = 0xff;
 __le32 tmp;
 u16 byen = BYTE_EN_BYTE;
 u8 shift = index & 3;

 data &= mask;

 if (index & 3) {
  byen <<= shift;
  mask <<= (shift * 8);
  data <<= (shift * 8);
  index &= ~3;
 }

 tmp = __cpu_to_le32(data);

 generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type);
}

static u16 ocp_reg_read(struct r8152 *tp, u16 addr)
{
 u16 ocp_base, ocp_index;

 ocp_base = addr & 0xf000;
 if (ocp_base != tp->ocp_base) {
  ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base);
  tp->ocp_base = ocp_base;
 }

 ocp_index = (addr & 0x0fff) | 0xb000;
 return ocp_read_word(tp, MCU_TYPE_PLA, ocp_index);
}

static void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data)
{
 u16 ocp_base, ocp_index;

 ocp_base = addr & 0xf000;
 if (ocp_base != tp->ocp_base) {
  ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base);
  tp->ocp_base = ocp_base;
 }

 ocp_index = (addr & 0x0fff) | 0xb000;
 ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data);
}

static inline void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value)
{
 ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value);
}

static inline int r8152_mdio_read(struct r8152 *tp, u32 reg_addr)
{
 return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2);
}

static void sram_write(struct r8152 *tp, u16 addr, u16 data)
{
 ocp_reg_write(tp, OCP_SRAM_ADDR, addr);
 ocp_reg_write(tp, OCP_SRAM_DATA, data);
}

static u16 sram_read(struct r8152 *tp, u16 addr)
{
 ocp_reg_write(tp, OCP_SRAM_ADDR, addr);
 return ocp_reg_read(tp, OCP_SRAM_DATA);
}

static int read_mii_word(struct net_device *netdev, int phy_id, int reg)
{
 struct r8152 *tp = netdev_priv(netdev);
 int ret;

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return -ENODEV;

 if (phy_id != R8152_PHY_ID)
  return -EINVAL;

 ret = r8152_mdio_read(tp, reg);

 return ret;
}

static
void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val)
{
 struct r8152 *tp = netdev_priv(netdev);

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return;

 if (phy_id != R8152_PHY_ID)
  return;

 r8152_mdio_write(tp, reg, val);
}

static int
r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags);

static int
rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
    u32 advertising);

static int __rtl8152_set_mac_address(struct net_device *netdev,
         struct sockaddr_storage *addr,
         bool in_resume)
{
 struct r8152 *tp = netdev_priv(netdev);
 int ret = -EADDRNOTAVAIL;

 if (!is_valid_ether_addr(addr->__data))
  goto out1;

 if (!in_resume) {
  ret = usb_autopm_get_interface(tp->intf);
  if (ret < 0)
   goto out1;
 }

 mutex_lock(&tp->control);

 eth_hw_addr_set(netdev, addr->__data);

 ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
 pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, addr->__data);
 ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);

 mutex_unlock(&tp->control);

 if (!in_resume)
  usb_autopm_put_interface(tp->intf);
out1:
 return ret;
}

static int rtl8152_set_mac_address(struct net_device *netdev, void *p)
{
 return __rtl8152_set_mac_address(netdev, p, false);
}

/* Devices containing proper chips can support a persistent
 * host system provided MAC address.
 * Examples of this are Dell TB15 and Dell WD15 docks
 */

static int vendor_mac_passthru_addr_read(struct r8152 *tp,
      struct sockaddr_storage *ss)
{
 acpi_status status;
 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 union acpi_object *obj;
 int ret = -EINVAL;
 u32 ocp_data;
 unsigned char buf[6];
 char *mac_obj_name;
 acpi_object_type mac_obj_type;
 int mac_strlen;

 if (tp->lenovo_macpassthru) {
  mac_obj_name = "\\MACA";
  mac_obj_type = ACPI_TYPE_STRING;
  mac_strlen = 0x16;
 } else {
  /* test for -AD variant of RTL8153 */
  ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
  if ((ocp_data & AD_MASK) == 0x1000) {
   /* test for MAC address pass-through bit */
   ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, EFUSE);
   if ((ocp_data & PASS_THRU_MASK) != 1) {
    netif_dbg(tp, probe, tp->netdev,
      "No efuse for RTL8153-AD MAC pass through\n");
    return -ENODEV;
   }
  } else {
   /* test for RTL8153-BND and RTL8153-BD */
   ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_1);
   if ((ocp_data & BND_MASK) == 0 && (ocp_data & BD_MASK) == 0) {
    netif_dbg(tp, probe, tp->netdev,
      "Invalid variant for MAC pass through\n");
    return -ENODEV;
   }
  }

  mac_obj_name = "\\_SB.AMAC";
  mac_obj_type = ACPI_TYPE_BUFFER;
  mac_strlen = 0x17;
 }

 /* returns _AUXMAC_#AABBCCDDEEFF# */
 status = acpi_evaluate_object(NULL, mac_obj_name, NULL, &buffer);
 obj = (union acpi_object *)buffer.pointer;
 if (!ACPI_SUCCESS(status))
  return -ENODEV;
 if (obj->type != mac_obj_type || obj->string.length != mac_strlen) {
  netif_warn(tp, probe, tp->netdev,
      "Invalid buffer for pass-thru MAC addr: (%d, %d)\n",
      obj->type, obj->string.length);
  goto amacout;
 }

 if (strncmp(obj->string.pointer, "_AUXMAC_#", 9) != 0 ||
     strncmp(obj->string.pointer + 0x15, "#", 1) != 0) {
  netif_warn(tp, probe, tp->netdev,
      "Invalid header when reading pass-thru MAC addr\n");
  goto amacout;
 }
 ret = hex2bin(buf, obj->string.pointer + 9, 6);
 if (!(ret == 0 && is_valid_ether_addr(buf))) {
  netif_warn(tp, probe, tp->netdev,
      "Invalid MAC for pass-thru MAC addr: %d, %pM\n",
      ret, buf);
  ret = -EINVAL;
  goto amacout;
 }
 memcpy(ss->__data, buf, 6);
 tp->netdev->addr_assign_type = NET_ADDR_STOLEN;
 netif_info(tp, probe, tp->netdev,
     "Using pass-thru MAC addr %pM\n", ss->__data);

amacout:
 kfree(obj);
 return ret;
}

static int determine_ethernet_addr(struct r8152 *tp,
       struct sockaddr_storage *ss)
{
 struct net_device *dev = tp->netdev;
 int ret;

 ss->ss_family = dev->type;

 ret = eth_platform_get_mac_address(&tp->udev->dev, ss->__data);
 if (ret < 0) {
  if (tp->version == RTL_VER_01) {
   ret = pla_ocp_read(tp, PLA_IDR, 8, ss->__data);
  } else {
   /* if device doesn't support MAC pass through this will
 * be expected to be non-zero
 */

   ret = vendor_mac_passthru_addr_read(tp, ss);
   if (ret < 0)
    ret = pla_ocp_read(tp, PLA_BACKUP, 8,
         ss->__data);
  }
 }

 if (ret < 0) {
  netif_err(tp, probe, dev, "Get ether addr fail\n");
 } else if (!is_valid_ether_addr(ss->__data)) {
  netif_err(tp, probe, dev, "Invalid ether addr %pM\n",
     ss->__data);
  eth_hw_addr_random(dev);
  ether_addr_copy(ss->__data, dev->dev_addr);
  netif_info(tp, probe, dev, "Random ether addr %pM\n",
      ss->__data);
  return 0;
 }

 return ret;
}

static int set_ethernet_addr(struct r8152 *tp, bool in_resume)
{
 struct net_device *dev = tp->netdev;
 struct sockaddr_storage ss;
 int ret;

 ret = determine_ethernet_addr(tp, &ss);
 if (ret < 0)
  return ret;

 if (tp->version == RTL_VER_01)
  eth_hw_addr_set(dev, ss.__data);
 else
  ret = __rtl8152_set_mac_address(dev, &ss, in_resume);

 return ret;
}

static void read_bulk_callback(struct urb *urb)
{
 struct net_device *netdev;
 int status = urb->status;
 struct rx_agg *agg;
 struct r8152 *tp;
 unsigned long flags;

 agg = urb->context;
 if (!agg)
  return;

 tp = agg->context;
 if (!tp)
  return;

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return;

 if (!test_bit(WORK_ENABLE, &tp->flags))
  return;

 netdev = tp->netdev;

 /* When link down, the driver would cancel all bulks. */
 /* This avoid the re-submitting bulk */
 if (!netif_carrier_ok(netdev))
  return;

 usb_mark_last_busy(tp->udev);

 switch (status) {
 case 0:
  if (urb->actual_length < ETH_ZLEN)
   break;

  spin_lock_irqsave(&tp->rx_lock, flags);
  list_add_tail(&agg->list, &tp->rx_done);
  spin_unlock_irqrestore(&tp->rx_lock, flags);
  napi_schedule(&tp->napi);
  return;
 case -ESHUTDOWN:
  rtl_set_unplug(tp);
  netif_device_detach(tp->netdev);
  return;
 case -EPROTO:
  urb->actual_length = 0;
  spin_lock_irqsave(&tp->rx_lock, flags);
  list_add_tail(&agg->list, &tp->rx_done);
  spin_unlock_irqrestore(&tp->rx_lock, flags);
  set_bit(RX_EPROTO, &tp->flags);
  schedule_delayed_work(&tp->schedule, 1);
  return;
 case -ENOENT:
  return/* the urb is in unlink state */
 case -ETIME:
  if (net_ratelimit())
   netdev_warn(netdev, "maybe reset is needed?\n");
  break;
 default:
  if (net_ratelimit())
   netdev_warn(netdev, "Rx status %d\n", status);
  break;
 }

 r8152_submit_rx(tp, agg, GFP_ATOMIC);
}

static void write_bulk_callback(struct urb *urb)
{
 struct net_device_stats *stats;
 struct net_device *netdev;
 struct tx_agg *agg;
 struct r8152 *tp;
 unsigned long flags;
 int status = urb->status;

 agg = urb->context;
 if (!agg)
  return;

 tp = agg->context;
 if (!tp)
  return;

 netdev = tp->netdev;
 stats = &netdev->stats;
 if (status) {
  if (net_ratelimit())
   netdev_warn(netdev, "Tx status %d\n", status);
  stats->tx_errors += agg->skb_num;
 } else {
  stats->tx_packets += agg->skb_num;
  stats->tx_bytes += agg->skb_len;
 }

 spin_lock_irqsave(&tp->tx_lock, flags);
 list_add_tail(&agg->list, &tp->tx_free);
 spin_unlock_irqrestore(&tp->tx_lock, flags);

 usb_autopm_put_interface_async(tp->intf);

 if (!netif_carrier_ok(netdev))
  return;

 if (!test_bit(WORK_ENABLE, &tp->flags))
  return;

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return;

 if (!skb_queue_empty(&tp->tx_queue))
  tasklet_schedule(&tp->tx_tl);
}

static void intr_callback(struct urb *urb)
{
 struct r8152 *tp;
 __le16 *d;
 int status = urb->status;
 int res;

 tp = urb->context;
 if (!tp)
  return;

 if (!test_bit(WORK_ENABLE, &tp->flags))
  return;

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return;

 switch (status) {
 case 0:   /* success */
  break;
 case -ECONNRESET: /* unlink */
 case -ESHUTDOWN:
  netif_device_detach(tp->netdev);
  fallthrough;
 case -ENOENT:
 case -EPROTO:
  netif_info(tp, intr, tp->netdev,
      "Stop submitting intr, status %d\n", status);
  return;
 case -EOVERFLOW:
  if (net_ratelimit())
   netif_info(tp, intr, tp->netdev,
       "intr status -EOVERFLOW\n");
  goto resubmit;
 /* -EPIPE:  should clear the halt */
 default:
  netif_info(tp, intr, tp->netdev, "intr status %d\n", status);
  goto resubmit;
 }

 d = urb->transfer_buffer;
 if (INTR_LINK & __le16_to_cpu(d[0])) {
  if (!netif_carrier_ok(tp->netdev)) {
   set_bit(RTL8152_LINK_CHG, &tp->flags);
   schedule_delayed_work(&tp->schedule, 0);
  }
 } else {
  if (netif_carrier_ok(tp->netdev)) {
   netif_stop_queue(tp->netdev);
   set_bit(RTL8152_LINK_CHG, &tp->flags);
   schedule_delayed_work(&tp->schedule, 0);
  }
 }

resubmit:
 res = usb_submit_urb(urb, GFP_ATOMIC);
 if (res == -ENODEV) {
  rtl_set_unplug(tp);
  netif_device_detach(tp->netdev);
 } else if (res) {
  netif_err(tp, intr, tp->netdev,
     "can't resubmit intr, status %d\n", res);
 }
}

static inline void *rx_agg_align(void *data)
{
 return (void *)ALIGN((uintptr_t)data, RX_ALIGN);
}

static inline void *tx_agg_align(void *data)
{
 return (void *)ALIGN((uintptr_t)data, TX_ALIGN);
}

static void free_rx_agg(struct r8152 *tp, struct rx_agg *agg)
{
 list_del(&agg->info_list);

 usb_free_urb(agg->urb);
 put_page(agg->page);
 kfree(agg);

 atomic_dec(&tp->rx_count);
}

static struct rx_agg *alloc_rx_agg(struct r8152 *tp, gfp_t mflags)
{
 struct net_device *netdev = tp->netdev;
 int node = netdev->dev.parent ? dev_to_node(netdev->dev.parent) : -1;
 unsigned int order = get_order(tp->rx_buf_sz);
 struct rx_agg *rx_agg;
 unsigned long flags;

 rx_agg = kmalloc_node(sizeof(*rx_agg), mflags, node);
 if (!rx_agg)
  return NULL;

 rx_agg->page = alloc_pages(mflags | __GFP_COMP | __GFP_NOWARN, order);
 if (!rx_agg->page)
  goto free_rx;

 rx_agg->buffer = page_address(rx_agg->page);

 rx_agg->urb = usb_alloc_urb(0, mflags);
 if (!rx_agg->urb)
  goto free_buf;

 rx_agg->context = tp;

 INIT_LIST_HEAD(&rx_agg->list);
 INIT_LIST_HEAD(&rx_agg->info_list);
 spin_lock_irqsave(&tp->rx_lock, flags);
 list_add_tail(&rx_agg->info_list, &tp->rx_info);
 spin_unlock_irqrestore(&tp->rx_lock, flags);

 atomic_inc(&tp->rx_count);

 return rx_agg;

free_buf:
 __free_pages(rx_agg->page, order);
free_rx:
 kfree(rx_agg);
 return NULL;
}

static void free_all_mem(struct r8152 *tp)
{
 struct rx_agg *agg, *agg_next;
 unsigned long flags;
 int i;

 spin_lock_irqsave(&tp->rx_lock, flags);

 list_for_each_entry_safe(agg, agg_next, &tp->rx_info, info_list)
  free_rx_agg(tp, agg);

 spin_unlock_irqrestore(&tp->rx_lock, flags);

 WARN_ON(atomic_read(&tp->rx_count));

 for (i = 0; i < RTL8152_MAX_TX; i++) {
  usb_free_urb(tp->tx_info[i].urb);
  tp->tx_info[i].urb = NULL;

  kfree(tp->tx_info[i].buffer);
  tp->tx_info[i].buffer = NULL;
  tp->tx_info[i].head = NULL;
 }

 usb_free_urb(tp->intr_urb);
 tp->intr_urb = NULL;

 kfree(tp->intr_buff);
 tp->intr_buff = NULL;
}

static int alloc_all_mem(struct r8152 *tp)
{
 struct net_device *netdev = tp->netdev;
 struct usb_interface *intf = tp->intf;
 struct usb_host_interface *alt = intf->cur_altsetting;
 struct usb_host_endpoint *ep_intr = alt->endpoint + 2;
 int node, i;

 node = netdev->dev.parent ? dev_to_node(netdev->dev.parent) : -1;

 spin_lock_init(&tp->rx_lock);
 spin_lock_init(&tp->tx_lock);
 INIT_LIST_HEAD(&tp->rx_info);
 INIT_LIST_HEAD(&tp->tx_free);
 INIT_LIST_HEAD(&tp->rx_done);
 skb_queue_head_init(&tp->tx_queue);
 skb_queue_head_init(&tp->rx_queue);
 atomic_set(&tp->rx_count, 0);

 for (i = 0; i < RTL8152_MAX_RX; i++) {
  if (!alloc_rx_agg(tp, GFP_KERNEL))
   goto err1;
 }

 for (i = 0; i < RTL8152_MAX_TX; i++) {
  struct urb *urb;
  u8 *buf;

  buf = kmalloc_node(agg_buf_sz, GFP_KERNEL, node);
  if (!buf)
   goto err1;

  if (buf != tx_agg_align(buf)) {
   kfree(buf);
   buf = kmalloc_node(agg_buf_sz + TX_ALIGN, GFP_KERNEL,
        node);
   if (!buf)
    goto err1;
  }

  urb = usb_alloc_urb(0, GFP_KERNEL);
  if (!urb) {
   kfree(buf);
   goto err1;
  }

  INIT_LIST_HEAD(&tp->tx_info[i].list);
  tp->tx_info[i].context = tp;
  tp->tx_info[i].urb = urb;
  tp->tx_info[i].buffer = buf;
  tp->tx_info[i].head = tx_agg_align(buf);

  list_add_tail(&tp->tx_info[i].list, &tp->tx_free);
 }

 tp->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
 if (!tp->intr_urb)
  goto err1;

 tp->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL);
 if (!tp->intr_buff)
  goto err1;

 tp->intr_interval = (int)ep_intr->desc.bInterval;
 usb_fill_int_urb(tp->intr_urb, tp->udev, tp->pipe_intr,
    tp->intr_buff, INTBUFSIZE, intr_callback,
    tp, tp->intr_interval);

 return 0;

err1:
 free_all_mem(tp);
 return -ENOMEM;
}

static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp)
{
 struct tx_agg *agg = NULL;
 unsigned long flags;

 if (list_empty(&tp->tx_free))
  return NULL;

 spin_lock_irqsave(&tp->tx_lock, flags);
 if (!list_empty(&tp->tx_free)) {
  struct list_head *cursor;

  cursor = tp->tx_free.next;
  list_del_init(cursor);
  agg = list_entry(cursor, struct tx_agg, list);
 }
 spin_unlock_irqrestore(&tp->tx_lock, flags);

 return agg;
}

/* r8152_csum_workaround()
 * The hw limits the value of the transport offset. When the offset is out of
 * range, calculate the checksum by sw.
 */

static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb,
      struct sk_buff_head *list)
{
 if (skb_shinfo(skb)->gso_size) {
  netdev_features_t features = tp->netdev->features;
  struct sk_buff *segs, *seg, *next;
  struct sk_buff_head seg_list;

  features &= ~(NETIF_F_SG | NETIF_F_IPV6_CSUM | NETIF_F_TSO6);
  segs = skb_gso_segment(skb, features);
  if (IS_ERR(segs) || !segs)
   goto drop;

  __skb_queue_head_init(&seg_list);

  skb_list_walk_safe(segs, seg, next) {
   skb_mark_not_on_list(seg);
   __skb_queue_tail(&seg_list, seg);
  }

  skb_queue_splice(&seg_list, list);
  dev_kfree_skb(skb);
 } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
  if (skb_checksum_help(skb) < 0)
   goto drop;

  __skb_queue_head(list, skb);
 } else {
  struct net_device_stats *stats;

drop:
  stats = &tp->netdev->stats;
  stats->tx_dropped++;
  dev_kfree_skb(skb);
 }
}

static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb)
{
 if (skb_vlan_tag_present(skb)) {
  u32 opts2;

  opts2 = TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb));
  desc->opts2 |= cpu_to_le32(opts2);
 }
}

static inline void rtl_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb)
{
 u32 opts2 = le32_to_cpu(desc->opts2);

 if (opts2 & RX_VLAN_TAG)
  __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
           swab16(opts2 & 0xffff));
}

static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc,
    struct sk_buff *skb, u32 len)
{
 u32 mss = skb_shinfo(skb)->gso_size;
 u32 opts1, opts2 = 0;
 int ret = TX_CSUM_SUCCESS;

 WARN_ON_ONCE(len > TX_LEN_MAX);

 opts1 = len | TX_FS | TX_LS;

 if (mss) {
  u32 transport_offset = (u32)skb_transport_offset(skb);

  if (transport_offset > GTTCPHO_MAX) {
   netif_warn(tp, tx_err, tp->netdev,
       "Invalid transport offset 0x%x for TSO\n",
       transport_offset);
   ret = TX_CSUM_TSO;
   goto unavailable;
  }

  switch (vlan_get_protocol(skb)) {
  case htons(ETH_P_IP):
   opts1 |= GTSENDV4;
   break;

  case htons(ETH_P_IPV6):
   if (skb_cow_head(skb, 0)) {
    ret = TX_CSUM_TSO;
    goto unavailable;
   }
   tcp_v6_gso_csum_prep(skb);
   opts1 |= GTSENDV6;
   break;

  default:
   WARN_ON_ONCE(1);
   break;
  }

  opts1 |= transport_offset << GTTCPHO_SHIFT;
  opts2 |= min(mss, MSS_MAX) << MSS_SHIFT;
 } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
  u32 transport_offset = (u32)skb_transport_offset(skb);
  u8 ip_protocol;

  if (transport_offset > TCPHO_MAX) {
   netif_warn(tp, tx_err, tp->netdev,
       "Invalid transport offset 0x%x\n",
       transport_offset);
   ret = TX_CSUM_NONE;
   goto unavailable;
  }

  switch (vlan_get_protocol(skb)) {
  case htons(ETH_P_IP):
   opts2 |= IPV4_CS;
   ip_protocol = ip_hdr(skb)->protocol;
   break;

  case htons(ETH_P_IPV6):
   opts2 |= IPV6_CS;
   ip_protocol = ipv6_hdr(skb)->nexthdr;
   break;

  default:
   ip_protocol = IPPROTO_RAW;
   break;
  }

  if (ip_protocol == IPPROTO_TCP)
   opts2 |= TCP_CS;
  else if (ip_protocol == IPPROTO_UDP)
   opts2 |= UDP_CS;
  else
   WARN_ON_ONCE(1);

  opts2 |= transport_offset << TCPHO_SHIFT;
 }

 desc->opts2 = cpu_to_le32(opts2);
 desc->opts1 = cpu_to_le32(opts1);

unavailable:
 return ret;
}

static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
{
 struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue;
 int remain, ret;
 u8 *tx_data;

 __skb_queue_head_init(&skb_head);
 spin_lock(&tx_queue->lock);
 skb_queue_splice_init(tx_queue, &skb_head);
 spin_unlock(&tx_queue->lock);

 tx_data = agg->head;
 agg->skb_num = 0;
 agg->skb_len = 0;
 remain = agg_buf_sz;

 while (remain >= ETH_ZLEN + sizeof(struct tx_desc)) {
  struct tx_desc *tx_desc;
  struct sk_buff *skb;
  unsigned int len;

  skb = __skb_dequeue(&skb_head);
  if (!skb)
   break;

  len = skb->len + sizeof(*tx_desc);

  if (len > remain) {
   __skb_queue_head(&skb_head, skb);
   break;
  }

  tx_data = tx_agg_align(tx_data);
  tx_desc = (struct tx_desc *)tx_data;

  if (r8152_tx_csum(tp, tx_desc, skb, skb->len)) {
   r8152_csum_workaround(tp, skb, &skb_head);
   continue;
  }

  rtl_tx_vlan_tag(tx_desc, skb);

  tx_data += sizeof(*tx_desc);

  len = skb->len;
  if (skb_copy_bits(skb, 0, tx_data, len) < 0) {
   struct net_device_stats *stats = &tp->netdev->stats;

   stats->tx_dropped++;
   dev_kfree_skb_any(skb);
   tx_data -= sizeof(*tx_desc);
   continue;
  }

  tx_data += len;
  agg->skb_len += len;
  agg->skb_num += skb_shinfo(skb)->gso_segs ?: 1;

  dev_kfree_skb_any(skb);

  remain = agg_buf_sz - (int)(tx_agg_align(tx_data) - agg->head);

  if (tp->dell_tb_rx_agg_bug)
   break;
 }

 if (!skb_queue_empty(&skb_head)) {
  spin_lock(&tx_queue->lock);
  skb_queue_splice(&skb_head, tx_queue);
  spin_unlock(&tx_queue->lock);
 }

 netif_tx_lock(tp->netdev);

 if (netif_queue_stopped(tp->netdev) &&
     skb_queue_len(&tp->tx_queue) < tp->tx_qlen)
  netif_wake_queue(tp->netdev);

 netif_tx_unlock(tp->netdev);

 ret = usb_autopm_get_interface_async(tp->intf);
 if (ret < 0)
  goto out_tx_fill;

 usb_fill_bulk_urb(agg->urb, tp->udev, tp->pipe_out,
     agg->head, (int)(tx_data - (u8 *)agg->head),
     (usb_complete_t)write_bulk_callback, agg);

 ret = usb_submit_urb(agg->urb, GFP_ATOMIC);
 if (ret < 0)
  usb_autopm_put_interface_async(tp->intf);

out_tx_fill:
 return ret;
}

static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc)
{
 u8 checksum = CHECKSUM_NONE;
 u32 opts2, opts3;

 if (!(tp->netdev->features & NETIF_F_RXCSUM))
  goto return_result;

 opts2 = le32_to_cpu(rx_desc->opts2);
 opts3 = le32_to_cpu(rx_desc->opts3);

 if (opts2 & RD_IPV4_CS) {
  if (opts3 & IPF)
   checksum = CHECKSUM_NONE;
  else if ((opts2 & RD_UDP_CS) && !(opts3 & UDPF))
   checksum = CHECKSUM_UNNECESSARY;
  else if ((opts2 & RD_TCP_CS) && !(opts3 & TCPF))
   checksum = CHECKSUM_UNNECESSARY;
 } else if (opts2 & RD_IPV6_CS) {
  if ((opts2 & RD_UDP_CS) && !(opts3 & UDPF))
   checksum = CHECKSUM_UNNECESSARY;
  else if ((opts2 & RD_TCP_CS) && !(opts3 & TCPF))
   checksum = CHECKSUM_UNNECESSARY;
 }

return_result:
 return checksum;
}

static inline bool rx_count_exceed(struct r8152 *tp)
{
 return atomic_read(&tp->rx_count) > RTL8152_MAX_RX;
}

static inline int agg_offset(struct rx_agg *agg, void *addr)
{
 return (int)(addr - agg->buffer);
}

static struct rx_agg *rtl_get_free_rx(struct r8152 *tp, gfp_t mflags)
{
 struct rx_agg *agg, *agg_next, *agg_free = NULL;
 unsigned long flags;

 spin_lock_irqsave(&tp->rx_lock, flags);

 list_for_each_entry_safe(agg, agg_next, &tp->rx_used, list) {
  if (page_count(agg->page) == 1) {
   if (!agg_free) {
    list_del_init(&agg->list);
    agg_free = agg;
    continue;
   }
   if (rx_count_exceed(tp)) {
    list_del_init(&agg->list);
    free_rx_agg(tp, agg);
   }
   break;
  }
 }

 spin_unlock_irqrestore(&tp->rx_lock, flags);

 if (!agg_free && atomic_read(&tp->rx_count) < tp->rx_pending)
  agg_free = alloc_rx_agg(tp, mflags);

 return agg_free;
}

static int rx_bottom(struct r8152 *tp, int budget)
{
 unsigned long flags;
 struct list_head *cursor, *next, rx_queue;
 int ret = 0, work_done = 0;
 struct napi_struct *napi = &tp->napi;

 if (!skb_queue_empty(&tp->rx_queue)) {
  while (work_done < budget) {
   struct sk_buff *skb = __skb_dequeue(&tp->rx_queue);
   struct net_device *netdev = tp->netdev;
   struct net_device_stats *stats = &netdev->stats;
   unsigned int pkt_len;

   if (!skb)
    break;

   pkt_len = skb->len;
   napi_gro_receive(napi, skb);
   work_done++;
   stats->rx_packets++;
   stats->rx_bytes += pkt_len;
  }
 }

 if (list_empty(&tp->rx_done) || work_done >= budget)
  goto out1;

 clear_bit(RX_EPROTO, &tp->flags);
 INIT_LIST_HEAD(&rx_queue);
 spin_lock_irqsave(&tp->rx_lock, flags);
 list_splice_init(&tp->rx_done, &rx_queue);
 spin_unlock_irqrestore(&tp->rx_lock, flags);

 list_for_each_safe(cursor, next, &rx_queue) {
  struct rx_desc *rx_desc;
  struct rx_agg *agg, *agg_free;
  int len_used = 0;
  struct urb *urb;
  u8 *rx_data;

  /* A bulk transfer of USB may contain may packets, so the
 * total packets may more than the budget. Deal with all
 * packets in current bulk transfer, and stop to handle the
 * next bulk transfer until next schedule, if budget is
 * exhausted.
 */

  if (work_done >= budget)
   break;

  list_del_init(cursor);

  agg = list_entry(cursor, struct rx_agg, list);
  urb = agg->urb;
  if (urb->status != 0 || urb->actual_length < ETH_ZLEN)
   goto submit;

  agg_free = rtl_get_free_rx(tp, GFP_ATOMIC);

  rx_desc = agg->buffer;
  rx_data = agg->buffer;
  len_used += sizeof(struct rx_desc);

  while (urb->actual_length > len_used) {
   struct net_device *netdev = tp->netdev;
   struct net_device_stats *stats = &netdev->stats;
   unsigned int pkt_len, rx_frag_head_sz, len;
   struct sk_buff *skb;
   bool use_frags;

   WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);

   pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
   if (pkt_len < ETH_ZLEN)
    break;

   len_used += pkt_len;
   if (urb->actual_length < len_used)
    break;

   pkt_len -= ETH_FCS_LEN;
   len = pkt_len;
   rx_data += sizeof(struct rx_desc);

   if (!agg_free || tp->rx_copybreak > len)
    use_frags = false;
   else
    use_frags = true;

   if (use_frags) {
    /* If the budget is exhausted, the packet
 * would be queued in the driver. That is,
 * napi_gro_frags() wouldn't be called, so
 * we couldn't use napi_get_frags().
 */

    if (work_done >= budget) {
     rx_frag_head_sz = tp->rx_copybreak;
     skb = napi_alloc_skb(napi,
            rx_frag_head_sz);
    } else {
     rx_frag_head_sz = 0;
     skb = napi_get_frags(napi);
    }
   } else {
    rx_frag_head_sz = 0;
    skb = napi_alloc_skb(napi, len);
   }

   if (!skb) {
    stats->rx_dropped++;
    goto find_next_rx;
   }

   skb->ip_summed = r8152_rx_csum(tp, rx_desc);
   rtl_rx_vlan_tag(rx_desc, skb);

   if (use_frags) {
    if (rx_frag_head_sz) {
     memcpy(skb->data, rx_data,
            rx_frag_head_sz);
     skb_put(skb, rx_frag_head_sz);
     len -= rx_frag_head_sz;
     rx_data += rx_frag_head_sz;
     skb->protocol = eth_type_trans(skb,
               netdev);
    }

    skb_add_rx_frag(skb, 0, agg->page,
      agg_offset(agg, rx_data),
      len, SKB_DATA_ALIGN(len));
    get_page(agg->page);
   } else {
    memcpy(skb->data, rx_data, len);
    skb_put(skb, len);
    skb->protocol = eth_type_trans(skb, netdev);
   }

   if (work_done < budget) {
    if (use_frags)
     napi_gro_frags(napi);
    else
     napi_gro_receive(napi, skb);

    work_done++;
    stats->rx_packets++;
    stats->rx_bytes += pkt_len;
   } else {
    __skb_queue_tail(&tp->rx_queue, skb);
   }

find_next_rx:
   rx_data = rx_agg_align(rx_data + len + ETH_FCS_LEN);
   rx_desc = (struct rx_desc *)rx_data;
   len_used = agg_offset(agg, rx_data);
   len_used += sizeof(struct rx_desc);
  }

  WARN_ON(!agg_free && page_count(agg->page) > 1);

  if (agg_free) {
   spin_lock_irqsave(&tp->rx_lock, flags);
   if (page_count(agg->page) == 1) {
    list_add(&agg_free->list, &tp->rx_used);
   } else {
    list_add_tail(&agg->list, &tp->rx_used);
    agg = agg_free;
    urb = agg->urb;
   }
   spin_unlock_irqrestore(&tp->rx_lock, flags);
  }

submit:
  if (!ret) {
   ret = r8152_submit_rx(tp, agg, GFP_ATOMIC);
  } else {
   urb->actual_length = 0;
   list_add_tail(&agg->list, next);
  }
 }

 /* Splice the remained list back to rx_done for next schedule */
 if (!list_empty(&rx_queue)) {
  spin_lock_irqsave(&tp->rx_lock, flags);
  list_splice(&rx_queue, &tp->rx_done);
  spin_unlock_irqrestore(&tp->rx_lock, flags);
 }

out1:
 return work_done;
}

static void tx_bottom(struct r8152 *tp)
{
 int res;

 do {
  struct net_device *netdev = tp->netdev;
  struct tx_agg *agg;

  if (skb_queue_empty(&tp->tx_queue))
   break;

  agg = r8152_get_tx_agg(tp);
  if (!agg)
   break;

  res = r8152_tx_agg_fill(tp, agg);
  if (!res)
   continue;

  if (res == -ENODEV) {
   rtl_set_unplug(tp);
   netif_device_detach(netdev);
  } else {
   struct net_device_stats *stats = &netdev->stats;
   unsigned long flags;

   netif_warn(tp, tx_err, netdev,
       "failed tx_urb %d\n", res);
   stats->tx_dropped += agg->skb_num;

   spin_lock_irqsave(&tp->tx_lock, flags);
   list_add_tail(&agg->list, &tp->tx_free);
   spin_unlock_irqrestore(&tp->tx_lock, flags);
  }
 } while (res == 0);
}

static void bottom_half(struct tasklet_struct *t)
{
 struct r8152 *tp = from_tasklet(tp, t, tx_tl);

 if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
  return;

 if (!test_bit(WORK_ENABLE, &tp->flags))
  return;

 /* When link down, the driver would cancel all bulks. */
 /* This avoid the re-submitting bulk */
 if (!netif_carrier_ok(tp->netdev))
  return;

 clear_bit(SCHEDULE_TASKLET, &tp->flags);

 tx_bottom(tp);
}

static int r8152_poll(struct napi_struct *napi, int budget)
{
 struct r8152 *tp = container_of(napi, struct r8152, napi);
 int work_done;

 if (!budget)
  return 0;

 work_done = rx_bottom(tp, budget);

 if (work_done < budget) {
  if (!napi_complete_done(napi, work_done))
   goto out;
  if (!list_empty(&tp->rx_done))
   napi_schedule(napi);
 }

out:
 return work_done;
}

static
int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags)
{
 int ret;

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=90 G=91

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.