Linux USB
 help / color / mirror / Atom feed
* Re: [PATCH v2] dma-debug: suppress cacheline overlap warning when arch has no DMA alignment requirement
From: Marek Szyprowski @ 2026-04-02 11:26 UTC (permalink / raw)
  To: Robin Murphy, Andy Shevchenko
  Cc: Mikhail Gavrilov, iommu, linux-kernel, linux-usb, linux-mm, harry,
	vbabka, akpm, stern, linux, hch, Jeff.kirsher, catalin.marinas
In-Reply-To: <75f65aa7-4f89-4ef8-8941-51b1d54d1ad3@arm.com>

On 01.04.2026 17:26, Robin Murphy wrote:
> On 2026-04-01 2:25 pm, Andy Shevchenko wrote:
>> On Wed, Apr 1, 2026 at 3:11 PM Robin Murphy <robin.murphy@arm.com> wrote:
>>> On 2026-03-30 8:44 am, Marek Szyprowski wrote:
>>>> On 27.03.2026 13:41, Mikhail Gavrilov wrote:
>>
>> ...
>>
>>> TBH I'd be inclined to have CONFIG_DMA_DEBUG raise ARCH_DMA_MINALIGN as
>>> appropriate such that genuine false-positives can't happen, rather than
>>> effectively defeat the whole check,
>>
>> I dunno if you read v1 thread, where I proposed to unroll the check
>> and use pr_debug_once() for the cases which we expect not to panic,
>> but would be good to have a track of.
>
> I had not seen v1, as I took the last 3 days off and hadn't got that far up my inbox yet - I guess it's at least reassuring to have reached similar conclusions independently :)
>
> The fundamental issue here is that dma-debug doesn't realistically have a way to know whether the thing being mapped is intentionally a whole dedicated kmalloc allocation - where we can trust SLUB (and DMA_BOUNCE_UNALIGNED_KMALLOC if appropriate) to do the right thing across different systems - or just something which might happen to line up by coincidence on someone's development machine, but for portability they definitely do still need to take explicit care about (e.g. struct devres::data).


Right, the debug code cannot distinguish them. I'm still convinced that increasing
ARCH_DMA_MINALIGN when DEBUG is enabled is not a good idea. I also agree that the
current exceptions for DMA_BOUNCE_UNALIGNED_KMALLOC and dma_get_cache_alignment() <
L1_CACHE_BYTES are wider than really needed. What we can do about it?

For the (dma_get_cache_alignment() < L1_CACHE_BYTES) case I came up with the idea
of reducing the check only to the dma_get_cache_alignment()-size overlapping:


diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c
index 86f87e43438c..9bf4b5c368f9 100644
--- a/kernel/dma/debug.c
+++ b/kernel/dma/debug.c
@@ -418,13 +418,13 @@ static void hash_bucket_del(struct dma_debug_entry *entry)
 static RADIX_TREE(dma_active_cacheline, GFP_ATOMIC | __GFP_NOWARN);
 static DEFINE_SPINLOCK(radix_lock);
 #define ACTIVE_CACHELINE_MAX_OVERLAP ((1 << RADIX_TREE_MAX_TAGS) - 1)
-#define CACHELINE_PER_PAGE_SHIFT (PAGE_SHIFT - L1_CACHE_SHIFT)
-#define CACHELINES_PER_PAGE (1 << CACHELINE_PER_PAGE_SHIFT)

 static phys_addr_t to_cacheline_number(struct dma_debug_entry *entry)
 {
-       return ((entry->paddr >> PAGE_SHIFT) << CACHELINE_PER_PAGE_SHIFT) +
-               (offset_in_page(entry->paddr) >> L1_CACHE_SHIFT);
+       if (dma_get_cache_alignment() >= L1_CACHE_BYTES)
+               return entry->paddr >> L1_CACHE_SHIFT;
+       else
+               return entry->paddr >> ilog2(dma_get_cache_alignment());
 }

 static int active_cacheline_read_overlap(phys_addr_t cln)


For the remaining cases (DMA_BOUNCE_UNALIGNED_KMALLOC mainly), based on the Catalin's
suggestion, I came up with the following idea:

diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c
index 0677918f06a8..f9b6a989ac15 100644
--- a/kernel/dma/debug.c
+++ b/kernel/dma/debug.c
@@ -18,6 +18,7 @@
 #include <linux/uaccess.h>
 #include <linux/export.h>
 #include <linux/device.h>
+#include <linux/dma-direct.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/ctype.h>
@@ -590,6 +591,14 @@ static int dump_show(struct seq_file *seq, void *v)
 }
 DEFINE_SHOW_ATTRIBUTE(dump);

+static inline void dma_debug_fixup_bounced(struct dma_debug_entry *entry)
+{
+       if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) &&
+           dma_kmalloc_needs_bounce(entry->dev, entry->size, entry->direction) &&
+           is_swiotlb_active(entry->dev))
+               entry->paddr = dma_to_phys(entry->dev, entry->dev_addr);
+}
+
 /*
  * Wrapper function for adding an entry to the hash.
  * This function takes care of locking itself.
@@ -601,6 +610,8 @@ static void add_dma_entry(struct dma_debug_entry *entry, unsigned long attrs)
        unsigned long flags;
        int rc;

+       dma_debug_fixup_bounced(entry);
+
        entry->is_cache_clean = attrs & (DMA_ATTR_DEBUGGING_IGNORE_CACHELINES |
                                         DMA_ATTR_REQUIRE_COHERENT);

@@ -614,9 +625,7 @@ static void add_dma_entry(struct dma_debug_entry *entry, unsigned long attrs)
                global_disable = true;
        } else if (rc == -EEXIST &&
                   !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
-                  !(entry->is_cache_clean && overlap_cache_clean) &&
-                  !(IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) &&
-                    is_swiotlb_active(entry->dev))) {
+                  !(entry->is_cache_clean && overlap_cache_clean)) {
                err_printk(entry->dev, entry,
                        "cacheline tracking EEXIST, overlapping mappings aren't supported\n");
        }
@@ -981,6 +990,8 @@ static void check_unmap(struct dma_debug_entry *ref)
        struct hash_bucket *bucket;
        unsigned long flags;

+       dma_debug_fixup_bounced(ref);
+
        bucket = get_hash_bucket(ref, &flags);
        entry = bucket_find_exact(bucket, ref);


What do You think about it? Both at least allows to detect errors like multiple,
incompatible mappings of the same memory.


Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland


^ permalink raw reply related

* Re: [PATCH v1 2/2] mfd: Add Host Interface (HIF) support for Nuvoton NCT6694
From: Marc Kleine-Budde @ 2026-04-02 11:05 UTC (permalink / raw)
  To: a0282524688
  Cc: tmyu0, linusw, brgl, linux, andi.shyti, lee, mailhol,
	alexandre.belloni, wim, linux-kernel, linux-gpio, linux-i2c,
	linux-can, netdev, linux-watchdog, linux-hwmon, linux-rtc,
	linux-usb
In-Reply-To: <20260402051442.1426672-3-a0282524688@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 11365 bytes --]

On 02.04.2026 13:14:42, a0282524688@gmail.com wrote:
> From: Ming Yu <a0282524688@gmail.com>
>
> The Nuvoton NCT6694 also provides a Host Interface (HIF) via eSPI
> to the host to access its features.
>
> Sub-devices can use the common functions nct6694_read_msg() and
> nct6694_write_msg() to issue a command. They can also request
> interrupts that will be called when the HIF device triggers a
> shared memory interrupt.
>
> To support multiple transports, the driver configuration is
> updated to allow selecting between the USB and HIF interfaces.
>
> Signed-off-by: Ming Yu <a0282524688@gmail.com>
> ---
>  MAINTAINERS                         |   1 +
>  drivers/gpio/gpio-nct6694.c         |   7 -
>  drivers/hwmon/nct6694-hwmon.c       |  21 -
>  drivers/i2c/busses/i2c-nct6694.c    |   7 -
>  drivers/mfd/Kconfig                 |  47 +-
>  drivers/mfd/Makefile                |   3 +-
>  drivers/mfd/nct6694-hif.c           | 649 ++++++++++++++++++++++++++++
>  drivers/mfd/nct6694.c               |  97 +++--
>  drivers/net/can/usb/nct6694_canfd.c |   6 -
>  drivers/rtc/rtc-nct6694.c           |   7 -
>  drivers/watchdog/nct6694_wdt.c      |   7 -
>  include/linux/mfd/nct6694.h         |  51 ++-
>  12 files changed, 787 insertions(+), 116 deletions(-)
>  create mode 100644 drivers/mfd/nct6694-hif.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c3fe46d7c4bc..7b6241faa6df 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -18899,6 +18899,7 @@ S:	Supported
>  F:	drivers/gpio/gpio-nct6694.c
>  F:	drivers/hwmon/nct6694-hwmon.c
>  F:	drivers/i2c/busses/i2c-nct6694.c
> +F:	drivers/mfd/nct6694-hif.c
>  F:	drivers/mfd/nct6694.c
>  F:	drivers/net/can/usb/nct6694_canfd.c
>  F:	drivers/rtc/rtc-nct6694.c
> diff --git a/drivers/gpio/gpio-nct6694.c b/drivers/gpio/gpio-nct6694.c
> index 3703a61209e6..a279510ece89 100644
> --- a/drivers/gpio/gpio-nct6694.c
> +++ b/drivers/gpio/gpio-nct6694.c
> @@ -12,13 +12,6 @@
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
>
> -/*
> - * USB command module type for NCT6694 GPIO controller.
> - * This defines the module type used for communication with the NCT6694
> - * GPIO controller over the USB interface.
> - */
> -#define NCT6694_GPIO_MOD	0xFF
> -
>  #define NCT6694_GPIO_VER	0x90
>  #define NCT6694_GPIO_VALID	0x110
>  #define NCT6694_GPI_DATA	0x120
> diff --git a/drivers/hwmon/nct6694-hwmon.c b/drivers/hwmon/nct6694-hwmon.c
> index 6dcf22ca5018..581451875f2c 100644
> --- a/drivers/hwmon/nct6694-hwmon.c
> +++ b/drivers/hwmon/nct6694-hwmon.c
> @@ -15,13 +15,6 @@
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
>
> -/*
> - * USB command module type for NCT6694 report channel
> - * This defines the module type used for communication with the NCT6694
> - * report channel over the USB interface.
> - */
> -#define NCT6694_RPT_MOD			0xFF
> -
>  /* Report channel */
>  /*
>   * The report channel is used to report the status of the hardware monitor
> @@ -38,13 +31,6 @@
>  #define NCT6694_TIN_STS(x)		(0x6A + (x))
>  #define NCT6694_FIN_STS(x)		(0x6E + (x))
>
> -/*
> - * USB command module type for NCT6694 HWMON controller.
> - * This defines the module type used for communication with the NCT6694
> - * HWMON controller over the USB interface.
> - */
> -#define NCT6694_HWMON_MOD		0x00
> -
>  /* Command 00h - Hardware Monitor Control */
>  #define NCT6694_HWMON_CONTROL		0x00
>  #define NCT6694_HWMON_CONTROL_SEL	0x00
> @@ -53,13 +39,6 @@
>  #define NCT6694_HWMON_ALARM		0x02
>  #define NCT6694_HWMON_ALARM_SEL		0x00
>
> -/*
> - * USB command module type for NCT6694 PWM controller.
> - * This defines the module type used for communication with the NCT6694
> - * PWM controller over the USB interface.
> - */
> -#define NCT6694_PWM_MOD			0x01
> -
>  /* PWM Command - Manual Control */
>  #define NCT6694_PWM_CONTROL		0x01
>  #define NCT6694_PWM_CONTROL_SEL		0x00
> diff --git a/drivers/i2c/busses/i2c-nct6694.c b/drivers/i2c/busses/i2c-nct6694.c
> index 7d8ad997f6d2..7ee209a04d16 100644
> --- a/drivers/i2c/busses/i2c-nct6694.c
> +++ b/drivers/i2c/busses/i2c-nct6694.c
> @@ -11,13 +11,6 @@
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
>
> -/*
> - * USB command module type for NCT6694 I2C controller.
> - * This defines the module type used for communication with the NCT6694
> - * I2C controller over the USB interface.
> - */
> -#define NCT6694_I2C_MOD			0x03
> -
>  /* Command 00h - I2C Deliver */
>  #define NCT6694_I2C_DELIVER		0x00
>  #define NCT6694_I2C_DELIVER_SEL		0x00
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 7192c9d1d268..8a715ec2f79f 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1164,19 +1164,46 @@ config MFD_MENF21BMC
>  	  will be called menf21bmc.
>
>  config MFD_NCT6694
> -	tristate "Nuvoton NCT6694 support"
> +	tristate
>  	select MFD_CORE
> +	help
> +	  Core MFD support for the Nuvoton NCT6694 peripheral expander.
> +	  This provides the common APIs and shared structures used by all
> +	  interfaces (USB, HIF) to access the NCT6694 hardware features
> +	  such as GPIO, I2C, CAN-FD, Watchdog, ADC, PWM, and RTC.
> +
> +	  It is selected automatically by the transport interface drivers.
> +
> +config MFD_NCT6694_HIF
> +	tristate "Nuvoton NCT6694 HIF (eSPI) interface support"
> +	depends on HAS_IOPORT && ACPI
> +	select MFD_NCT6694
> +	select REGMAP_MMIO
> +	help
> +	  This enables support for the Nuvoton NCT6694 peripheral expander
> +	  connected via the Host Interface (HIF) using eSPI transport.
> +
> +	  The transport driver uses Super-I/O mapping and shared memory to
> +	  communicate with the NCT6694 firmware. Enable this option if you
> +	  are using the NCT6694 over an eSPI interface on an ACPI platform.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called nct6694-hif.
> +
> +config MFD_NCT6694_USB
> +	tristate "Nuvoton NCT6694 USB interface support"
> +	select MFD_NCT6694
>  	depends on USB
>  	help
> -	  This enables support for the Nuvoton USB device NCT6694, which shares
> -	  peripherals.
> -	  The Nuvoton NCT6694 is a peripheral expander with 16 GPIO chips,
> -	  6 I2C controllers, 2 CANfd controllers, 2 Watchdog timers, ADC,
> -	  PWM, and RTC.
> -	  This driver provides core APIs to access the NCT6694 hardware
> -	  monitoring and control features.
> -	  Additional drivers must be enabled to utilize the specific
> -	  functionalities of the device.
> +	  This enables support for the Nuvoton NCT6694 peripheral expander
> +	  connected via the USB interface.
> +
> +	  The transport driver uses USB bulk and interrupt transfers to
> +	  communicate with the NCT6694 firmware. Enable this option if you
> +	  are using the NCT6694 via a USB connection.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called nct6694.
>
>  config MFD_OCELOT
>  	tristate "Microsemi Ocelot External Control Support"
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index e75e8045c28a..4cee9b74978c 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -124,7 +124,8 @@ obj-$(CONFIG_MFD_MC13XXX_I2C)	+= mc13xxx-i2c.o
>
>  obj-$(CONFIG_MFD_PF1550)	+= pf1550.o
>
> -obj-$(CONFIG_MFD_NCT6694)	+= nct6694.o
> +obj-$(CONFIG_MFD_NCT6694_HIF)	+= nct6694-hif.o
> +obj-$(CONFIG_MFD_NCT6694_USB)	+= nct6694.o
>
>  obj-$(CONFIG_MFD_CORE)		+= mfd-core.o
>
> diff --git a/drivers/mfd/nct6694-hif.c b/drivers/mfd/nct6694-hif.c
> new file mode 100644
> index 000000000000..a5953c951eb5
> --- /dev/null
> +++ b/drivers/mfd/nct6694-hif.c
> @@ -0,0 +1,649 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2026 Nuvoton Technology Corp.
> + *
> + * Nuvoton NCT6694 host-interface (eSPI) transport driver.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/bits.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/nct6694.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/unaligned.h>
> +
> +#define DRVNAME "nct6694-hif"
> +
> +#define NCT6694_POLL_INTERVAL_US	10
> +#define NCT6694_POLL_TIMEOUT_US		10000
> +
> +/*
> + * Super-I/O registers
> + */
> +#define SIO_REG_LDSEL		0x07	/* Logical device select */
> +#define SIO_REG_DEVID		0x20	/* Device ID (2 bytes) */
> +#define SIO_REG_LD_SHM		0x0F	/* Logical device shared memory control */
> +
> +#define SIO_REG_SHM_ENABLE	0x30	/* Enable shared memory */
> +#define SIO_REG_SHM_BASE_ADDR	0x60	/* Shared memory base address (2 bytes) */
> +#define SIO_REG_SHM_IRQ_NR	0x70	/* Shared memory interrupt number */
> +
> +#define SIO_REG_UNLOCK_KEY	0x87	/* Key to enable Super-I/O */
> +#define SIO_REG_LOCK_KEY	0xAA	/* Key to disable Super-I/O */
> +
> +#define SIO_NCT6694B_ID		0xD029
> +#define SIO_NCT6694D_ID		0x5832
> +
> +/*
> + * Super-I/O Shared Memory Logical Device registers
> + */
> +#define NCT6694_SHM_COFS_STS			0x2E
> +#define NCT6694_SHM_COFS_STS_COFS4W		BIT(7)
> +
> +#define NCT6694_SHM_COFS_CTL2			0x3B
> +#define NCT6694_SHM_COFS_CTL2_COFS4W_IE		BIT(3)
> +
> +#define NCT6694_SHM_INTR_STATUS			0x9C	/* Interrupt status register (4 bytes) */
> +
> +enum nct6694_chips {
> +	NCT6694B = 0,
> +	NCT6694D,
> +};
> +
> +enum nct6694_module_id {
> +	NCT6694_GPIO0 = 0,
> +	NCT6694_GPIO1,
> +	NCT6694_GPIO2,
> +	NCT6694_GPIO3,
> +	NCT6694_GPIO4,
> +	NCT6694_GPIO5,
> +	NCT6694_GPIO6,
> +	NCT6694_GPIO7,
> +	NCT6694_GPIO8,
> +	NCT6694_GPIO9,
> +	NCT6694_GPIOA,
> +	NCT6694_GPIOB,
> +	NCT6694_GPIOC,
> +	NCT6694_GPIOD,
> +	NCT6694_GPIOE,
> +	NCT6694_GPIOF,
> +	NCT6694_I2C0,
> +	NCT6694_I2C1,
> +	NCT6694_I2C2,
> +	NCT6694_I2C3,
> +	NCT6694_I2C4,
> +	NCT6694_I2C5,
> +	NCT6694_CAN0,
> +	NCT6694_CAN1,
> +};
> +
> +struct __packed nct6694_msg {
> +	struct nct6694_cmd_header cmd_header;
> +	struct nct6694_response_header response_header;
> +	unsigned char *data;
> +};
> +
> +struct nct6694_sio_data {
> +	enum nct6694_chips chip;
> +	int sioreg;	/* Super-I/O index port */
> +
> +	/* Super-I/O access functions */
> +	int (*sio_enter)(struct nct6694_sio_data *sio_data);
> +	void (*sio_exit)(struct nct6694_sio_data *sio_data);
> +	void (*sio_select)(struct nct6694_sio_data *sio_data, int ld);
> +	int (*sio_inb)(struct nct6694_sio_data *sio_data, int reg);
> +	int (*sio_inw)(struct nct6694_sio_data *sio_data, int reg);
> +	void (*sio_outb)(struct nct6694_sio_data *sio_data, int reg, int val);

The signatures of the function look a bit strange. I expect functions
reading/writing bytes use u8 not int, register offsets should probably
be an unsigned int.

Why do you have pointers to the access functions? Why not use them
directly?

Marc

--
Pengutronix e.K.                 | Marc Kleine-Budde          |
Embedded Linux                   | https://www.pengutronix.de |
Vertretung Nürnberg              | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-9   |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH] usb: chipidea: udc: reject non-control requests while controller is suspended
From: Xu Yang @ 2026-04-02 10:16 UTC (permalink / raw)
  To: Andreea.Popescu@aumovio.com
  Cc: Peter Chen, Greg Kroah-Hartman, linux-usb@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <FRYP281MB2618B5E8484BD399A6B3085AEA50A@FRYP281MB2618.DEUP281.PROD.OUTLOOK.COM>

On Wed, Apr 01, 2026 at 10:47:54AM +0000, Andreea.Popescu@aumovio.com wrote:
> On Tue, Mar 31, 2026 at 12:21:45PM +0000, Andreea.Popescu@aumovio.com wrote:
> >> When Linux runtime PM autosuspends a ChipIdea UDC that is still
> >> enumerated by the host, the driver gates the PHY clocks and marks
> >> the controller as suspended (ci->in_lpm = 1) but deliberately leaves
> >> gadget.speed unchanged so upper-layer gadget drivers do not see a
> >> spurious disconnect.
> >
> >It's strange that chipidea UDC will runtime suspend even it's already
> >enumerated by the host. AFAIK, the udc driver will call pm_runtime_get_sync()
> >in ci_hdrc_gadget_connect(is_active = true), so it will be in runtime active
> >state all the time unless a explicit pm_runtime_put/_autosuspend() is called
> >in somewhere.
> >
> >Would you share more details how device controller go to runtime suspended?
> >Thanks,
> >Xu Yang
> Thank you very much for taking the time and pointing this. It made me realize a very important distinction. I am using an I.MX board, due to this I will split my answer, so you can decide if it's still worth what I am proposing or you can just reject it. Either way, I am most grateful.

Thank you for the information.

> Still applicable to 6.19 mainline:
> ep_queue returning 0 for USB_SPEED_UNKNOWN: I believe there might be the following window: _gadget_stop_activity() sets gadget.speed = USB_SPEED_UNKNOWN, but ep_queue is called before that completes from a concurrent context. The return 0 is misleading and should be -ESHUTDOWN.

I think this will hardly happen or the window is very small. Because when
the driver sees the speed is USB_SPEED_UNKNOWN, it has most likely already
seen that ep->enabled is false. _gadget_stop_activity() will call usb_ep_disable(),
so usb_ep_queue() will return -ESHUTDOWN early.

> I.MX specific: On i.MX SoCs the chipidea controller sits inside a power domain managed by imx-blk-ctrl or the GPC. When that parent domain is shut down by the platform PM framework, pm_runtime_force_suspend() is called on the chipidea device, bypassing usage_count entirely and invoking ci_runtime_suspend → ci_controller_suspend → ci->in_lpm = true. This happens while VBUS is still present and the gadget is enumerated. This is the actual path I observed and it is platform-specific, not a general chipidea mainline issue. Due to this, please disregard the proposed change with _ep_queue guard on ci->in_lpm

Then I guess the PM framework is working abnormally. How could a parent
domain shut down itself when its active subdomain and users are using it?

Thanks,
Xu Yang


^ permalink raw reply

* Re: [PATCH v2] USB: serial: option: add Telit Cinterion FN990A MBIM composition
From: Fabio Porcedda @ 2026-04-02 10:04 UTC (permalink / raw)
  To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, Daniele Palmas, stable
In-Reply-To: <ac46YeSJeYVvm0Hn@hovoldconsulting.com>

On Thu, Apr 2, 2026 at 11:44 AM Johan Hovold <johan@kernel.org> wrote:
>
> On Thu, Apr 02, 2026 at 10:37:22AM +0200, Fabio Porcedda wrote:
> > Add the following Telit Cinterion FN990A MBIM composition:
> >
> > 0x1074: MBIM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (diag) +
> >         DPL (Data Packet Logging) + adb
> >
> > T:  Bus=01 Lev=01 Prnt=04 Port=06 Cnt=01 Dev#=  3 Spd=480  MxCh= 0
> > D:  Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
> > P:  Vendor=1bc7 ProdID=1074 Rev=05.04
> > S:  Manufacturer=Telit Wireless Solutions
> > S:  Product=FN990
> > S:  SerialNumber=70628d0c
> > C:  #Ifs= 7 Cfg#= 1 Atr=e0 MxPwr=500mA
> > I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim
> > E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=32ms
> > I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim
> > E:  Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > E:  Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option
> > E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > E:  Ad=83(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
> > I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
> > E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > E:  Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > E:  Ad=85(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
> > I:  If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
> > E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > E:  Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > E:  Ad=87(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
> > I:  If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
> > E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > E:  Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> > I:  If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none)
> > E:  Ad=8f(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> >
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Fabio Porcedda <fabio.porcedda@gmail.com>
>
> > @@ -1383,6 +1383,8 @@ static const struct usb_device_id option_ids[] = {
> >         .driver_info = NCTRL(2) | RSVD(3) },
> >       { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff),    /* Telit FN990A (ECM) */
> >         .driver_info = NCTRL(0) | RSVD(1) },
> > +     { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1074, 0xff),    /* Telit FN990A (MBIM) */
> > +       .driver_info = NCTRL(5) | RSVD(6) | RSVD(7) },
>
> There is no adb interface in the usb-devices output in the commit
> message. Do you still need to reserve interface 7?

The output of usb-devices was not complete, I've sent a new version
with the full output:
https://lore.kernel.org/linux-usb/20260402095727.108281-1-fabio.porcedda@gmail.com

> >       { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff),    /* Telit FN990A (PCIe) */
> >         .driver_info = RSVD(0) },
> >       { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff),    /* Telit FN990A (rmnet + audio) */
>
> Johan

Thanks
-- 
Fabio Porcedda

^ permalink raw reply

* [PATCH v3] USB: serial: option: add Telit Cinterion FN990A MBIM composition
From: Fabio Porcedda @ 2026-04-02  9:57 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Greg Kroah-Hartman, linux-usb, Daniele Palmas, Fabio Porcedda,
	stable

Add the following Telit Cinterion FN990A MBIM composition:

0x1074: MBIM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (diag) +
        DPL (Data Packet Logging) + adb

T:  Bus=01 Lev=01 Prnt=04 Port=06 Cnt=01 Dev#=  7 Spd=480  MxCh= 0
D:  Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1bc7 ProdID=1074 Rev=05.04
S:  Manufacturer=Telit Wireless Solutions
S:  Product=FN990
S:  SerialNumber=70628d0c
C:  #Ifs= 8 Cfg#= 1 Atr=e0 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim
E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=32ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim
E:  Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option
E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=83(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=85(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=87(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none)
E:  Ad=8f(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 7 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none)
E:  Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms

Cc: stable@vger.kernel.org
Signed-off-by: Fabio Porcedda <fabio.porcedda@gmail.com>
---
v3:
- Added the full output of usb-devices
- Link to v2: https://lore.kernel.org/linux-usb/20260402083722.100973-1-fabio.porcedda@gmail.com

v2:
- Added "Cc: stable@vger.kernel.org"
- Link to v1: https://lore.kernel.org/linux-usb/20260402082747.98441-1-fabio.porcedda@gmail.com

 drivers/usb/serial/option.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 313612114db9..c71461893d20 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1383,6 +1383,8 @@ static const struct usb_device_id option_ids[] = {
 	  .driver_info = NCTRL(2) | RSVD(3) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff),	/* Telit FN990A (ECM) */
 	  .driver_info = NCTRL(0) | RSVD(1) },
+	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1074, 0xff),	/* Telit FN990A (MBIM) */
+	  .driver_info = NCTRL(5) | RSVD(6) | RSVD(7) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff),	/* Telit FN990A (PCIe) */
 	  .driver_info = RSVD(0) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff),	/* Telit FN990A (rmnet + audio) */
-- 
2.53.0


^ permalink raw reply related

* [GIT PULL] USB serial device ids for 7.0-rc7
From: Johan Hovold @ 2026-04-02  9:49 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-usb, linux-kernel

The following changes since commit f338e77383789c0cae23ca3d48adcc5e9e137e3c:

  Linux 7.0-rc4 (2026-03-15 13:52:05 -0700)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial.git tags/usb-serial-7.0-rc7

for you to fetch changes up to e8d0ed37bd51da52da6225d278e330c2f18a6198:

  USB: serial: option: add MeiG Smart SRM825WN (2026-04-01 11:32:08 +0200)

----------------------------------------------------------------
USB serial device ids for 7.0-rc7

Here are some new modem and io_edgeport device ids.

All have been in linux-next with no reported issues.

----------------------------------------------------------------
Ernestas Kulik (1):
      USB: serial: option: add MeiG Smart SRM825WN

Frej Drejhammar (1):
      USB: serial: io_edgeport: add support for Blackbox IC135A

Wanquan Zhong (1):
      USB: serial: option: add support for Rolling Wireless RW135R-GL

 drivers/usb/serial/io_edgeport.c | 3 +++
 drivers/usb/serial/io_usbvend.h  | 1 +
 drivers/usb/serial/option.c      | 4 ++++
 3 files changed, 8 insertions(+)

^ permalink raw reply

* Re: [PATCH] usb: core: Fix bandwidth for devices with invalid wBytesPerInterval
From: Michal Pecio @ 2026-04-02  9:44 UTC (permalink / raw)
  To: Tao Xue; +Cc: gregkh, linux-usb, linux-kernel, caiyadong, stable
In-Reply-To: <20260402021400.28853-1-xuetao09@huawei.com>

On Thu, 2 Apr 2026 10:14:00 +0800, Tao Xue wrote:
> As specified in Section 4.14.2 of the xHCI Specification, the xHC
> reserves bandwidth for periodic endpoints according to bInterval and
> wBytesPerInterval (Max ESIT Payload).

For SuperSpeed endpoints, yes.
This follows from USB3 spec 9.6.7.

> Some peripherals report an invalid wBytesPerInterval in their device
> descriptor, which is either 0 or smaller than the actual data length
> transmitted. This issue is observed on ASIX AX88179 series USB 3.0
> Ethernet adapters.

Damn, it really does.

      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              11
        bMaxBurst               0
        wBytesPerInterval       0

Any other examples besides AX88179?

> These errors may lead to unexpected behavior on certain USB host
> controllers, causing USB peripherals to malfunction.

Out of curiosity, Bandwidth Overrun Error or something worse?

It's an oversight that these URBs aren't rejected with EMSGSIZE in the
first place. IIRC zero-length interrupt transfers are allowed by USB
specs and a zero-payload endpoint is probably legal per xHCI, but then
submitting non-empty URBs to it is not.

> To address the issue, return max(wBytesPerInterval, max_payload) when
> calculating bandwidth reservation.
> 
> Fixes: 9238f25d5d32 ("USB: xhci: properly set endpoint context fields for periodic eps.")
> Cc: <stable@kernel.org>
> Signed-off-by: Tao Xue <xuetao09@huawei.com>
> ---
>  drivers/usb/core/usb.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
> index e9a10a33534c..8f2e05a5a015 100644
> --- a/drivers/usb/core/usb.c
> +++ b/drivers/usb/core/usb.c
> @@ -1125,6 +1125,8 @@ EXPORT_SYMBOL_GPL(usb_free_noncoherent);
>  u32 usb_endpoint_max_periodic_payload(struct usb_device *udev,
>  				      const struct usb_host_endpoint *ep)
>  {
> +	u32 max_payload;
> +
>  	if (!usb_endpoint_xfer_isoc(&ep->desc) &&
>  	    !usb_endpoint_xfer_int(&ep->desc))
>  		return 0;
> @@ -1135,7 +1137,12 @@ u32 usb_endpoint_max_periodic_payload(struct usb_device *udev,
>  			return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval);
>  		fallthrough;
>  	case USB_SPEED_SUPER:
> -		return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
> +		max_payload = usb_endpoint_maxp(&ep->desc) * (ep->ss_ep_comp.bMaxBurst + 1);
> +		if (usb_endpoint_xfer_isoc(&ep->desc))
> +			return max_t(u32, max_payload * USB_SS_MULT(ep->ss_ep_comp.bmAttributes),
> +					ep->ss_ep_comp.wBytesPerInterval);
> +		else
> +			return max_t(u32, max_payload, ep->ss_ep_comp.wBytesPerInterval);

Obviously a kludge is necessary here to make these abominable devices
work reliably with xHCI, but OTOH exceeding wBytesPerInterval violates
USB3 9.6.7 and it's unclear if all devices would be happy.

There are devices which define such odd isochronous alt settings with
apparent intent to allow fine-grained bandwidth reservation:

        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               1
        bMaxBurst               0
        wBytesPerInterval     512

        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               1
        bMaxBurst               0
        wBytesPerInterval    1024

        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               1
        bMaxBurst               1  # 2 packets per interval
        wBytesPerInterval    1536

Isochronous drivers use this function to size their URBs or select the
right altsetting for given bandwidth. UVC has obeyed wBytesPerInterval
since forever with no apparent issues and UAC has recently been patched
to work like that too with no issues so far AFAIK.

Maybe start with something specific to the known bogus hardware, i.e.
interrupt endpoint with one packet and zero payload? In such case
it's high chance that the device actually meant it to be wMaxPacket.

>  	default:
>  		if (usb_endpoint_is_hs_isoc_double(udev, ep))
>  			return le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
> -- 
> 2.17.1
> 

^ permalink raw reply

* Re: [PATCH v2] USB: serial: option: add Telit Cinterion FN990A MBIM composition
From: Johan Hovold @ 2026-04-02  9:44 UTC (permalink / raw)
  To: Fabio Porcedda; +Cc: Greg Kroah-Hartman, linux-usb, Daniele Palmas, stable
In-Reply-To: <20260402083722.100973-1-fabio.porcedda@gmail.com>

On Thu, Apr 02, 2026 at 10:37:22AM +0200, Fabio Porcedda wrote:
> Add the following Telit Cinterion FN990A MBIM composition:
> 
> 0x1074: MBIM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (diag) +
>         DPL (Data Packet Logging) + adb
> 
> T:  Bus=01 Lev=01 Prnt=04 Port=06 Cnt=01 Dev#=  3 Spd=480  MxCh= 0
> D:  Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
> P:  Vendor=1bc7 ProdID=1074 Rev=05.04
> S:  Manufacturer=Telit Wireless Solutions
> S:  Product=FN990
> S:  SerialNumber=70628d0c
> C:  #Ifs= 7 Cfg#= 1 Atr=e0 MxPwr=500mA
> I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim
> E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=32ms
> I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim
> E:  Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> E:  Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option
> E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> E:  Ad=83(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
> I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
> E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> E:  Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> E:  Ad=85(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
> I:  If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
> E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> E:  Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> E:  Ad=87(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
> I:  If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
> E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> E:  Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> I:  If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none)
> E:  Ad=8f(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
> 
> Cc: stable@vger.kernel.org
> Signed-off-by: Fabio Porcedda <fabio.porcedda@gmail.com>

> @@ -1383,6 +1383,8 @@ static const struct usb_device_id option_ids[] = {
>  	  .driver_info = NCTRL(2) | RSVD(3) },
>  	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff),	/* Telit FN990A (ECM) */
>  	  .driver_info = NCTRL(0) | RSVD(1) },
> +	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1074, 0xff),	/* Telit FN990A (MBIM) */
> +	  .driver_info = NCTRL(5) | RSVD(6) | RSVD(7) },

There is no adb interface in the usb-devices output in the commit
message. Do you still need to reserve interface 7?

>  	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff),	/* Telit FN990A (PCIe) */
>  	  .driver_info = RSVD(0) },
>  	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff),	/* Telit FN990A (rmnet + audio) */

Johan

^ permalink raw reply

* Re: [PATCH] usbip: vhci: reject RET_SUBMIT with inflated number_of_packets
From: Nathan Rebello @ 2026-04-02  9:03 UTC (permalink / raw)
  To: gregkh; +Cc: skhan, linux-usb, addcontent08, kyungtae.kim, stable,
	Nathan Rebello
In-Reply-To: <2026040220-defeat-jokester-22dc@gregkh>

On Wed, Apr 02, 2026, Greg Kroah-Hartman wrote:
> This patch is somehow corrupted and can not be applied at all.  Nathan,
> how did you generate it?

I used git format-patch and git send-email — the patch had a
whitespace issue, sorry about that.

> Can you regenerate this against my latest usb-testing branch and resend?

Done — sent the v2 against your latest usb-testing.

Thanks,
Nathan Rebello

^ permalink raw reply

* [PATCH v2] usbip: validate number_of_packets in usbip_pack_ret_submit()
From: Nathan Rebello @ 2026-04-02  8:52 UTC (permalink / raw)
  To: linux-usb
  Cc: gregkh, addcontent08, skhan, kyungtae.kim, stable, Nathan Rebello

When a USB/IP client receives a RET_SUBMIT response,
usbip_pack_ret_submit() unconditionally overwrites
urb->number_of_packets from the network PDU. This value is
subsequently used as the loop bound in usbip_recv_iso() and
usbip_pad_iso() to iterate over urb->iso_frame_desc[], a flexible
array whose size was fixed at URB allocation time based on the
*original* number_of_packets from the CMD_SUBMIT.

A malicious USB/IP server can set number_of_packets in the response
to a value larger than what was originally submitted, causing a heap
out-of-bounds write when usbip_recv_iso() writes to
urb->iso_frame_desc[i] beyond the allocated region.

KASAN confirmed this with kernel 7.0.0-rc5:

  BUG: KASAN: slab-out-of-bounds in usbip_recv_iso+0x46a/0x640
  Write of size 4 at addr ffff888106351d40 by task vhci_rx/69

  The buggy address is located 0 bytes to the right of
   allocated 320-byte region [ffff888106351c00, ffff888106351d40)

The server side (stub_rx.c) and gadget side (vudc_rx.c) already
validate number_of_packets in the CMD_SUBMIT path since commits
c6688ef9f297 ("usbip: fix stub_rx: harden CMD_SUBMIT path to handle
malicious input") and b78d830f0049 ("usbip: fix vudc_rx: harden
CMD_SUBMIT path to handle malicious input"). The server side validates
against USBIP_MAX_ISO_PACKETS because no URB exists yet at that point.
On the client side we have the original URB, so we can use the tighter
bound: the response must not exceed the original number_of_packets.

This mirrors the existing validation of actual_length against
transfer_buffer_length in usbip_recv_xbuff(), which checks the
response value against the original allocation size.

Kelvin Mbogo's series ("usb: usbip: fix integer overflow in
usbip_recv_iso()", v2) hardens the receive-side functions themselves;
this patch complements that work by catching the bad value at its
source -- in usbip_pack_ret_submit() before the overwrite -- and
using the tighter per-URB allocation bound rather than the global
USBIP_MAX_ISO_PACKETS limit.

Fix this by checking rpdu->number_of_packets against
urb->number_of_packets in usbip_pack_ret_submit() before the
overwrite. On violation, clamp to zero so that usbip_recv_iso() and
usbip_pad_iso() safely return early.

Fixes: 1325f85fa49f ("staging: usbip: bugfix add number of packets for isochronous frames")
Cc: stable@vger.kernel.org
Acked-by: Shuah Khan <skhan@linuxfoundation.org>
Signed-off-by: Nathan Rebello <nathan.c.rebello@gmail.com>
---
Changes in v2:
  - Fixed patch whitespace corruption
  - Corrected Fixes tag commit hash

 drivers/usb/usbip/usbip_common.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 8ebaaeaf848e..a5837c0feb05 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -470,6 +470,18 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb,
 		urb->status		= rpdu->status;
 		urb->actual_length	= rpdu->actual_length;
 		urb->start_frame	= rpdu->start_frame;
+		/*
+		 * The number_of_packets field determines the length of
+		 * iso_frame_desc[], which is a flexible array allocated
+		 * at URB creation time. A response must never claim more
+		 * packets than originally submitted; doing so would cause
+		 * an out-of-bounds write in usbip_recv_iso() and
+		 * usbip_pad_iso(). Clamp to zero on violation so both
+		 * functions safely return early.
+		 */
+		if (rpdu->number_of_packets < 0 ||
+		    rpdu->number_of_packets > urb->number_of_packets)
+			rpdu->number_of_packets = 0;
 		urb->number_of_packets = rpdu->number_of_packets;
 		urb->error_count	= rpdu->error_count;
 	}
-- 
2.43.0.windows.1


^ permalink raw reply related

* [PATCH v2] USB: serial: option: add Telit Cinterion FN990A MBIM composition
From: Fabio Porcedda @ 2026-04-02  8:37 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Greg Kroah-Hartman, linux-usb, Daniele Palmas, Fabio Porcedda,
	stable

Add the following Telit Cinterion FN990A MBIM composition:

0x1074: MBIM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (diag) +
        DPL (Data Packet Logging) + adb

T:  Bus=01 Lev=01 Prnt=04 Port=06 Cnt=01 Dev#=  3 Spd=480  MxCh= 0
D:  Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1bc7 ProdID=1074 Rev=05.04
S:  Manufacturer=Telit Wireless Solutions
S:  Product=FN990
S:  SerialNumber=70628d0c
C:  #Ifs= 7 Cfg#= 1 Atr=e0 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim
E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=32ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim
E:  Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option
E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=83(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=85(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=87(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none)
E:  Ad=8f(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms

Cc: stable@vger.kernel.org
Signed-off-by: Fabio Porcedda <fabio.porcedda@gmail.com>
---
v2:
- Added "Cc: stable@vger.kernel.org"
- Link to v1: https://lore.kernel.org/linux-usb/20260402082747.98441-1-fabio.porcedda@gmail.com

 drivers/usb/serial/option.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 313612114db9..c71461893d20 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1383,6 +1383,8 @@ static const struct usb_device_id option_ids[] = {
 	  .driver_info = NCTRL(2) | RSVD(3) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff),	/* Telit FN990A (ECM) */
 	  .driver_info = NCTRL(0) | RSVD(1) },
+	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1074, 0xff),	/* Telit FN990A (MBIM) */
+	  .driver_info = NCTRL(5) | RSVD(6) | RSVD(7) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff),	/* Telit FN990A (PCIe) */
 	  .driver_info = RSVD(0) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff),	/* Telit FN990A (rmnet + audio) */
-- 
2.53.0


^ permalink raw reply related

* [PATCH v4] usbip: tools: add hint when no exported devices are found
From: Zongmin Zhou @ 2026-04-02  8:32 UTC (permalink / raw)
  To: skhan; +Cc: gregkh, i, linux-kernel, linux-usb, valentina.manea.m,
	Zongmin Zhou
In-Reply-To: <8d7000a9-981c-468a-bd4b-60111e0b77e9@linuxfoundation.org>

From: Zongmin Zhou <zhouzongmin@kylinos.cn>

When refresh_exported_devices() finds no devices, it's helpful to
inform users about potential causes. This could be due to:

1. The usbip driver module is not loaded.
2. No devices have been exported yet.

Add an informational message to guide users when ndevs == 0.

Also update the condition in usbip_host_driver_open() and
usbip_device_driver_open() to check both ret and ndevs == 0,
and change err() to info().

Message visibility by scenario:
- usbipd (console mode): Show on console/serial, this allows instant
  visibility for debugging.
- usbipd -D (daemon mode): Message logged to syslog, can keep logs for
  later traceability in production. Also can use "journalctl -f" to
  trace on console.

Suggested-by: Shuah Khan <skhan@linuxfoundation.org>
Signed-off-by: Zongmin Zhou <zhouzongmin@kylinos.cn>
---
Changes in v4:
- Printing behavior adjusted as suggested.
Changes in v3:
- Just add an informational message when no devices are found.
Changes in v2:
- Use system calls directly instead of checking sysfs dir.

 tools/usb/usbip/libsrc/usbip_device_driver.c | 6 +++---
 tools/usb/usbip/libsrc/usbip_host_common.c   | 3 +++
 tools/usb/usbip/libsrc/usbip_host_driver.c   | 7 ++++---
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c
index 927a151fa9aa..1dfbb76ab26c 100644
--- a/tools/usb/usbip/libsrc/usbip_device_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.c
@@ -137,9 +137,9 @@ static int usbip_device_driver_open(struct usbip_host_driver *hdriver)
 	INIT_LIST_HEAD(&hdriver->edev_list);
 
 	ret = usbip_generic_driver_open(hdriver);
-	if (ret)
-		err("please load " USBIP_CORE_MOD_NAME ".ko and "
-		    USBIP_DEVICE_DRV_NAME ".ko!");
+	if (ret || hdriver->ndevs == 0)
+		info("please load " USBIP_CORE_MOD_NAME ".ko and "
+		     USBIP_DEVICE_DRV_NAME ".ko");
 
 	return ret;
 }
diff --git a/tools/usb/usbip/libsrc/usbip_host_common.c b/tools/usb/usbip/libsrc/usbip_host_common.c
index ca78aa368476..01599cb2fa7b 100644
--- a/tools/usb/usbip/libsrc/usbip_host_common.c
+++ b/tools/usb/usbip/libsrc/usbip_host_common.c
@@ -149,6 +149,9 @@ static int refresh_exported_devices(struct usbip_host_driver *hdriver)
 		}
 	}
 
+	if (hdriver->ndevs == 0)
+		info("Please load appropriate modules or export devices.");
+
 	return 0;
 }
 
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c
index 573e73ec36bd..bd8a6b84de0e 100644
--- a/tools/usb/usbip/libsrc/usbip_host_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_host_driver.c
@@ -32,9 +32,10 @@ static int usbip_host_driver_open(struct usbip_host_driver *hdriver)
 	INIT_LIST_HEAD(&hdriver->edev_list);
 
 	ret = usbip_generic_driver_open(hdriver);
-	if (ret)
-		err("please load " USBIP_CORE_MOD_NAME ".ko and "
-		    USBIP_HOST_DRV_NAME ".ko!");
+	if (ret || hdriver->ndevs == 0)
+		info("please load " USBIP_CORE_MOD_NAME ".ko and "
+		     USBIP_HOST_DRV_NAME ".ko");
+
 	return ret;
 }
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH net-next v6 2/2] r8152: Add support for the RTL8157 hardware
From: Birger Koblitz @ 2026-04-02  8:28 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Birger Koblitz
In-Reply-To: <20260402-rtl8157_next-v6-0-a9b77c0931ef@birger-koblitz.de>

The RTL8157 uses a different packet descriptor format compared to the
previous generation of chips. Add support for this format by adding a
descriptor format structure into the r8152 structure and corresponding
desc_ops functions which abstract the vlan-tag, tx/rx len and
tx/rx checksum algorithms.

Also, add support for the SRAM access interface of the RTL8157 and
the ADV indirect access interface and PHY setup.

For initialization of the RTL8157, combine the existing RTL8156B and
RTL8156 init functions and add RTL8157-specific functinality in order
to improve code readability and maintainability.
r8156_init() is now called with RTL_VER_10 and RTL_VER_11 for the RTL8156,
with RTL_VER_12, RTL_VER_13 and RTL_VER_15 for the RTL8156B and with
RTL_VER_16 for the RTL8157 and checks the version for chip-specific code.
Also add USB power control functions for the RTL8157.

Add support for the USB device ID of Realtek RTL8157-based adapters. Detect
the RTL8157 as RTL_VER_16 and set it up.

Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
 drivers/net/usb/r8152.c | 929 ++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 745 insertions(+), 184 deletions(-)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 15527f3cb5478df3450d5e53b5a277fca1460e44..dab08f8983b426f6ac35261b3d7ceb14da751b58 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -123,6 +123,7 @@
 #define USB_CSR_DUMMY1		0xb464
 #define USB_CSR_DUMMY2		0xb466
 #define USB_DEV_STAT		0xb808
+#define USB_U2P3_V2_CTRL	0xc2c0
 #define USB_CONNECT_TIMER	0xcbf8
 #define USB_MSC_TIMER		0xcbfc
 #define USB_BURST_SIZE		0xcfc0
@@ -156,6 +157,9 @@
 #define USB_U1U2_TIMER		0xd4da
 #define USB_FW_TASK		0xd4e8	/* RTL8153B */
 #define USB_RX_AGGR_NUM		0xd4ee
+#define USB_ADV_ADDR		0xd5d6
+#define USB_ADV_DATA		0xd5d8
+#define USB_ADV_CMD		0xd5dc
 #define USB_UPS_CTRL		0xd800
 #define USB_POWER_CUT		0xd80a
 #define USB_MISC_0		0xd81a
@@ -213,6 +217,8 @@
 #define OCP_PHY_PATCH_STAT	0xb800
 #define OCP_PHY_PATCH_CMD	0xb820
 #define OCP_PHY_LOCK		0xb82e
+#define OCP_SRAM2_ADDR		0xb87c
+#define OCP_SRAM2_DATA		0xb87e
 #define OCP_ADC_IOFFSET		0xbcfc
 #define OCP_ADC_CFG		0xbc06
 #define OCP_SYSCLK_CFG		0xc416
@@ -490,6 +496,12 @@
 /* USB_RX_AGGR_NUM */
 #define RX_AGGR_NUM_MASK	0x1ff
 
+/* USB_ADV_CMD */
+#define ADV_CMD_BMU		0
+#define ADV_CMD_BUSY		BIT(0)
+#define ADV_CMD_WR		BIT(1)
+#define ADV_CMD_IP		BIT(2)
+
 /* USB_UPS_CTRL */
 #define POWER_CUT		0x0100
 
@@ -529,11 +541,15 @@
 #define CDC_ECM_EN		BIT(3)
 #define RX_AGG_DISABLE		0x0010
 #define RX_ZERO_EN		0x0080
+#define RX_DESC_16B		0x0400
 
 /* USB_U2P3_CTRL */
 #define U2P3_ENABLE		0x0001
 #define RX_DETECT8		BIT(3)
 
+/* USB_U2P3_V2_CTRL */
+#define U2P3_V2_ENABLE		BIT(29)
+
 /* USB_POWER_CUT */
 #define PWR_EN			0x0001
 #define PHASE2_EN		0x0008
@@ -746,8 +762,6 @@ enum rtl_register_content {
 #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
@@ -759,7 +773,6 @@ enum rtl_register_content {
 #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 {
@@ -844,6 +857,40 @@ struct tx_desc {
 #define TX_VLAN_TAG		BIT(16)
 };
 
+struct rx_desc_v2 {
+	__le32 opts1;
+#define RX_LEN_MASK_2			0xfffe0000
+#define rx_v2_get_len(x)		(((x) & RX_LEN_MASK_2) >> 17)
+#define RX_VLAN_TAG_2			BIT(3)
+#define RX_VER_MASK			0x3
+
+	__le32 opts2;
+
+	__le32 opts3;
+#define IPF_2				BIT(26) /* IP checksum fail */
+#define UDPF_2				BIT(25) /* UDP checksum fail */
+#define TCPF_2				BIT(24) /* TCP checksum fail */
+#define RD_IPV6_CS_2			BIT(15)
+#define RD_IPV4_CS_2			BIT(14)
+#define RD_UDP_CS_2			BIT(11)
+#define RD_TCP_CS_2			BIT(10)
+
+	__le32 opts4;
+};
+
+struct tx_desc_v2 {
+	__le32 opts1;
+
+	__le32 opts2;
+#define TCPHO_MAX_2		0x3ffU
+
+	__le32 opts3;
+#define tx_v2_set_len(x)	((x) << 4)
+
+	__le32 opts4;
+#define TX_SIG			(0x15 << 27)
+};
+
 struct r8152;
 
 struct rx_agg {
@@ -917,6 +964,19 @@ struct r8152 {
 		u32 ctap_short_off:1;
 	} ups_info;
 
+	struct desc_info {
+		void (*vlan_tag)(void *desc, struct sk_buff *skb);
+		u8 align;
+		u8 size;
+	} rx_desc, tx_desc;
+
+	struct desc_ops {
+		void (*tx_len)(struct r8152 *tp, void *desc, u32 len);
+		u32 (*rx_len)(struct r8152 *tp, void *desc);
+		u8 (*rx_csum)(struct r8152 *tp, void *desc);
+		int (*tx_csum)(struct r8152 *tp, void *desc, struct sk_buff *skb, u32 len);
+	} desc_ops;
+
 #define RTL_VER_SIZE		32
 
 	struct rtl_fw {
@@ -1181,6 +1241,7 @@ enum rtl_version {
 	RTL_VER_13,
 	RTL_VER_14,
 	RTL_VER_15,
+	RTL_VER_16,
 
 	RTL_VER_MAX
 };
@@ -1206,7 +1267,7 @@ enum tx_csum_stat {
 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))
+#define RTL_LIMITED_TSO_SIZE	(size_to_mtu(agg_buf_sz) - tp->tx_desc.size)
 
 /* 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
@@ -1617,6 +1678,122 @@ static inline int r8152_mdio_read(struct r8152 *tp, u32 reg_addr)
 	return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2);
 }
 
+static int wait_cmd_ready(struct r8152 *tp, u16 cmd)
+{
+	return poll_timeout_us(u16 ocp_data = ocp_read_word(tp, MCU_TYPE_USB, cmd),
+				!(ocp_data & ADV_CMD_BUSY), 2000, 20000, false);
+}
+
+static int ocp_adv_read(struct r8152 *tp, u16 cmd, u16 addr, u32 *data)
+{
+	int ret;
+
+	ret = wait_cmd_ready(tp, USB_ADV_CMD);
+	if (ret < 0)
+		goto out;
+
+	ocp_write_word(tp, MCU_TYPE_USB, USB_ADV_ADDR, addr);
+
+	cmd |= ADV_CMD_BUSY;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_ADV_CMD, cmd);
+
+	ret = wait_cmd_ready(tp, USB_ADV_CMD);
+	if (ret < 0)
+		goto out;
+
+	*data = ocp_read_dword(tp, MCU_TYPE_USB, USB_ADV_DATA);
+
+out:
+	return ret;
+}
+
+static int ocp_adv_write(struct r8152 *tp, u16 cmd, u16 addr, u32 data)
+{
+	int ret;
+
+	ret = wait_cmd_ready(tp, USB_ADV_CMD);
+	if (ret < 0)
+		goto out;
+
+	cmd |= ADV_CMD_WR;
+	ocp_write_dword(tp, MCU_TYPE_USB, USB_ADV_DATA, data);
+
+	ocp_write_word(tp, MCU_TYPE_USB, USB_ADV_ADDR, addr);
+
+	cmd |= ADV_CMD_BUSY;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_ADV_CMD, cmd);
+
+out:
+	return ret;
+}
+
+static int rtl_bmu_read(struct r8152 *tp, u16 addr, u32 *data)
+{
+	return ocp_adv_read(tp, ADV_CMD_BMU, addr, data);
+}
+
+static int rtl_bmu_write(struct r8152 *tp, u16 addr, u32 data)
+{
+	return ocp_adv_write(tp, ADV_CMD_BMU, addr, data);
+}
+
+static int rtl_bmu_w0w1(struct r8152 *tp, u16 addr, u32 clear, u32 set)
+{
+	u32 bmu;
+	int ret;
+
+	ret = rtl_bmu_read(tp, addr, &bmu);
+	if (ret < 0)
+		goto out;
+
+	bmu = (bmu & ~clear) | set;
+	ret = rtl_bmu_write(tp, addr, bmu);
+
+out:
+	return ret;
+}
+
+static int rtl_bmu_clr_bits(struct r8152 *tp, u16 addr, u32 clear)
+{
+	return rtl_bmu_w0w1(tp, addr, clear, 0);
+}
+
+static int rtl_ip_read(struct r8152 *tp, u16 addr, u32 *data)
+{
+	return ocp_adv_read(tp, ADV_CMD_IP, addr, data);
+}
+
+static int rtl_ip_write(struct r8152 *tp, u16 addr, u32 data)
+{
+	return ocp_adv_write(tp, ADV_CMD_IP, addr, data);
+}
+
+static int rtl_ip_w0w1(struct r8152 *tp, u16 addr, u32 clear, u32 set)
+{
+	int ret;
+	u32 ip;
+
+	ret = rtl_ip_read(tp, addr, &ip);
+	if (ret < 0)
+		goto out;
+
+	ip = (ip & ~clear) | set;
+	ret = rtl_ip_write(tp, addr, ip);
+
+out:
+	return ret;
+}
+
+static int rtl_ip_clr_bits(struct r8152 *tp, u16 addr, u32 clear)
+{
+	return rtl_ip_w0w1(tp, addr, clear, 0);
+}
+
+static int rtl_ip_set_bits(struct r8152 *tp, u16 addr, u32 set)
+{
+	return rtl_ip_w0w1(tp, addr, 0, set);
+}
+
 static void sram_write(struct r8152 *tp, u16 addr, u16 data)
 {
 	ocp_reg_write(tp, OCP_SRAM_ADDR, addr);
@@ -1629,6 +1806,20 @@ static u16 sram_read(struct r8152 *tp, u16 addr)
 	return ocp_reg_read(tp, OCP_SRAM_DATA);
 }
 
+static u16 sram2_read(struct r8152 *tp, u16 addr)
+{
+	ocp_reg_write(tp, OCP_SRAM2_ADDR, addr);
+	return ocp_reg_read(tp, OCP_SRAM2_DATA);
+}
+
+static void sram2_write_w0w1(struct r8152 *tp, u16 addr, u16 clear, u16 set)
+{
+	u16 data = sram2_read(tp, addr);
+
+	data = (data & ~clear) | set;
+	ocp_reg_write(tp, OCP_SRAM2_DATA, data);
+}
+
 static int read_mii_word(struct net_device *netdev, int phy_id, int reg)
 {
 	struct r8152 *tp = netdev_priv(netdev);
@@ -2159,14 +2350,14 @@ static void intr_callback(struct urb *urb)
 	}
 }
 
-static inline void *rx_agg_align(void *data)
+static void *rx_agg_align(struct r8152 *tp, void *data)
 {
-	return (void *)ALIGN((uintptr_t)data, RX_ALIGN);
+	return (void *)ALIGN((uintptr_t)data, tp->rx_desc.align);
 }
 
-static inline void *tx_agg_align(void *data)
+static void *tx_agg_align(struct r8152 *tp, void *data)
 {
-	return (void *)ALIGN((uintptr_t)data, TX_ALIGN);
+	return (void *)ALIGN((uintptr_t)data, tp->tx_desc.align);
 }
 
 static void free_rx_agg(struct r8152 *tp, struct rx_agg *agg)
@@ -2284,9 +2475,9 @@ static int alloc_all_mem(struct r8152 *tp)
 		if (!buf)
 			goto err1;
 
-		if (buf != tx_agg_align(buf)) {
+		if (buf != tx_agg_align(tp, buf)) {
 			kfree(buf);
-			buf = kmalloc_node(agg_buf_sz + TX_ALIGN, GFP_KERNEL,
+			buf = kmalloc_node(agg_buf_sz + tp->tx_desc.align, GFP_KERNEL,
 					   node);
 			if (!buf)
 				goto err1;
@@ -2302,7 +2493,7 @@ static int alloc_all_mem(struct r8152 *tp)
 		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);
+		tp->tx_info[i].head = tx_agg_align(tp, buf);
 
 		list_add_tail(&tp->tx_info[i].list, &tp->tx_free);
 	}
@@ -2389,8 +2580,17 @@ static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb,
 	}
 }
 
-static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb)
+static void r8152_tx_len(struct r8152 *tp, void *tx_desc, u32 len)
 {
+	struct tx_desc *desc = tx_desc;
+
+	desc->opts1 |= cpu_to_le32(len);
+}
+
+static void r8152_tx_vlan_tag(void *d, struct sk_buff *skb)
+{
+	struct tx_desc *desc = d;
+
 	if (skb_vlan_tag_present(skb)) {
 		u32 opts2;
 
@@ -2399,8 +2599,10 @@ static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb)
 	}
 }
 
-static inline void rtl_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb)
+static void r8152_rx_vlan_tag(void *d, struct sk_buff *skb)
 {
+	struct rx_desc *desc = d;
+
 	u32 opts2 = le32_to_cpu(desc->opts2);
 
 	if (opts2 & RX_VLAN_TAG)
@@ -2408,10 +2610,11 @@ static inline void rtl_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb)
 				       swab16(opts2 & 0xffff));
 }
 
-static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc,
+static int r8152_tx_csum(struct r8152 *tp, void *d,
 			 struct sk_buff *skb, u32 len)
 {
 	u32 mss = skb_shinfo(skb)->gso_size;
+	struct tx_desc *desc = d;
 	u32 opts1, opts2 = 0;
 	int ret = TX_CSUM_SUCCESS;
 
@@ -2496,6 +2699,74 @@ static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc,
 	return ret;
 }
 
+static u32 r8152_rx_len(struct r8152 *tp, void *d)
+{
+	struct rx_desc *desc = d;
+
+	return le32_to_cpu(desc->opts1) & RX_LEN_MASK;
+}
+
+static u32 r8157_rx_len(struct r8152 *tp, void *d)
+{
+	struct rx_desc_v2 *desc = d;
+
+	return rx_v2_get_len(le32_to_cpu(desc->opts1));
+}
+
+static void r8157_rx_vlan_tag(void *desc, struct sk_buff *skb)
+{
+	struct rx_desc_v2 *d = desc;
+	u32 opts1;
+
+	opts1 = le32_to_cpu(d->opts1);
+	if (opts1 & RX_VLAN_TAG_2) {
+		u32 opts2 = le32_to_cpu(d->opts2);
+
+		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+				       swab16((opts2 >> 16) & 0xffff));
+	}
+}
+
+static int r8157_tx_csum(struct r8152 *tp, void *tx_desc, struct sk_buff *skb, u32 len)
+{
+	u32 mss = skb_shinfo(skb)->gso_size;
+
+	if (!mss && skb->ip_summed == CHECKSUM_PARTIAL) {
+		u32 transport_offset = (u32)skb_transport_offset(skb);
+
+		if (transport_offset > TCPHO_MAX_2) {
+			netif_warn(tp, tx_err, tp->netdev,
+				   "Invalid transport offset 0x%x\n",
+				   transport_offset);
+			return TX_CSUM_NONE;
+		}
+	}
+
+	return r8152_tx_csum(tp, tx_desc, skb, len);
+}
+
+static void r8157_tx_len(struct r8152 *tp, void *tx_desc, u32 len)
+{
+	struct tx_desc_v2 *desc = tx_desc;
+
+	desc->opts3 = cpu_to_le32(tx_v2_set_len(len));
+	desc->opts4 = cpu_to_le32(TX_SIG);
+}
+
+static int rtl_tx_csum(struct r8152 *tp, void *desc, struct sk_buff *skb,
+		       u32 len)
+{
+	int ret = TX_CSUM_SUCCESS;
+
+	WARN_ON_ONCE(len > TX_LEN_MAX);
+
+	ret = tp->desc_ops.tx_csum(tp, desc, skb, len);
+	if (!ret)
+		tp->desc_ops.tx_len(tp, desc, len);
+
+	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;
@@ -2512,33 +2783,33 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
 	agg->skb_len = 0;
 	remain = agg_buf_sz;
 
-	while (remain >= ETH_ZLEN + sizeof(struct tx_desc)) {
-		struct tx_desc *tx_desc;
+	while (remain >= ETH_ZLEN + tp->tx_desc.size) {
 		struct sk_buff *skb;
 		unsigned int len;
+		void *tx_desc;
 
 		skb = __skb_dequeue(&skb_head);
 		if (!skb)
 			break;
 
-		len = skb->len + sizeof(*tx_desc);
+		len = skb->len + tp->tx_desc.size;
 
 		if (len > remain) {
 			__skb_queue_head(&skb_head, skb);
 			break;
 		}
 
-		tx_data = tx_agg_align(tx_data);
-		tx_desc = (struct tx_desc *)tx_data;
+		tx_data = tx_agg_align(tp, tx_data);
+		tx_desc = (void *)tx_data;
 
-		if (r8152_tx_csum(tp, tx_desc, skb, skb->len)) {
+		if (rtl_tx_csum(tp, tx_desc, skb, skb->len)) {
 			r8152_csum_workaround(tp, skb, &skb_head);
 			continue;
 		}
 
-		rtl_tx_vlan_tag(tx_desc, skb);
+		tp->tx_desc.vlan_tag(tx_desc, skb);
 
-		tx_data += sizeof(*tx_desc);
+		tx_data += tp->tx_desc.size;
 
 		len = skb->len;
 		if (skb_copy_bits(skb, 0, tx_data, len) < 0) {
@@ -2546,7 +2817,7 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
 
 			stats->tx_dropped++;
 			dev_kfree_skb_any(skb);
-			tx_data -= sizeof(*tx_desc);
+			tx_data -= tp->tx_desc.size;
 			continue;
 		}
 
@@ -2556,7 +2827,7 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
 
 		dev_kfree_skb_any(skb);
 
-		remain = agg_buf_sz - (int)(tx_agg_align(tx_data) - agg->head);
+		remain = agg_buf_sz - (int)(tx_agg_align(tp, tx_data) - agg->head);
 
 		if (tp->dell_tb_rx_agg_bug)
 			break;
@@ -2594,8 +2865,9 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
 	return ret;
 }
 
-static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc)
+static u8 r8152_rx_csum(struct r8152 *tp, void *d)
 {
+	struct rx_desc *rx_desc = d;
 	u8 checksum = CHECKSUM_NONE;
 	u32 opts2, opts3;
 
@@ -2623,6 +2895,30 @@ static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc)
 	return checksum;
 }
 
+static u8 r8157_rx_csum(struct r8152 *tp, void *desc)
+{
+	struct rx_desc_v2 *d = desc;
+	u8 checksum = CHECKSUM_NONE;
+	u32 opts3;
+
+	if (!(tp->netdev->features & NETIF_F_RXCSUM))
+		goto return_result;
+
+	opts3 = le32_to_cpu(d->opts3);
+
+	if ((opts3 & (RD_IPV4_CS_2 | IPF_2)) == (RD_IPV4_CS_2 | IPF_2)) {
+		checksum = CHECKSUM_NONE;
+	} else if (opts3 & (RD_IPV4_CS_2 | RD_IPV6_CS_2)) {
+		if ((opts3 & (RD_UDP_CS_2 | UDPF_2)) ==  RD_UDP_CS_2)
+			checksum = CHECKSUM_UNNECESSARY;
+		else if ((opts3 & (RD_TCP_CS_2 | TCPF_2)) == RD_TCP_CS_2)
+			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;
@@ -2698,10 +2994,10 @@ static int rx_bottom(struct r8152 *tp, int budget)
 	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;
+		void *rx_desc;
 		u8 *rx_data;
 
 		/* A bulk transfer of USB may contain may packets, so the
@@ -2724,7 +3020,7 @@ static int rx_bottom(struct r8152 *tp, int budget)
 
 		rx_desc = agg->buffer;
 		rx_data = agg->buffer;
-		len_used += sizeof(struct rx_desc);
+		len_used += tp->rx_desc.size;
 
 		while (urb->actual_length > len_used) {
 			struct net_device *netdev = tp->netdev;
@@ -2735,7 +3031,7 @@ static int rx_bottom(struct r8152 *tp, int budget)
 
 			WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);
 
-			pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
+			pkt_len = tp->desc_ops.rx_len(tp, rx_desc);
 			if (pkt_len < ETH_ZLEN)
 				break;
 
@@ -2745,7 +3041,7 @@ static int rx_bottom(struct r8152 *tp, int budget)
 
 			pkt_len -= ETH_FCS_LEN;
 			len = pkt_len;
-			rx_data += sizeof(struct rx_desc);
+			rx_data += tp->rx_desc.size;
 
 			if (!agg_free || tp->rx_copybreak > len)
 				use_frags = false;
@@ -2776,8 +3072,8 @@ static int rx_bottom(struct r8152 *tp, int budget)
 				goto find_next_rx;
 			}
 
-			skb->ip_summed = r8152_rx_csum(tp, rx_desc);
-			rtl_rx_vlan_tag(rx_desc, skb);
+			skb->ip_summed = tp->desc_ops.rx_csum(tp, rx_desc);
+			tp->rx_desc.vlan_tag(rx_desc, skb);
 
 			if (use_frags) {
 				if (rx_frag_head_sz) {
@@ -2814,10 +3110,10 @@ static int rx_bottom(struct r8152 *tp, int budget)
 			}
 
 find_next_rx:
-			rx_data = rx_agg_align(rx_data + len + ETH_FCS_LEN);
-			rx_desc = (struct rx_desc *)rx_data;
+			rx_data = rx_agg_align(tp, rx_data + len + ETH_FCS_LEN);
+			rx_desc = rx_data;
 			len_used = agg_offset(agg, rx_data);
-			len_used += sizeof(struct rx_desc);
+			len_used += tp->rx_desc.size;
 		}
 
 		WARN_ON(!agg_free && page_count(agg->page) > 1);
@@ -3060,13 +3356,19 @@ static netdev_features_t
 rtl8152_features_check(struct sk_buff *skb, struct net_device *dev,
 		       netdev_features_t features)
 {
+	struct r8152 *tp = netdev_priv(dev);
 	u32 mss = skb_shinfo(skb)->gso_size;
-	int max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX;
+	int max_offset;
+
+	if (tp->version < RTL_VER_16)
+		max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX;
+	else
+		max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX_2;
 
 	if ((mss || skb->ip_summed == CHECKSUM_PARTIAL) &&
 	    skb_transport_offset(skb) > max_offset)
 		features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
-	else if ((skb->len + sizeof(struct tx_desc)) > agg_buf_sz)
+	else if ((skb->len + tp->tx_desc.size) > agg_buf_sz)
 		features &= ~NETIF_F_GSO_MASK;
 
 	return features;
@@ -3104,31 +3406,26 @@ static void r8152b_reset_packet_filter(struct r8152 *tp)
 
 static void rtl8152_nic_reset(struct r8152 *tp)
 {
-	int i;
-
 	switch (tp->version) {
 	case RTL_TEST_01:
 	case RTL_VER_10:
 	case RTL_VER_11:
 		ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_CR, CR_TE);
-
-		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_BMU_RESET,
-				  BMU_RESET_EP_IN);
-
+		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_BMU_RESET, BMU_RESET_EP_IN);
 		ocp_word_set_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, CDC_ECM_EN);
-
 		ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_CR, CR_RE);
-
-		ocp_word_set_bits(tp, MCU_TYPE_USB, USB_BMU_RESET,
-				  BMU_RESET_EP_IN);
-
+		ocp_word_set_bits(tp, MCU_TYPE_USB, USB_BMU_RESET, BMU_RESET_EP_IN);
 		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, CDC_ECM_EN);
 		break;
 
+	case RTL_VER_16:
+		ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_CR, CR_RE | CR_TE);
+		break;
+
 	default:
 		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
 
-		for (i = 0; i < 1000; i++) {
+		for (int i = 0; i < 1000; i++) {
 			if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
 				break;
 			if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
@@ -3141,7 +3438,7 @@ static void rtl8152_nic_reset(struct r8152 *tp)
 
 static void set_tx_qlen(struct r8152 *tp)
 {
-	tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + sizeof(struct tx_desc));
+	tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + tp->tx_desc.size);
 }
 
 static inline u16 rtl8152_get_speed(struct r8152 *tp)
@@ -3345,6 +3642,7 @@ static void r8153_set_rx_early_timeout(struct r8152 *tp)
 	case RTL_VER_12:
 	case RTL_VER_13:
 	case RTL_VER_15:
+	case RTL_VER_16:
 		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT,
 			       640 / 8);
 		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR,
@@ -3356,9 +3654,14 @@ static void r8153_set_rx_early_timeout(struct r8152 *tp)
 	}
 }
 
+static u32 rx_reserved_size(struct r8152 *tp, u32 mtu)
+{
+	return mtu_to_size(mtu) + tp->rx_desc.size + tp->rx_desc.align;
+}
+
 static void r8153_set_rx_early_size(struct r8152 *tp)
 {
-	u32 ocp_data = tp->rx_buf_sz - rx_reserved_size(tp->netdev->mtu);
+	u32 ocp_data = tp->rx_buf_sz - rx_reserved_size(tp, tp->netdev->mtu);
 
 	switch (tp->version) {
 	case RTL_VER_03:
@@ -3383,6 +3686,10 @@ static void r8153_set_rx_early_size(struct r8152 *tp)
 		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
 			       ocp_data / 8);
 		break;
+	case RTL_VER_16:
+		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
+			       ocp_data / 16);
+		break;
 	default:
 		WARN_ON_ONCE(1);
 		break;
@@ -3494,6 +3801,7 @@ static void rtl_rx_vlan_en(struct r8152 *tp, bool enable)
 	case RTL_VER_12:
 	case RTL_VER_13:
 	case RTL_VER_15:
+	case RTL_VER_16:
 	default:
 		if (enable)
 			ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_RCR1,
@@ -3651,6 +3959,14 @@ static void r8153_u2p3en(struct r8152 *tp, bool enable)
 		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_U2P3_CTRL, U2P3_ENABLE);
 }
 
+static int r8157_u2p3en(struct r8152 *tp, bool enable)
+{
+	if (enable)
+		return rtl_ip_set_bits(tp, USB_U2P3_V2_CTRL, U2P3_V2_ENABLE);
+	else
+		return rtl_ip_clr_bits(tp, USB_U2P3_V2_CTRL, U2P3_V2_ENABLE);
+}
+
 static void r8153b_ups_flags(struct r8152 *tp)
 {
 	u32 ups_flags = 0;
@@ -3996,6 +4312,18 @@ static void r8153b_power_cut_en(struct r8152 *tp, bool enable)
 	ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_MISC_0, PCUT_STATUS);
 }
 
+static void r8157_power_cut_en(struct r8152 *tp, bool enable)
+{
+	if (enable) {
+		ocp_word_set_bits(tp, MCU_TYPE_USB, USB_POWER_CUT, PWR_EN | PHASE2_EN);
+		ocp_byte_set_bits(tp, MCU_TYPE_USB, USB_MISC_2, BIT(1));
+	} else {
+		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_POWER_CUT, PWR_EN);
+		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_MISC_0, PCUT_STATUS);
+		ocp_byte_clr_bits(tp, MCU_TYPE_USB, USB_MISC_2, BIT(1));
+	}
+}
+
 static void r8153_queue_wake(struct r8152 *tp, bool enable)
 {
 	if (enable)
@@ -4112,6 +4440,22 @@ static void rtl8156_runtime_enable(struct r8152 *tp, bool enable)
 	}
 }
 
+static void rtl8157_runtime_enable(struct r8152 *tp, bool enable)
+{
+	if (enable) {
+		r8153_queue_wake(tp, true);
+		r8153b_u1u2en(tp, false);
+		r8157_u2p3en(tp, false);
+		rtl_runtime_suspend_enable(tp, true);
+	} else {
+		r8153_queue_wake(tp, false);
+		rtl_runtime_suspend_enable(tp, false);
+		r8157_u2p3en(tp, true);
+		if (tp->udev->speed >= USB_SPEED_SUPER)
+			r8153b_u1u2en(tp, true);
+	}
+}
+
 static void r8153_teredo_off(struct r8152 *tp)
 {
 	switch (tp->version) {
@@ -4136,6 +4480,7 @@ static void r8153_teredo_off(struct r8152 *tp)
 	case RTL_VER_13:
 	case RTL_VER_14:
 	case RTL_VER_15:
+	case RTL_VER_16:
 	default:
 		/* The bit 0 ~ 7 are relative with teredo settings. They are
 		 * W1C (write 1 to clear), so set all 1 to disable it.
@@ -4189,6 +4534,7 @@ static void rtl_clear_bp(struct r8152 *tp, u16 type)
 		bp_num = 8;
 		break;
 	case RTL_VER_14:
+	case RTL_VER_16:
 	default:
 		ocp_write_word(tp, type, USB_BP2_EN, 0);
 		bp_num = 16;
@@ -4296,6 +4642,7 @@ static bool rtl8152_is_fw_phy_speed_up_ok(struct r8152 *tp, struct fw_phy_speed_
 	case RTL_VER_11:
 	case RTL_VER_12:
 	case RTL_VER_14:
+	case RTL_VER_16:
 		goto out;
 	case RTL_VER_13:
 	case RTL_VER_15:
@@ -5457,6 +5804,7 @@ static void rtl_eee_enable(struct r8152 *tp, bool enable)
 	case RTL_VER_12:
 	case RTL_VER_13:
 	case RTL_VER_15:
+	case RTL_VER_16:
 		if (enable) {
 			r8156_eee_en(tp, true);
 			ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv);
@@ -6041,15 +6389,24 @@ static int rtl8156_enable(struct r8152 *tp)
 	if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
 		return -ENODEV;
 
-	r8156_fc_parameter(tp);
+	if (tp->version < RTL_VER_12)
+		r8156_fc_parameter(tp);
+
 	set_tx_qlen(tp);
 	rtl_set_eee_plus(tp);
+
+	if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_16)
+		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, RX_AGGR_NUM_MASK);
+
 	r8153_set_rx_early_timeout(tp);
 	r8153_set_rx_early_size(tp);
 
 	speed = rtl8152_get_speed(tp);
 	rtl_set_ifg(tp, speed);
 
+	if (tp->version >= RTL_VER_16)
+		return rtl_enable(tp);
+
 	if (speed & _2500bps)
 		ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
 				  IDLE_SPDWN_EN);
@@ -6057,10 +6414,12 @@ static int rtl8156_enable(struct r8152 *tp)
 		ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
 				  IDLE_SPDWN_EN);
 
-	if (speed & _1000bps)
-		ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x11);
-	else if (speed & _500bps)
-		ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x3d);
+	if (tp->version < RTL_VER_12) {
+		if (speed & _1000bps)
+			ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x11);
+		else if (speed & _500bps)
+			ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x3d);
+	}
 
 	if (tp->udev->speed == USB_SPEED_HIGH) {
 		/* USB 0xb45e[3:0] l1_nyet_hird */
@@ -6085,45 +6444,6 @@ static void rtl8156_disable(struct r8152 *tp)
 	rtl8153_disable(tp);
 }
 
-static int rtl8156b_enable(struct r8152 *tp)
-{
-	u16 speed;
-
-	if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
-		return -ENODEV;
-
-	set_tx_qlen(tp);
-	rtl_set_eee_plus(tp);
-
-	ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, RX_AGGR_NUM_MASK);
-
-	r8153_set_rx_early_timeout(tp);
-	r8153_set_rx_early_size(tp);
-
-	speed = rtl8152_get_speed(tp);
-	rtl_set_ifg(tp, speed);
-
-	if (speed & _2500bps)
-		ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
-				  IDLE_SPDWN_EN);
-	else
-		ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
-				  IDLE_SPDWN_EN);
-
-	if (tp->udev->speed == USB_SPEED_HIGH) {
-		if (is_flow_control(speed))
-			ocp_word_w0w1(tp, MCU_TYPE_USB, USB_L1_CTRL, 0xf, 0xf);
-		else
-			ocp_word_w0w1(tp, MCU_TYPE_USB, USB_L1_CTRL, 0xf, 0x1);
-	}
-
-	ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_FW_TASK, FC_PATCH_TASK);
-	usleep_range(1000, 2000);
-	ocp_word_set_bits(tp, MCU_TYPE_USB, USB_FW_TASK, FC_PATCH_TASK);
-
-	return rtl_enable(tp);
-}
-
 static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
 			     u32 advertising)
 {
@@ -6466,7 +6786,7 @@ static void rtl8156_change_mtu(struct r8152 *tp)
 	/* TX share fifo free credit full threshold */
 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, 512 / 64);
 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL,
-		       ALIGN(rx_max_size + sizeof(struct tx_desc), 1024) / 16);
+		       ALIGN(rx_max_size + tp->tx_desc.size, 1024) / 16);
 }
 
 static void rtl8156_up(struct r8152 *tp)
@@ -6475,7 +6795,8 @@ static void rtl8156_up(struct r8152 *tp)
 		return;
 
 	r8153b_u1u2en(tp, false);
-	r8153_u2p3en(tp, false);
+	if (tp->version != RTL_VER_16)
+		r8153_u2p3en(tp, false);
 	r8153_aldps_en(tp, false);
 
 	rxdy_gated_en(tp, true);
@@ -6488,6 +6809,9 @@ static void rtl8156_up(struct r8152 *tp)
 
 	ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, NOW_IS_OOB);
 
+	if (tp->version == RTL_VER_16)
+		ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_RCR1, BIT(3));
+
 	ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, MCU_BORW_EN);
 
 	rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX);
@@ -6511,8 +6835,11 @@ static void rtl8156_up(struct r8152 *tp)
 	ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3,
 			  PLA_MCU_SPDWN_EN);
 
-	ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_SPEED_OPTION,
-			  RG_PWRDN_EN | ALL_SPEED_OFF);
+	ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PLA_MCU_SPDWN_EN);
+
+	if (tp->version != RTL_VER_16)
+		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_SPEED_OPTION,
+				  RG_PWRDN_EN | ALL_SPEED_OFF);
 
 	ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, 0x00600400);
 
@@ -6522,9 +6849,10 @@ static void rtl8156_up(struct r8152 *tp)
 	}
 
 	r8153_aldps_en(tp, true);
-	r8153_u2p3en(tp, true);
+	if (tp->version != RTL_VER_16)
+		r8153_u2p3en(tp, true);
 
-	if (tp->udev->speed >= USB_SPEED_SUPER)
+	if (tp->version != RTL_VER_16 && tp->udev->speed >= USB_SPEED_SUPER)
 		r8153b_u1u2en(tp, true);
 }
 
@@ -6539,8 +6867,10 @@ static void rtl8156_down(struct r8152 *tp)
 			  PLA_MCU_SPDWN_EN);
 
 	r8153b_u1u2en(tp, false);
-	r8153_u2p3en(tp, false);
-	r8153b_power_cut_en(tp, false);
+	if (tp->version != RTL_VER_16) {
+		r8153_u2p3en(tp, false);
+		r8153b_power_cut_en(tp, false);
+	}
 	r8153_aldps_en(tp, false);
 
 	ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, NOW_IS_OOB);
@@ -7664,106 +7994,241 @@ static void r8156b_hw_phy_cfg(struct r8152 *tp)
 	set_bit(PHY_RESET, &tp->flags);
 }
 
-static void r8156_init(struct r8152 *tp)
+static void r8157_hw_phy_cfg(struct r8152 *tp)
 {
+	u32 ocp_data;
 	u16 data;
-	int i;
-
-	if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
-		return;
-
-	ocp_byte_clr_bits(tp, MCU_TYPE_USB, USB_ECM_OP, EN_ALL_SPEED);
-
-	ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
+	int ret;
 
-	ocp_word_set_bits(tp, MCU_TYPE_USB, USB_ECM_OPTION, BYPASS_MAC_RESET);
+	r8156b_wait_loading_flash(tp);
 
-	r8153b_u1u2en(tp, false);
-
-	for (i = 0; i < 500; i++) {
-		if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
-		    AUTOLOAD_DONE)
-			break;
-
-		msleep(20);
-		if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
-			return;
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+	if (ocp_data & PCUT_STATUS) {
+		ocp_data &= ~PCUT_STATUS;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
 	}
 
 	data = r8153_phy_status(tp, 0);
-	if (data == PHY_STAT_EXT_INIT)
+	switch (data) {
+	case PHY_STAT_EXT_INIT:
+		ocp_reg_clr_bits(tp, 0xa466, BIT(0));
 		ocp_reg_clr_bits(tp, 0xa468, BIT(3) | BIT(1));
+		break;
+	case PHY_STAT_LAN_ON:
+	case PHY_STAT_PWRDN:
+	default:
+		break;
+	}
 
-	r8152_mdio_test_and_clr_bit(tp, MII_BMCR, BMCR_PDOWN);
+	data = r8152_mdio_read(tp, MII_BMCR);
+	if (data & BMCR_PDOWN) {
+		data &= ~BMCR_PDOWN;
+		r8152_mdio_write(tp, MII_BMCR, data);
+	}
 
-	data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
-	WARN_ON_ONCE(data != PHY_STAT_LAN_ON);
+	r8153_aldps_en(tp, false);
+	rtl_eee_enable(tp, false);
 
-	r8153_u2p3en(tp, false);
+	ret = r8153_phy_status(tp, PHY_STAT_LAN_ON);
+	if (ret < 0)
+		return;
+	WARN_ON_ONCE(ret != PHY_STAT_LAN_ON);
 
-	/* MSC timer = 0xfff * 8ms = 32760 ms */
-	ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
+	/* PFM mode */
+	ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_PHY_PWR, PFM_PWM_SWITCH);
 
-	/* U1/U2/L1 idle timer. 500 us */
-	ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
+	/* Advanced Power Saving parameter */
+	ocp_reg_set_bits(tp, 0xa430, BIT(0) | BIT(1));
 
-	r8153b_power_cut_en(tp, false);
-	r8156_ups_en(tp, false);
-	r8153_queue_wake(tp, false);
-	rtl_runtime_suspend_enable(tp, false);
+	/* aldpsce force mode */
+	ocp_reg_clr_bits(tp, 0xa44a, BIT(2));
 
-	if (tp->udev->speed >= USB_SPEED_SUPER)
-		r8153b_u1u2en(tp, true);
+	switch (tp->version) {
+	case RTL_VER_16:
+		/* XG_INRX parameter */
+		sram_write_w0w1(tp, 0x8183, 0xff00, 0x5900);
+		ocp_reg_set_bits(tp, 0xa654, BIT(11));
+		ocp_reg_set_bits(tp, 0xb648, BIT(14));
+		ocp_reg_clr_bits(tp, 0xad2c, BIT(15));
+		ocp_reg_set_bits(tp, 0xad94, BIT(5));
+		ocp_reg_set_bits(tp, 0xada0, BIT(1));
+		ocp_reg_w0w1(tp, 0xae06, 0xfc00, 0x7c00);
+		sram2_write_w0w1(tp, 0x8647, 0xff00, 0xe600);
+		sram2_write_w0w1(tp, 0x8036, 0xff00, 0x3000);
+		sram2_write_w0w1(tp, 0x8078, 0xff00, 0x3000);
+
+		/* green mode */
+		sram2_write_w0w1(tp, 0x89e9, 0xff00, 0);
+		sram2_write_w0w1(tp, 0x8ffd, 0xff00, 0x0100);
+		sram2_write_w0w1(tp, 0x8ffe, 0xff00, 0x0200);
+		sram2_write_w0w1(tp, 0x8fff, 0xff00, 0x0400);
+
+		/* recognize AQC/Bcom function */
+		sram_write_w0w1(tp, 0x8018, 0xff00, 0x7700);
+		ocp_reg_write(tp, OCP_SRAM_ADDR, 0x8f9c);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x0005);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x00ed);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x0502);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x0b00);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0xd401);
+		sram_write_w0w1(tp, 0x8fa8, 0xff00, 0x2900);
+
+		/* RFI_corr_thd 5g */
+		sram2_write_w0w1(tp, 0x814b, 0xff00, 0x1100);
+		sram2_write_w0w1(tp, 0x814d, 0xff00, 0x1100);
+		sram2_write_w0w1(tp, 0x814f, 0xff00, 0x0b00);
+		sram2_write_w0w1(tp, 0x8142, 0xff00, 0x0100);
+		sram2_write_w0w1(tp, 0x8144, 0xff00, 0x0100);
+		sram2_write_w0w1(tp, 0x8150, 0xff00, 0x0100);
+
+		/* RFI_corr_thd 2p5g */
+		sram2_write_w0w1(tp, 0x8118, 0xff00, 0x0700);
+		sram2_write_w0w1(tp, 0x811a, 0xff00, 0x0700);
+		sram2_write_w0w1(tp, 0x811c, 0xff00, 0x0500);
+		sram2_write_w0w1(tp, 0x810f, 0xff00, 0x0100);
+		sram2_write_w0w1(tp, 0x8111, 0xff00, 0x0100);
+		sram2_write_w0w1(tp, 0x811d, 0xff00, 0x0100);
+
+		/* RFI parameter */
+		ocp_reg_clr_bits(tp, 0xad1c, BIT(8));
+		ocp_reg_w0w1(tp, 0xade8, 0xffc0, 0x1400);
+		sram2_write_w0w1(tp, 0x864b, 0xff00, 0x9d00);
+		sram2_write_w0w1(tp, 0x862c, 0xff00, 0x1200);
+		ocp_reg_write(tp, OCP_SRAM_ADDR, 0x8566);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x003f);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x3f02);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x023c);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x3b0a);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x1c00);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+		ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+
+		/* RFI-color noise gen parameter 5g */
+		ocp_reg_set_bits(tp, 0xad9c, BIT(5));
+		sram2_write_w0w1(tp, 0x8122, 0xff00, 0x0c00);
+		ocp_reg_write(tp, OCP_SRAM2_ADDR, 0x82c8);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ed);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ff);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0009);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03fe);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x000b);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0021);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f7);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03b8);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03e0);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0049);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0049);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03e0);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03b8);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f7);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0021);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x000b);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03fe);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0009);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ff);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ed);
+
+		/* RFI-color noise gen parameter 2p5g */
+		sram2_write_w0w1(tp, 0x80ef, 0xff00, 0x0c00);
+		ocp_reg_write(tp, OCP_SRAM2_ADDR, 0x82a0);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x000e);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03fe);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ed);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0006);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x001a);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f1);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03d8);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0023);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0054);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0322);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x00dd);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ab);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03dc);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0027);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x000e);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03e5);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f9);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0012);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0001);
+		ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f1);
+
+		/* modify thermal speed down threshold */
+		ocp_reg_w0w1(tp, 0xb54c, 0xffc0, 0x3700);
+
+		/* XG compatibility modification */
+		ocp_reg_set_bits(tp, 0xb648, BIT(6));
+		sram2_write_w0w1(tp, 0x8082, 0xff00, 0x5d00);
+		sram2_write_w0w1(tp, 0x807c, 0xff00, 0x5000);
+		sram2_write_w0w1(tp, 0x809d, 0xff00, 0x5000);
+		break;
+	default:
+		break;
+	}
 
-	usb_enable_lpm(tp->udev);
+	if (rtl_phy_patch_request(tp, true, true))
+		return;
 
-	r8156_mac_clk_spd(tp, true);
+	ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, EEE_SPDWN_EN);
 
-	ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3,
-			  PLA_MCU_SPDWN_EN);
+	ocp_reg_w0w1(tp, OCP_DOWN_SPEED, EN_EEE_100 | EN_EEE_1000, EN_10M_CLKDIV);
 
-	if (rtl8152_get_speed(tp) & LINK_STATUS)
-		ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS,
-				  CUR_LINK_OK | POLL_LINK_CHG);
-	else
-		ocp_word_w0w1(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, CUR_LINK_OK,
-			      POLL_LINK_CHG);
+	tp->ups_info._10m_ckdiv = true;
+	tp->ups_info.eee_plloff_100 = false;
+	tp->ups_info.eee_plloff_giga = false;
 
-	set_bit(GREEN_ETHERNET, &tp->flags);
+	ocp_reg_set_bits(tp, OCP_POWER_CFG, EEE_CLKDIV_EN);
+	tp->ups_info.eee_ckdiv = true;
 
-	/* rx aggregation */
-	ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL,
-			  RX_AGG_DISABLE | RX_ZERO_EN);
+	rtl_phy_patch_request(tp, false, true);
 
-	ocp_byte_set_bits(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ACT_ODMA);
+	rtl_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
 
-	r8156_mdio_force_mode(tp);
-	rtl_tally_reset(tp);
+	ocp_reg_clr_bits(tp, 0xa428, BIT(9));
+	ocp_reg_clr_bits(tp, 0xa5ea, BIT(0) | BIT(1));
+	tp->ups_info.lite_mode = 0;
 
-	tp->coalesce = 15000;	/* 15 us */
+	if (tp->eee_en)
+		rtl_eee_enable(tp, true);
+
+	r8153_aldps_en(tp, true);
+	r8152b_enable_fc(tp);
+
+	set_bit(PHY_RESET, &tp->flags);
 }
 
-static void r8156b_init(struct r8152 *tp)
+static void r8156_init(struct r8152 *tp)
 {
+	u32 ocp_data;
 	u16 data;
 	int i;
 
 	if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
 		return;
 
+	if (tp->version == RTL_VER_16) {
+		ocp_byte_set_bits(tp, MCU_TYPE_USB, 0xcffe, BIT(3));
+		ocp_byte_clr_bits(tp, MCU_TYPE_USB, 0xd3ca, BIT(0));
+	}
+
 	ocp_byte_clr_bits(tp, MCU_TYPE_USB, USB_ECM_OP, EN_ALL_SPEED);
 
-	ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
+	if (tp->version != RTL_VER_16)
+		ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
 
 	ocp_word_set_bits(tp, MCU_TYPE_USB, USB_ECM_OPTION, BYPASS_MAC_RESET);
 
-	ocp_word_set_bits(tp, MCU_TYPE_USB, USB_U2P3_CTRL, RX_DETECT8);
+	if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_15)
+		ocp_word_set_bits(tp, MCU_TYPE_USB, USB_U2P3_CTRL, RX_DETECT8);
 
 	r8153b_u1u2en(tp, false);
 
 	switch (tp->version) {
 	case RTL_VER_13:
 	case RTL_VER_15:
+	case RTL_VER_16:
 		r8156b_wait_loading_flash(tp);
 		break;
 	default:
@@ -7783,14 +8248,22 @@ static void r8156b_init(struct r8152 *tp)
 	data = r8153_phy_status(tp, 0);
 	if (data == PHY_STAT_EXT_INIT) {
 		ocp_reg_clr_bits(tp, 0xa468, BIT(3) | BIT(1));
-		ocp_reg_clr_bits(tp, 0xa466, BIT(0));
+		if (tp->version >= RTL_VER_12)
+			ocp_reg_clr_bits(tp, 0xa466, BIT(0));
 	}
 
-	r8152_mdio_test_and_clr_bit(tp, MII_BMCR, BMCR_PDOWN);
+	data = r8152_mdio_read(tp, MII_BMCR);
+	if (data & BMCR_PDOWN) {
+		data &= ~BMCR_PDOWN;
+		r8152_mdio_write(tp, MII_BMCR, data);
+	}
 
 	data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
 
-	r8153_u2p3en(tp, false);
+	if (tp->version == RTL_VER_16)
+		r8157_u2p3en(tp, false);
+	else
+		r8153_u2p3en(tp, false);
 
 	/* MSC timer = 0xfff * 8ms = 32760 ms */
 	ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
@@ -7798,7 +8271,11 @@ static void r8156b_init(struct r8152 *tp)
 	/* U1/U2/L1 idle timer. 500 us */
 	ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
 
-	r8153b_power_cut_en(tp, false);
+	if (tp->version == RTL_VER_16)
+		r8157_power_cut_en(tp, false);
+	else
+		r8153b_power_cut_en(tp, false);
+
 	r8156_ups_en(tp, false);
 	r8153_queue_wake(tp, false);
 	rtl_runtime_suspend_enable(tp, false);
@@ -7808,39 +8285,53 @@ static void r8156b_init(struct r8152 *tp)
 
 	usb_enable_lpm(tp->udev);
 
-	ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_RCR, SLOT_EN);
+	if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_15) {
+		ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_RCR, SLOT_EN);
 
-	ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_CPCR, FLOW_CTRL_EN);
+		ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_CPCR, FLOW_CTRL_EN);
 
-	/* enable fc timer and set timer to 600 ms. */
-	ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER,
-		       CTRL_TIMER_EN | (600 / 8));
+		/* enable fc timer and set timer to 600 ms. */
+		ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER, CTRL_TIMER_EN | (600 / 8));
 
-	if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & DACK_DET_EN))
-		ocp_word_w0w1(tp, MCU_TYPE_USB, USB_FW_CTRL, AUTO_SPEEDUP,
-			      FLOW_CTRL_PATCH_2);
-	else
-		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_FW_CTRL, AUTO_SPEEDUP);
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL);
+		if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & DACK_DET_EN))
+			ocp_data |= FLOW_CTRL_PATCH_2;
+		ocp_data &= ~AUTO_SPEEDUP;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data);
 
-	ocp_word_set_bits(tp, MCU_TYPE_USB, USB_FW_TASK, FC_PATCH_TASK);
+		ocp_word_set_bits(tp, MCU_TYPE_USB, USB_FW_TASK, FC_PATCH_TASK);
+	}
 
 	r8156_mac_clk_spd(tp, true);
 
-	ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3,
-			  PLA_MCU_SPDWN_EN);
+	if (tp->version != RTL_VER_16)
+		ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PLA_MCU_SPDWN_EN);
 
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
 	if (rtl8152_get_speed(tp) & LINK_STATUS)
-		ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS,
-				  CUR_LINK_OK | POLL_LINK_CHG);
+		ocp_data |= CUR_LINK_OK;
 	else
-		ocp_word_w0w1(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, CUR_LINK_OK,
-			      POLL_LINK_CHG);
+		ocp_data &= ~CUR_LINK_OK;
+	ocp_data |= POLL_LINK_CHG;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
 
 	set_bit(GREEN_ETHERNET, &tp->flags);
 
-	/* rx aggregation */
-	ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL,
-			  RX_AGG_DISABLE | RX_ZERO_EN);
+	/* rx aggregation / 16 bytes Rx descriptor */
+	if (tp->version == RTL_VER_16)
+		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_DESC_16B);
+	else
+		ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_ZERO_EN);
+
+	if (tp->version < RTL_VER_12)
+		ocp_byte_set_bits(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ACT_ODMA);
+
+	if (tp->version == RTL_VER_16) {
+		/* Disable Rx Zero Len */
+		rtl_bmu_clr_bits(tp, 0x2300, BIT(3));
+		/* TX descriptor Signature */
+		ocp_byte_clr_bits(tp, MCU_TYPE_USB, 0xd4ae, BIT(1));
+	}
 
 	r8156_mdio_force_mode(tp);
 	rtl_tally_reset(tp);
@@ -8990,6 +9481,11 @@ static void rtl8153_unload(struct r8152 *tp)
 		return;
 
 	r8153_power_cut_en(tp, false);
+
+	if (tp->version >= RTL_VER_16) {
+		/* Disable Interrupt Mitigation */
+		ocp_byte_clr_bits(tp, MCU_TYPE_USB, 0xcf04, BIT(0) | BIT(1) | BIT(2) | BIT(7));
+	}
 }
 
 static void rtl8153b_unload(struct r8152 *tp)
@@ -9000,6 +9496,38 @@ static void rtl8153b_unload(struct r8152 *tp)
 	r8153b_power_cut_en(tp, false);
 }
 
+static int r8152_desc_init(struct r8152 *tp)
+{
+	tp->rx_desc.size = sizeof(struct rx_desc);
+	tp->rx_desc.align = 8;
+	tp->rx_desc.vlan_tag = r8152_rx_vlan_tag;
+	tp->desc_ops.rx_csum = r8152_rx_csum;
+	tp->desc_ops.rx_len = r8152_rx_len;
+	tp->tx_desc.size = sizeof(struct tx_desc);
+	tp->tx_desc.align = 4;
+	tp->tx_desc.vlan_tag = r8152_tx_vlan_tag;
+	tp->desc_ops.tx_csum = r8152_tx_csum;
+	tp->desc_ops.tx_len = r8152_tx_len;
+
+	return 0;
+}
+
+static int r8157_desc_init(struct r8152 *tp)
+{
+	tp->rx_desc.size = sizeof(struct rx_desc_v2);
+	tp->rx_desc.align = 16;
+	tp->rx_desc.vlan_tag = r8157_rx_vlan_tag;
+	tp->desc_ops.rx_csum = r8157_rx_csum;
+	tp->desc_ops.rx_len = r8157_rx_len;
+	tp->tx_desc.size = sizeof(struct tx_desc_v2);
+	tp->tx_desc.align = 16;
+	tp->tx_desc.vlan_tag = r8152_tx_vlan_tag;
+	tp->desc_ops.tx_csum = r8157_tx_csum;
+	tp->desc_ops.tx_len = r8157_tx_len;
+
+	return 0;
+}
+
 static int rtl_ops_init(struct r8152 *tp)
 {
 	struct rtl_ops *ops = &tp->rtl_ops;
@@ -9023,6 +9551,7 @@ static int rtl_ops_init(struct r8152 *tp)
 		tp->rx_buf_sz		= 16 * 1024;
 		tp->eee_en		= true;
 		tp->eee_adv		= MDIO_EEE_100TX;
+		r8152_desc_init(tp);
 		break;
 
 	case RTL_VER_03:
@@ -9047,6 +9576,7 @@ static int rtl_ops_init(struct r8152 *tp)
 			tp->rx_buf_sz	= 32 * 1024;
 		tp->eee_en		= true;
 		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
+		r8152_desc_init(tp);
 		break;
 
 	case RTL_VER_08:
@@ -9066,6 +9596,7 @@ static int rtl_ops_init(struct r8152 *tp)
 		tp->rx_buf_sz		= 32 * 1024;
 		tp->eee_en		= true;
 		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
+		r8152_desc_init(tp);
 		break;
 
 	case RTL_VER_11:
@@ -9088,6 +9619,7 @@ static int rtl_ops_init(struct r8152 *tp)
 		ops->change_mtu		= rtl8156_change_mtu;
 		tp->rx_buf_sz		= 48 * 1024;
 		tp->support_2500full	= 1;
+		r8152_desc_init(tp);
 		break;
 
 	case RTL_VER_12:
@@ -9098,8 +9630,8 @@ static int rtl_ops_init(struct r8152 *tp)
 		tp->eee_en		= true;
 		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
 		tp->eee_adv2		= MDIO_EEE_2_5GT;
-		ops->init		= r8156b_init;
-		ops->enable		= rtl8156b_enable;
+		ops->init		= r8156_init;
+		ops->enable		= rtl8156_enable;
 		ops->disable		= rtl8153_disable;
 		ops->up			= rtl8156_up;
 		ops->down		= rtl8156_down;
@@ -9111,6 +9643,7 @@ static int rtl_ops_init(struct r8152 *tp)
 		ops->autosuspend_en	= rtl8156_runtime_enable;
 		ops->change_mtu		= rtl8156_change_mtu;
 		tp->rx_buf_sz		= 48 * 1024;
+		r8152_desc_init(tp);
 		break;
 
 	case RTL_VER_14:
@@ -9129,6 +9662,29 @@ static int rtl_ops_init(struct r8152 *tp)
 		tp->rx_buf_sz		= 32 * 1024;
 		tp->eee_en		= true;
 		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
+		r8152_desc_init(tp);
+		break;
+
+	case RTL_VER_16:
+		tp->eee_en		= true;
+		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
+		tp->eee_adv2		= MDIO_EEE_2_5GT | MDIO_EEE_5GT;
+		ops->init		= r8156_init;
+		ops->enable		= rtl8156_enable;
+		ops->disable		= rtl8153_disable;
+		ops->up			= rtl8156_up;
+		ops->down		= rtl8156_down;
+		ops->unload		= rtl8153_unload;
+		ops->eee_get		= r8153_get_eee;
+		ops->eee_set		= r8152_set_eee;
+		ops->in_nway		= rtl8153_in_nway;
+		ops->hw_phy_cfg		= r8157_hw_phy_cfg;
+		ops->autosuspend_en	= rtl8157_runtime_enable;
+		ops->change_mtu		= rtl8156_change_mtu;
+		tp->rx_buf_sz		= 32 * 1024;
+		tp->support_2500full	= 1;
+		tp->support_5000full	= 1;
+		r8157_desc_init(tp);
 		break;
 
 	default:
@@ -9281,6 +9837,9 @@ static u8 __rtl_get_hw_ver(struct usb_device *udev)
 	case 0x7420:
 		version = RTL_VER_15;
 		break;
+	case 0x1030:
+		version = RTL_VER_16;
+		break;
 	default:
 		version = RTL_VER_UNKNOWN;
 		dev_info(&udev->dev, "Unknown version 0x%04x\n", ocp_data);
@@ -9432,6 +9991,7 @@ static int rtl8152_probe_once(struct usb_interface *intf,
 	case RTL_VER_12:
 	case RTL_VER_13:
 	case RTL_VER_15:
+	case RTL_VER_16:
 		netdev->max_mtu = size_to_mtu(16 * 1024);
 		break;
 	case RTL_VER_01:
@@ -9591,6 +10151,7 @@ static const struct usb_device_id rtl8152_table[] = {
 	{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8153) },
 	{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8155) },
 	{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8156) },
+	{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8157) },
 
 	/* Microsoft */
 	{ USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab) },

-- 
2.47.3


^ permalink raw reply related

* [PATCH net-next v6 1/2] r8152: Add support for 5Gbit Link Speeds and EEE
From: Birger Koblitz @ 2026-04-02  8:28 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Birger Koblitz
In-Reply-To: <20260402-rtl8157_next-v6-0-a9b77c0931ef@birger-koblitz.de>

The RTL8157 supports 5GBit Link speeds. Add support for this speed
in the setup and setting/getting through ethtool. Also add 5GBit EEE.
Add functionality for setup and ethtool get/set methods.

Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
 drivers/net/usb/r8152.c | 100 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 82 insertions(+), 18 deletions(-)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 8747c55e0a48433e2300c151af2ac36e839a3929..15527f3cb5478df3450d5e53b5a277fca1460e44 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -604,6 +604,7 @@ enum spd_duplex {
 	FORCE_100M_FULL,
 	FORCE_1000M_FULL,
 	NWAY_2500M_FULL,
+	NWAY_5000M_FULL,
 };
 
 /* OCP_ALDPS_CONFIG */
@@ -725,6 +726,7 @@ enum spd_duplex {
 #define BP4_SUPER_ONLY		0x1578	/* RTL_VER_04 only */
 
 enum rtl_register_content {
+	_5000bps	= BIT(12),
 	_2500bps	= BIT(10),
 	_1250bps	= BIT(9),
 	_500bps		= BIT(8),
@@ -738,6 +740,7 @@ enum rtl_register_content {
 };
 
 #define is_speed_2500(_speed)	(((_speed) & (_2500bps | LINK_STATUS)) == (_2500bps | LINK_STATUS))
+#define is_speed_5000(_speed)	(((_speed) & (_5000bps | LINK_STATUS)) == (_5000bps | LINK_STATUS))
 #define is_flow_control(_speed)	(((_speed) & (_tx_flow | _rx_flow)) == (_tx_flow | _rx_flow))
 
 #define RTL8152_MAX_TX		4
@@ -944,6 +947,7 @@ struct r8152 {
 	unsigned int pipe_in, pipe_out, pipe_intr, pipe_ctrl_in, pipe_ctrl_out;
 
 	u32 support_2500full:1;
+	u32 support_5000full:1;
 	u32 lenovo_macpassthru:1;
 	u32 dell_tb_rx_agg_bug:1;
 	u16 ocp_base;
@@ -1194,6 +1198,7 @@ enum tx_csum_stat {
 #define RTL_ADVERTISED_1000_HALF		BIT(4)
 #define RTL_ADVERTISED_1000_FULL		BIT(5)
 #define RTL_ADVERTISED_2500_FULL		BIT(6)
+#define RTL_ADVERTISED_5000_FULL		BIT(7)
 
 /* 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.
@@ -5398,12 +5403,23 @@ static void r8153_eee_en(struct r8152 *tp, bool enable)
 
 static void r8156_eee_en(struct r8152 *tp, bool enable)
 {
+	u16 config;
+
 	r8153_eee_en(tp, enable);
 
+	config = ocp_reg_read(tp, OCP_EEE_ADV2);
+
 	if (enable && (tp->eee_adv2 & MDIO_EEE_2_5GT))
-		ocp_reg_set_bits(tp, OCP_EEE_ADV2, MDIO_EEE_2_5GT);
+		config |= MDIO_EEE_2_5GT;
 	else
-		ocp_reg_clr_bits(tp, OCP_EEE_ADV2, MDIO_EEE_2_5GT);
+		config &= ~MDIO_EEE_2_5GT;
+
+	if (enable && (tp->eee_adv2 & MDIO_EEE_5GT))
+		config |= MDIO_EEE_5GT;
+	else
+		config &= ~MDIO_EEE_5GT;
+
+	ocp_reg_write(tp, OCP_EEE_ADV2, config);
 }
 
 static void rtl_eee_enable(struct r8152 *tp, bool enable)
@@ -6167,9 +6183,13 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
 
 			if (tp->support_2500full)
 				support |= RTL_ADVERTISED_2500_FULL;
+
+			if (tp->support_5000full)
+				support |= RTL_ADVERTISED_5000_FULL;
 		}
 
-		if (!(advertising & support))
+		advertising &= support;
+		if (!advertising)
 			return -EINVAL;
 
 		orig = r8152_mdio_read(tp, MII_ADVERTISE);
@@ -6212,15 +6232,20 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
 				r8152_mdio_write(tp, MII_CTRL1000, new1);
 		}
 
-		if (tp->support_2500full) {
+		if (tp->support_2500full || tp->support_5000full) {
 			orig = ocp_reg_read(tp, OCP_10GBT_CTRL);
-			new1 = orig & ~MDIO_AN_10GBT_CTRL_ADV2_5G;
+			new1 = orig & ~(MDIO_AN_10GBT_CTRL_ADV2_5G | MDIO_AN_10GBT_CTRL_ADV5G);
 
 			if (advertising & RTL_ADVERTISED_2500_FULL) {
 				new1 |= MDIO_AN_10GBT_CTRL_ADV2_5G;
 				tp->ups_info.speed_duplex = NWAY_2500M_FULL;
 			}
 
+			if (advertising & RTL_ADVERTISED_5000_FULL) {
+				new1 |= MDIO_AN_10GBT_CTRL_ADV5G;
+				tp->ups_info.speed_duplex = NWAY_5000M_FULL;
+			}
+
 			if (orig != new1)
 				ocp_reg_write(tp, OCP_10GBT_CTRL, new1);
 		}
@@ -8248,17 +8273,38 @@ int rtl8152_get_link_ksettings(struct net_device *netdev,
 	linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
 			 cmd->link_modes.supported, tp->support_2500full);
 
-	if (tp->support_2500full) {
-		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-				 cmd->link_modes.advertising,
-				 ocp_reg_read(tp, OCP_10GBT_CTRL) & MDIO_AN_10GBT_CTRL_ADV2_5G);
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+			 cmd->link_modes.supported, tp->support_5000full);
+
+	if (tp->support_2500full || tp->support_5000full) {
+		u16 ocp_10gbt_ctrl = ocp_reg_read(tp, OCP_10GBT_CTRL);
+		u16 ocp_10gbt_stat = ocp_reg_read(tp, OCP_10GBT_STAT);
+
+		if (tp->support_2500full) {
+			linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+					 cmd->link_modes.advertising,
+					 ocp_10gbt_ctrl & MDIO_AN_10GBT_CTRL_ADV2_5G);
+
+			linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+					 cmd->link_modes.lp_advertising,
+					 ocp_10gbt_stat & MDIO_AN_10GBT_STAT_LP2_5G);
+
+			if (is_speed_2500(rtl8152_get_speed(tp)))
+				cmd->base.speed = SPEED_2500;
+		}
+
+		if (tp->support_5000full) {
+			linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+					 cmd->link_modes.advertising,
+					 ocp_10gbt_ctrl & MDIO_AN_10GBT_CTRL_ADV5G);
 
-		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-				 cmd->link_modes.lp_advertising,
-				 ocp_reg_read(tp, OCP_10GBT_STAT) & MDIO_AN_10GBT_STAT_LP2_5G);
+			linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+					 cmd->link_modes.lp_advertising,
+					 ocp_10gbt_stat & MDIO_AN_10GBT_STAT_LP5G);
 
-		if (is_speed_2500(rtl8152_get_speed(tp)))
-			cmd->base.speed = SPEED_2500;
+			if (is_speed_5000(rtl8152_get_speed(tp)))
+				cmd->base.speed = SPEED_5000;
+		}
 	}
 
 	mutex_unlock(&tp->control);
@@ -8308,6 +8354,10 @@ static int rtl8152_set_link_ksettings(struct net_device *dev,
 		     cmd->link_modes.advertising))
 		advertising |= RTL_ADVERTISED_2500_FULL;
 
+	if (test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+		     cmd->link_modes.advertising))
+		advertising |= RTL_ADVERTISED_5000_FULL;
+
 	mutex_lock(&tp->control);
 
 	ret = rtl8152_set_speed(tp, cmd->base.autoneg, cmd->base.speed,
@@ -8425,7 +8475,7 @@ static int r8152_set_eee(struct r8152 *tp, struct ethtool_keee *eee)
 
 	tp->eee_en = eee->eee_enabled;
 	tp->eee_adv = val;
-	if (tp->support_2500full) {
+	if (tp->support_2500full || tp->support_5000full) {
 		val = linkmode_to_mii_eee_cap2_t(eee->advertised);
 		tp->eee_adv2 = val;
 	}
@@ -8449,19 +8499,28 @@ static int r8153_get_eee(struct r8152 *tp, struct ethtool_keee *eee)
 	val = ocp_reg_read(tp, OCP_EEE_LPABLE);
 	mii_eee_cap1_mod_linkmode_t(eee->lp_advertised, val);
 
-	if (tp->support_2500full) {
-		linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, eee->supported);
-
+	if (tp->support_2500full || tp->support_5000full) {
 		val = ocp_reg_read(tp, OCP_EEE_ADV2);
 		mii_eee_cap2_mod_linkmode_adv_t(eee->advertised, val);
 
 		val = ocp_reg_read(tp, OCP_EEE_LPABLE2);
 		mii_eee_cap2_mod_linkmode_adv_t(eee->lp_advertised, val);
+	}
+
+	if (tp->support_2500full) {
+		linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, eee->supported);
 
 		if (speed & _2500bps)
 			linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, common);
 	}
 
+	if (tp->support_5000full) {
+		linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, eee->supported);
+
+		if (speed & _5000bps)
+			linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, common);
+	}
+
 	eee->eee_enabled = tp->eee_en;
 
 	if (speed & _1000bps)
@@ -9402,6 +9461,11 @@ static int rtl8152_probe_once(struct usb_interface *intf,
 		} else {
 			tp->speed = SPEED_1000;
 		}
+		if (tp->support_5000full &&
+		    tp->udev->speed >= USB_SPEED_SUPER) {
+			tp->speed = SPEED_5000;
+			tp->advertising |= RTL_ADVERTISED_5000_FULL;
+		}
 		tp->advertising |= RTL_ADVERTISED_1000_FULL;
 	}
 	tp->duplex = DUPLEX_FULL;

-- 
2.47.3


^ permalink raw reply related

* [PATCH net-next v6 0/2] r8152: Add support for the RTL8157 5Gbit USB Ethernet chip
From: Birger Koblitz @ 2026-04-02  8:28 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Birger Koblitz

Add support for the RTL8157, which is a 5GBit USB-Ethernet adapter
chip in the RTL815x family of chips.

The RTL8157 uses a different frame descriptor format, and different
SRAM/ADV access methods, plus offers 5GBit/s Ethernet, so support for these
features is added in addition to chip initialization and configuration.

The module was tested with an OEM RTL8157 USB adapter:
[25758.328238] usb 4-1: new SuperSpeed Plus Gen 2x1 USB device number 2 using xhci_hcd
[25758.345565] usb 4-1: New USB device found, idVendor=0bda, idProduct=8157, bcdDevice=30.00
[25758.345585] usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=7
[25758.345593] usb 4-1: Product: USB 10/100/1G/2.5G/5G LAN
[25758.345599] usb 4-1: Manufacturer: Realtek
[25758.345605] usb 4-1: SerialNumber: 000300E04C68xxxx
[25758.534241] r8152-cfgselector 4-1: reset SuperSpeed Plus Gen 2x1 USB device number 2 using xhci_hcd
[25758.603511] r8152 4-1:1.0: skip request firmware
[25758.653351] r8152 4-1:1.0 eth0: v1.12.13
[25758.689271] r8152 4-1:1.0 enx00e04c68xxxx: renamed from eth0
[25763.271682] r8152 4-1:1.0 enx00e04c68xxxx: carrier on

The RTL8157 adapter was tested against an AQC107 PCIe-card supporting
10GBit/s and an RTL8126 5Gbit PCIe-card supporting 5GBit/s for
performance, link speed and EEE negotiation. Using USB3.2 Gen 1 with
the RTL8157 USB adapter and running iperf3 against the AQC107 PCIe
card resulted in 3.47 Gbits/sec, whereas using USB3.2 Gen2 resulted
in 4.70 Gbits/sec, speeds against the RTL8126-card were the same.

As the code integrates the RTL8157-specific code with existing RTL8156 code
in order to improve code maintainability (instead of adding RTL8157-specific
functions duplicaing most of the RTL8156 code), regression tests were done
with an Edimax EU-4307 V1.0 USB-Ethernet adapter with RTL8156.

The code is based on the out-of-tree r8152 driver published by Realtek under
the GPL.

This patch is on top of linux-next as the code re-uses the 2.5 Gbit EEE
recently added in r8152.c.

Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
Changes in v6:
- Rebased to net-next
- Fixed typos: ocp_10bt -> ocp_10gbt
- Link to v5: https://lore.kernel.org/r/20260331-rtl8157_next-v5-0-deb3095f8380@birger-koblitz.de

Changes in v5:
- Filter advertising in rtl8152_set_speed() to prevent incorrect speeds
- Prevent double USB transfers in rtl8152_get_link_ksettings()
- Make sure OCP_EEE_ADV2 and OCP_EEE_LPABLE2 are read if a device
  supports 5GBit but not 2.5Gbit
- Fix rtl8157_runtime_enable() to follow the behavior of
  rtl8156_runtime_enable()
- Prevent call to r8153_u2p3en in rtl8156_up() for RTL8157
- Fix rtl8157_unload() to disable interrupt mitigation
- Link to v4: https://lore.kernel.org/r/20260324-rtl8157_next-v4-0-034312b12de5@birger-koblitz.de

Changes in v4:
- Fix return type of ocp_adv_read()
- In r8152_tx_csum() use tx_desc
- Use TCPHO_MAX_2 for RTL8157 in rtl8152_features_check()
- Include RTL_VER_12 in RTL8156B chip versions in rtl8156_init()
- Remove inline keyword from rx_agg_align and tx_agg_align
- Link to v3: https://lore.kernel.org/r/20260320-rtl8157_next-v3-0-1aefeca7fda7@birger-koblitz.de

Changes in v3:
- Apply reverse Christmas tree order for declarations
- Use poll_timeout_us for register polling
- In rtl8156_enable(), fix version comparison: tp->version >= RTL_VER_16
- Correct error handling of r8153_phy_status in r8157_hw_phy_cfg()
- Fix use of ocp_word_clr_bits for PLA_MCU_SPDWN_EN register
- Link to v2: https://lore.kernel.org/r/20260317-rtl8157_next-v2-0-10ea1fa488d1@birger-koblitz.de

Changes in v2:
- Fixed missing initialization of ret value in wait_cmd_ready()
- Combine all parts with RTL8157-specific code to avoid undefined functions
- Link to v1: https://lore.kernel.org/r/20260314-rtl8157_next-v1-0-9ba77b428afd@birger-koblitz.de

---
Birger Koblitz (2):
      r8152: Add support for 5Gbit Link Speeds and EEE
      r8152: Add support for the RTL8157 hardware

 drivers/net/usb/r8152.c | 1029 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 827 insertions(+), 202 deletions(-)
---
base-commit: cee10a01e286e88e0949979e91231270ca9fdb8e
change-id: 20260314-rtl8157_next-ae18683eb4fa

Best regards,
-- 
Birger Koblitz <mail@birger-koblitz.de>


^ permalink raw reply

* Re: [PATCH net-next v5 0/2] r8152: Add support for the RTL8157 5Gbit USB Ethernet chip
From: Birger Koblitz @ 2026-04-02  8:27 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
	linux-usb, netdev, linux-kernel, Chih Kai Hsu
In-Reply-To: <20260401203711.7e56ff95@kernel.org>

On 4/2/26 05:37, Jakub Kicinski wrote:
> On Tue, 31 Mar 2026 17:55:52 +0200 Birger Koblitz wrote:
>> Add support for the RTL8157, which is a 5GBit USB-Ethernet adapter
>> chip in the RTL815x family of chips.
>>
>> The RTL8157 uses a different frame descriptor format, and different
>> SRAM/ADV access methods, plus offers 5GBit/s Ethernet, so support for these
>> features is added in addition to chip initialization and configuration.
> 
> This version does not seem to apply to net-next.
> Please make sure you base it on net-next not linux-next.
Sorry about that, I was not aware of the parallel changes. I have rebased
to net-next and will send a v6.

^ permalink raw reply

* [PATCH] USB: serial: option: add Telit Cinterion FN990A MBIM composition
From: Fabio Porcedda @ 2026-04-02  8:27 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Greg Kroah-Hartman, linux-usb, Daniele Palmas, Fabio Porcedda

Add the following Telit Cinterion FN990A MBIM composition:

0x1074: MBIM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (diag) +
        DPL (Data Packet Logging) + adb

T:  Bus=01 Lev=01 Prnt=04 Port=06 Cnt=01 Dev#=  3 Spd=480  MxCh= 0
D:  Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1bc7 ProdID=1074 Rev=05.04
S:  Manufacturer=Telit Wireless Solutions
S:  Product=FN990
S:  SerialNumber=70628d0c
C:  #Ifs= 7 Cfg#= 1 Atr=e0 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim
E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=32ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim
E:  Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option
E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=83(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=85(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=87(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none)
E:  Ad=8f(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms

Signed-off-by: Fabio Porcedda <fabio.porcedda@gmail.com>
---
 drivers/usb/serial/option.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 313612114db9..c71461893d20 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1383,6 +1383,8 @@ static const struct usb_device_id option_ids[] = {
 	  .driver_info = NCTRL(2) | RSVD(3) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff),	/* Telit FN990A (ECM) */
 	  .driver_info = NCTRL(0) | RSVD(1) },
+	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1074, 0xff),	/* Telit FN990A (MBIM) */
+	  .driver_info = NCTRL(5) | RSVD(6) | RSVD(7) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff),	/* Telit FN990A (PCIe) */
 	  .driver_info = RSVD(0) },
 	{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff),	/* Telit FN990A (rmnet + audio) */
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH] usb: core: Fix bandwidth for devices with invalid wBytesPerInterval
From: Xuetao (kirin) @ 2026-04-02  8:26 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb, linux-kernel, caiyadong, stable, stern
In-Reply-To: <2026040232-wrinkly-replace-4fdf@gregkh>



在 2026/4/2 15:10, Greg KH 写道:
> On Thu, Apr 02, 2026 at 02:59:35PM +0800, Xuetao (kirin) wrote:
>>
>>
>> 在 2026/4/2 11:51, Greg KH 写道:
>>> On Thu, Apr 02, 2026 at 10:14:00AM +0800, Tao Xue wrote:
>>>> As specified in Section 4.14.2 of the xHCI Specification, the xHC
>>>> reserves bandwidth for periodic endpoints according to bInterval and
>>>> wBytesPerInterval (Max ESIT Payload).
>>>>
>>>> Some peripherals report an invalid wBytesPerInterval in their device
>>>> descriptor, which is either 0 or smaller than the actual data length
>>>> transmitted. This issue is observed on ASIX AX88179 series USB 3.0
>>>> Ethernet adapters.
>>>>
>>>> These errors may lead to unexpected behavior on certain USB host
>>>> controllers, causing USB peripherals to malfunction.
>>>>
>>>> To address the issue, return max(wBytesPerInterval, max_payload) when
>>>> calculating bandwidth reservation.
>>>>
>>>> Fixes: 9238f25d5d32 ("USB: xhci: properly set endpoint context fields for periodic eps.")
>>>> Cc: <stable@kernel.org>
>>>> Signed-off-by: Tao Xue <xuetao09@huawei.com>
>>>> ---
>>>>    drivers/usb/core/usb.c | 9 ++++++++-
>>>>    1 file changed, 8 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
>>>> index e9a10a33534c..8f2e05a5a015 100644
>>>> --- a/drivers/usb/core/usb.c
>>>> +++ b/drivers/usb/core/usb.c
>>>> @@ -1125,6 +1125,8 @@ EXPORT_SYMBOL_GPL(usb_free_noncoherent);
>>>>    u32 usb_endpoint_max_periodic_payload(struct usb_device *udev,
>>>>    				      const struct usb_host_endpoint *ep)
>>>>    {
>>>> +	u32 max_payload;
>>>> +
>>>>    	if (!usb_endpoint_xfer_isoc(&ep->desc) &&
>>>>    	    !usb_endpoint_xfer_int(&ep->desc))
>>>>    		return 0;
>>>> @@ -1135,7 +1137,12 @@ u32 usb_endpoint_max_periodic_payload(struct usb_device *udev,
>>>>    			return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval);
>>>>    		fallthrough;
>>>>    	case USB_SPEED_SUPER:
>>>> -		return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
>>>> +		max_payload = usb_endpoint_maxp(&ep->desc) * (ep->ss_ep_comp.bMaxBurst + 1);
>>>> +		if (usb_endpoint_xfer_isoc(&ep->desc))
>>>> +			return max_t(u32, max_payload * USB_SS_MULT(ep->ss_ep_comp.bmAttributes),
>>>> +					ep->ss_ep_comp.wBytesPerInterval);
>>>> +		else
>>>> +			return max_t(u32, max_payload, ep->ss_ep_comp.wBytesPerInterval);
>>>
>>> You dropped the conversion from le16 to cpu?  Why?
>>>
>>> thanks,
>>>
>>> greg k-h
>>
>> Hi Greg,
>>
>> Thank you for the review.
>>
>> 1、You're right, that was an oversight. I should keep the le16_to_cpu().
>> Here's the corrected version:
>>
>>      max_payload = usb_endpoint_maxp(&ep->desc) * (ep->ss_ep_comp.bMaxBurst +
>> 1);
>>      if (usb_endpoint_xfer_isoc(&ep->desc))
>>          return max_t(u32, max_payload *
>> USB_SS_MULT(ep->ss_ep_comp.bmAttributes),
>>                          le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval));
>>      else
>>          return max_t(u32, max_payload,
>> le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval));
> 
> That's hard to follow as it is line-wrapped, just fix it up for your
> next version and I'll be glad to review it then.
> 
Thank you for the review.I will submit a new patch later.

>> 2、Following Alan's suggestion in another email, should I check whether
>> wBytesPerInterval is a valid value and handle it in the
>> usb_parse_ss_endpoint_companion() ?
>>
>> However, when parsing the device descriptor, we do not know whether the
>> actual data length transmitted by the peripheral is greater than
>> wBytesPerInterval.
>>
>> Therefore, would it be sufficient to only add a check for whether
>> wBytesPerInterval is 0 in the existing flow, and if it is 0, set
>> wBytesPerInterval to cpu_to_le16(max_tx) by default?
> 
> No, don't override a value given by a device.  Mark the descriptor as
> invalid and fail attaching to the device.
> 
>> For example, modify it in the following way:
>>
>>       if (le16_to_cpu(desc->wBytesPerInterval) > max_tx ||
>> le16_to_cpu(desc->wBytesPerInterval) == 0) {
>>          dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in "
>>                  "config %d interface %d altsetting %d ep %d: "
>>                  "setting to %d\n",
>>                  usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
>>                  le16_to_cpu(desc->wBytesPerInterval),
>>                  cfgno, inum, asnum, ep->desc.bEndpointAddress,
>>                  max_tx);
>>          ep->ss_ep_comp.wBytesPerInterval = cpu_to_le16(max_tx);
>>      }
> 
> There's nothing a user can do with this type of error, and yet the
> kernel is supposed to fix it up?  We should just fail it and tell the
> user then, like we do for other broken descriptors.
> 
> Did you find this issue with a real device, or is this just due to
> fuzzing invalid descriptor values?
> 
> thanks,
> 
> greg k-h

This is not a fuzzing issue.I have encountered this issue on several USB 
3.0 docks (with built-in USB Ethernet adapters), such as UGREEN, 
thinkplus, and Baseus. They all use the ASIX AX88179 USB 3.0 Gigabit 
Ethernet Adapter (VID:0x0B95, PID: 0x1790). I am seeing two scenarios:

int ep:
Scenario 1:
	wMaxPacketSize = 16
	bMaxBurst = 0
	wBytesPerInterval = 8
	tx_data_len = 8/16

Scenario 2:
	wMaxPacketSize = 16
	bMaxBurst = 0
	wBytesPerInterval = 0
	tx_data_len = 8/16

These devices work perfectly on the dwc3 controller, but they 
consistently fail on another USB host controller. When I temporarily 
change wBytesPerInterval to 16, the problem is resolved.

 From the comparison results, different controllers behave differently 
when encountering invalid wBytesPerInterval values in the device 
descriptor.The dwc3 controller is tolerant of this error.



^ permalink raw reply

* Re: [PATCH v2 1/3] usb: usbip: fix integer overflow in usbip_recv_iso()
From: Greg KH @ 2026-04-02  7:52 UTC (permalink / raw)
  To: Nathan Rebello; +Cc: linux-usb, addcontent08, skhan, kyungtae.kim
In-Reply-To: <20260327043153.643-1-nathan.c.rebello@gmail.com>

On Fri, Mar 27, 2026 at 12:31:53AM -0400, Nathan Rebello wrote:
> Hi Kelvin,
> 
> Your series hardens usbip_recv_iso() and usbip_pad_iso() against
> malicious number_of_packets values, but the bad value still lands in
> urb->number_of_packets via usbip_pack_ret_submit() before those
> checks run.
> 
> The patch below validates at the source — in usbip_pack_ret_submit()
> before the overwrite — and checks against the original
> urb->number_of_packets (the actual allocation bound) rather than
> USBIP_MAX_ISO_PACKETS. This is a tighter check because the URB may
> have been allocated for far fewer than 1024 packets.
> 
> This could complement your series as an additional layer, or stand
> alone. Would be glad to rework this however the maintainers see fit —
> whether folded into your series or submitted separately.
> 
> ---
> 
> From: Nathan Rebello <nathan.c.rebello@gmail.com>
> Subject: [PATCH] usbip: vhci: reject RET_SUBMIT with inflated
>  number_of_packets
> 
> When a USB/IP client receives a RET_SUBMIT response,
> usbip_pack_ret_submit() unconditionally overwrites
> urb->number_of_packets from the network PDU. This value is
> subsequently used as the loop bound in usbip_recv_iso() and
> usbip_pad_iso() to iterate over urb->iso_frame_desc[], a flexible
> array whose size was fixed at URB allocation time based on the
> *original* number_of_packets from the CMD_SUBMIT.
> 
> A malicious USB/IP server can set number_of_packets in the response
> to a value larger than what was originally submitted, causing a heap
> out-of-bounds write when usbip_recv_iso() writes to
> urb->iso_frame_desc[i] beyond the allocated region.
> 
> KASAN confirmed this with kernel 7.0.0-rc5:
> 
>   BUG: KASAN: slab-out-of-bounds in usbip_recv_iso+0x46a/0x640
>   Write of size 4 at addr ffff888106351d40 by task vhci_rx/69
> 
>   The buggy address is located 0 bytes to the right of
>    allocated 320-byte region [ffff888106351c00, ffff888106351d40)
> 
> The server side (stub_rx.c) and gadget side (vudc_rx.c) already
> validate number_of_packets in the CMD_SUBMIT path since commits
> c6688ef9f297 ("usbip: fix stub_rx: harden CMD_SUBMIT path to handle
> malicious input") and b78d830f0049 ("usbip: fix vudc_rx: harden
> CMD_SUBMIT path to handle malicious input"). The server side validates
> against USBIP_MAX_ISO_PACKETS because no URB exists yet at that point.
> On the client side we have the original URB, so we can use the tighter
> bound: the response must not exceed the original number_of_packets.
> 
> This mirrors the existing validation of actual_length against
> transfer_buffer_length in usbip_recv_xbuff(), which checks the
> response value against the original allocation size.
> 
> Fix this by checking rpdu->number_of_packets against
> urb->number_of_packets in usbip_pack_ret_submit() before the
> overwrite. On violation, clamp to zero so that usbip_recv_iso() and
> usbip_pad_iso() safely return early.
> 
> Fixes: 0775a9cbc798 ("staging: usbip: vhci extension: modifications to the client side")

This id is not in the kernel tree :(


^ permalink raw reply

* Re: [PATCH] usbip: vhci: reject RET_SUBMIT with inflated number_of_packets
From: Greg KH @ 2026-04-02  7:45 UTC (permalink / raw)
  To: Shuah Khan; +Cc: Nathan Rebello, linux-usb, addcontent08, kyungtae.kim, stable
In-Reply-To: <34da1928-f6e7-43fb-a436-6bc02e262698@linuxfoundation.org>

On Tue, Mar 31, 2026 at 05:17:44PM -0600, Shuah Khan wrote:
> On 3/27/26 00:44, Nathan Rebello wrote:
> > When a USB/IP client receives a RET_SUBMIT response,
> > usbip_pack_ret_submit() unconditionally overwrites
> > urb->number_of_packets from the network PDU. This value is
> > subsequently used as the loop bound in usbip_recv_iso() and
> > usbip_pad_iso() to iterate over urb->iso_frame_desc[], a flexible
> > array whose size was fixed at URB allocation time based on the
> > *original* number_of_packets from the CMD_SUBMIT.
> > 
> > A malicious USB/IP server can set number_of_packets in the response
> > to a value larger than what was originally submitted, causing a heap
> > out-of-bounds write when usbip_recv_iso() writes to
> > urb->iso_frame_desc[i] beyond the allocated region.
> > 
> > KASAN confirmed this with kernel 7.0.0-rc5:
> > 
> >    BUG: KASAN: slab-out-of-bounds in usbip_recv_iso+0x46a/0x640
> >    Write of size 4 at addr ffff888106351d40 by task vhci_rx/69
> > 
> >    The buggy address is located 0 bytes to the right of
> >     allocated 320-byte region [ffff888106351c00, ffff888106351d40)
> > 
> > The server side (stub_rx.c) and gadget side (vudc_rx.c) already
> > validate number_of_packets in the CMD_SUBMIT path since commits
> > c6688ef9f297 ("usbip: fix stub_rx: harden CMD_SUBMIT path to handle
> > malicious input") and b78d830f0049 ("usbip: fix vudc_rx: harden
> > CMD_SUBMIT path to handle malicious input"). The server side validates
> > against USBIP_MAX_ISO_PACKETS because no URB exists yet at that point.
> > On the client side we have the original URB, so we can use the tighter
> > bound: the response must not exceed the original number_of_packets.
> > 
> > This mirrors the existing validation of actual_length against
> > transfer_buffer_length in usbip_recv_xbuff(), which checks the
> > response value against the original allocation size.
> > 
> > Kelvin Mbogo's series ("usb: usbip: fix integer overflow in
> > usbip_recv_iso()", v2) hardens the receive-side functions themselves;
> > this patch complements that work by catching the bad value at its
> > source -- in usbip_pack_ret_submit() before the overwrite -- and
> > using the tighter per-URB allocation bound rather than the global
> > USBIP_MAX_ISO_PACKETS limit.
> > 
> > Fix this by checking rpdu->number_of_packets against
> > urb->number_of_packets in usbip_pack_ret_submit() before the
> > overwrite. On violation, clamp to zero so that usbip_recv_iso() and
> > usbip_pad_iso() safely return early.
> > 
> > Fixes: 0775a9cbc798 ("staging: usbip: vhci extension: modifications to the client side")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Nathan Rebello <nathan.c.rebello@gmail.com>
> > ---
> >   drivers/usb/usbip/usbip_common.c | 13 ++++++++++++-
> >   1 file changed, 12 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
> > --- a/drivers/usb/usbip/usbip_common.c
> > +++ b/drivers/usb/usbip/usbip_common.c
> > @@ -470,7 +470,18 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb,
> >   		urb->status		= rpdu->status;
> >   		urb->actual_length	= rpdu->actual_length;
> >   		urb->start_frame	= rpdu->start_frame;
> > -		urb->number_of_packets = rpdu->number_of_packets;
> > +		/*
> > +		 * The number_of_packets field determines the length of
> > +		 * iso_frame_desc[], which is a flexible array allocated
> > +		 * at URB creation time. A response must never claim more
> > +		 * packets than originally submitted; doing so would cause
> > +		 * an out-of-bounds write in usbip_recv_iso() and
> > +		 * usbip_pad_iso(). Clamp to zero on violation so both
> > +		 * functions safely return early.
> > +		 */
> > +		if (rpdu->number_of_packets < 0 ||
> > +		    rpdu->number_of_packets > urb->number_of_packets)
> > +			rpdu->number_of_packets = 0;
> > +		urb->number_of_packets = rpdu->number_of_packets;
> >   		urb->error_count	= rpdu->error_count;
> >   	}
> >   }
> 
> Look good to me.
> 
> Acked-by: Shuah Khan <skhan@linuxfoundation.org>

This patch is somehow corrupted and can not be applied at all.  Nathan,
how did you generate it?

You can tell something is wrong as it shows you removing, and then
adding, the same line, which is a huge hint something went wrong.

Can you regenerate this against my latest usb-testing branch and resend?

thanks,

greg k-h

^ permalink raw reply

* [PATCH] power: supply: max77759_charger: fix voltage scale (mV -> uV)
From: Amit Sunil Dhamne via B4 Relay @ 2026-04-02  7:15 UTC (permalink / raw)
  To: Amit Sunil Dhamne, Sebastian Reichel, Greg Kroah-Hartman,
	André Draszik
  Cc: linux-kernel, linux-pm, linux-usb, Badhri Jagan Sridharan

From: Amit Sunil Dhamne <amitsd@google.com>

CONSTANT_CHARGE_VOLTAGE_MAX property incorrectly uses mV instead of uV.
Add fix to use uV as per the power-supply subsystem convention.

Also, add a note indicating all the properties use non negative values.
A negative value indicates failure with the appropriate error value. In
that case, it should not be taken as a measurement value or status.

Signed-off-by: Amit Sunil Dhamne <amitsd@google.com>
---
This patch is a follow-up to the series [1]. [1] has been accepted in
the USB subsystem tree (usb-next). However, since Sebastian has additional
feedback [2], I am sending this patch to address it. Please note that this
patch is based out of usb-next branch on usb tree. This patch should be
applied on top of 70d7dd27f6dc ("power: supply: max77759: add charger driver").

[1] https://lore.kernel.org/all/20260325-max77759-charger-v9-0-4486dd297adc@google.com/
[2] https://lore.kernel.org/all/ac2jYUA2F5oQsA2g@venus/#t
---
Output of power supply selftest:
root@google-gs:/data/power_supply# ./test_power_supply_properties.sh
TAP version 13
1..66
 # Testing device max77759-charger
ok 1 max77759-charger.exists
ok 2 max77759-charger.uevent.NAME
ok 3 max77759-charger.sysfs.type
ok 4 max77759-charger.uevent.TYPE
ok 5 max77759-charger.sysfs.usb_type # SKIP
 # Reported: '1' ()
ok 6 max77759-charger.sysfs.online
 # Reported: '1' ()
ok 7 max77759-charger.sysfs.present
 # Reported: 'Charging'
ok 8 max77759-charger.sysfs.status
ok 9 max77759-charger.sysfs.capacity # SKIP
ok 10 max77759-charger.sysfs.capacity_level # SKIP
ok 11 max77759-charger.sysfs.model_name # SKIP
ok 12 max77759-charger.sysfs.manufacturer # SKIP
ok 13 max77759-charger.sysfs.serial_number # SKIP
ok 14 max77759-charger.sysfs.technology # SKIP
ok 15 max77759-charger.sysfs.cycle_count # SKIP
ok 16 max77759-charger.sysfs.scope # SKIP
 # Reported: '3000000' uA (3000 mA)
ok 17 max77759-charger.sysfs.input_current_limit
ok 18 max77759-charger.sysfs.input_voltage_limit # SKIP
ok 19 max77759-charger.sysfs.voltage_now # SKIP
ok 20 max77759-charger.sysfs.voltage_min # SKIP
ok 21 max77759-charger.sysfs.voltage_max # SKIP
ok 22 max77759-charger.sysfs.voltage_min_design # SKIP
ok 23 max77759-charger.sysfs.voltage_max_design # SKIP
ok 24 max77759-charger.sysfs.current_now # SKIP
ok 25 max77759-charger.sysfs.current_max # SKIP
ok 26 max77759-charger.sysfs.charge_now # SKIP
ok 27 max77759-charger.sysfs.charge_full # SKIP
ok 28 max77759-charger.sysfs.charge_full_design # SKIP
ok 29 max77759-charger.sysfs.power_now # SKIP
ok 30 max77759-charger.sysfs.energy_now # SKIP
ok 31 max77759-charger.sysfs.energy_full # SKIP
ok 32 max77759-charger.sysfs.energy_full_design # SKIP
ok 33 max77759-charger.sysfs.energy_full_design # SKIP
 # Testing device tcpm-source-psy-1-0025
ok 34 tcpm-source-psy-1-0025.exists
ok 35 tcpm-source-psy-1-0025.uevent.NAME
ok 36 tcpm-source-psy-1-0025.sysfs.type
ok 37 tcpm-source-psy-1-0025.uevent.TYPE
 # Reported: 'C [PD] PD_PPS PD_SPR_AVS PD_PPS_SPR_AVS' ()
ok 38 tcpm-source-psy-1-0025.sysfs.usb_type
 # Reported: '1' ()
ok 39 tcpm-source-psy-1-0025.sysfs.online
ok 40 tcpm-source-psy-1-0025.sysfs.present # SKIP
ok 41 tcpm-source-psy-1-0025.sysfs.status # SKIP
ok 42 tcpm-source-psy-1-0025.sysfs.capacity # SKIP
ok 43 tcpm-source-psy-1-0025.sysfs.capacity_level # SKIP
ok 44 tcpm-source-psy-1-0025.sysfs.model_name # SKIP
ok 45 tcpm-source-psy-1-0025.sysfs.manufacturer # SKIP
ok 46 tcpm-source-psy-1-0025.sysfs.serial_number # SKIP
ok 47 tcpm-source-psy-1-0025.sysfs.technology # SKIP
ok 48 tcpm-source-psy-1-0025.sysfs.cycle_count # SKIP
ok 49 tcpm-source-psy-1-0025.sysfs.scope # SKIP
ok 50 tcpm-source-psy-1-0025.sysfs.input_current_limit # SKIP
ok 51 tcpm-source-psy-1-0025.sysfs.input_voltage_limit # SKIP
 # Reported: '5000000' uV (5 V)
ok 52 tcpm-source-psy-1-0025.sysfs.voltage_now
 # Reported: '5000000' uV (5 V)
ok 53 tcpm-source-psy-1-0025.sysfs.voltage_min
 # Reported: '5000000' uV (5 V)
ok 54 tcpm-source-psy-1-0025.sysfs.voltage_max
ok 55 tcpm-source-psy-1-0025.sysfs.voltage_min_design # SKIP
ok 56 tcpm-source-psy-1-0025.sysfs.voltage_max_design # SKIP
 # Reported: '3000000' uA (3000 mA)
ok 57 tcpm-source-psy-1-0025.sysfs.current_now
 # Reported: '3000000' uA (3000 mA)
ok 58 tcpm-source-psy-1-0025.sysfs.current_max
ok 59 tcpm-source-psy-1-0025.sysfs.charge_now # SKIP
ok 60 tcpm-source-psy-1-0025.sysfs.charge_full # SKIP
ok 61 tcpm-source-psy-1-0025.sysfs.charge_full_design # SKIP
ok 62 tcpm-source-psy-1-0025.sysfs.power_now # SKIP
ok 63 tcpm-source-psy-1-0025.sysfs.energy_now # SKIP
ok 64 tcpm-source-psy-1-0025.sysfs.energy_full # SKIP
ok 65 tcpm-source-psy-1-0025.sysfs.energy_full_design # SKIP
ok 66 tcpm-source-psy-1-0025.sysfs.energy_full_design # SKIP
 # 47 skipped test(s) detected.  Consider enabling relevant config options to improve coverage.
 # Totals: pass:19 fail:0 xfail:0 xpass:0 skip:47 error:0
---
 drivers/power/supply/max77759_charger.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/drivers/power/supply/max77759_charger.c b/drivers/power/supply/max77759_charger.c
index 9bb414599f16..58594bb78426 100644
--- a/drivers/power/supply/max77759_charger.c
+++ b/drivers/power/supply/max77759_charger.c
@@ -26,7 +26,7 @@
 
 /* Default values for Fast Charge Current & Float Voltage */
 #define CHG_CC_DEFAULT_UA			2266770
-#define CHG_FV_DEFAULT_MV			4300
+#define CHG_FV_DEFAULT_UV			4300000
 
 #define MAX_NUM_RETRIES				3
 #define PSY_WORK_RETRY_DELAY_MS			10
@@ -61,10 +61,10 @@ static const struct linear_range chgcc_limit_ranges[] = {
 	LINEAR_RANGE(200000, 0x3, 0x3C, 66670),
 };
 
-/* Charge Termination Voltage Limits (in mV) */
+/* Charge Termination Voltage Limits (in uV) */
 static const struct linear_range chg_cv_prm_ranges[] = {
-	LINEAR_RANGE(3800, 0x38, 0x39, 100),
-	LINEAR_RANGE(4000, 0x0, 0x32, 10),
+	LINEAR_RANGE(3800000, 0x38, 0x39, 100000),
+	LINEAR_RANGE(4000000, 0x0, 0x32, 10000),
 };
 
 /* USB input current limits (in uA) */
@@ -310,14 +310,14 @@ static int get_float_voltage(struct max77759_charger *chg)
 	return ret ? ret : val;
 }
 
-static int set_float_voltage_limit(struct max77759_charger *chg, u32 fv_mv)
+static int set_float_voltage_limit(struct max77759_charger *chg, u32 fv_uv)
 {
 	u32 regval;
 	bool found;
 
 	linear_range_get_selector_high_array(chg_cv_prm_ranges,
 					     ARRAY_SIZE(chg_cv_prm_ranges),
-					     fv_mv, &regval, &found);
+					     fv_uv, &regval, &found);
 	if (!found)
 		return -EINVAL;
 
@@ -370,6 +370,11 @@ static const enum power_supply_property max77759_charger_props[] = {
 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
 };
 
+/*
+ * Note: None of the properties in this driver support usage of negative values.
+ * If you do see one, it's because the function is reporting an error value and
+ * should not be taken as a measurement value or status.
+ */
 static int max77759_charger_get_property(struct power_supply *psy,
 					 enum power_supply_property psp,
 					 union power_supply_propval *pval)
@@ -557,10 +562,10 @@ static int max77759_charger_init(struct max77759_charger *chg)
 		return ret;
 
 	if (power_supply_get_battery_info(chg->psy, &info)) {
-		fv = CHG_FV_DEFAULT_MV;
+		fv = CHG_FV_DEFAULT_UV;
 		fast_chg_curr = CHG_CC_DEFAULT_UA;
 	} else {
-		fv = info->constant_charge_voltage_max_uv / 1000;
+		fv = info->constant_charge_voltage_max_uv;
 		fast_chg_curr = info->constant_charge_current_max_ua;
 	}
 

---
base-commit: 81ebd43cc0d6d106ce7b6ccbf7b5e40ca7f5503d
change-id: 20260402-fix-psy-max77759-usb-next-15a4f86a08ce

Best regards,
-- 
Amit Sunil Dhamne <amitsd@google.com>



^ permalink raw reply related

* [PATCH v2 3/3] usb: chipidea: otg: not wait vbus drop if use role_switch
From: Xu Yang @ 2026-04-02  7:14 UTC (permalink / raw)
  To: peter.chen, gregkh; +Cc: jun.li, linux-usb, linux-kernel, imx
In-Reply-To: <20260402071457.2516021-1-xu.yang_2@nxp.com>

The usb role switch will update ID and VBUS states at the same time, and
vbus will not drop when execute data role swap in Type-C usecase. So lets
not wait vbus drop in usb role switch case too.

Fixes: e1b5d2bed67c ("usb: chipidea: core: handle usb role switch in a common way")
Cc: stable@vger.kernel.org
Acked-by: Peter Chen <peter.chen@kernel.org>
Reviewed-by: Jun Li <jun.li@nxp.com>
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>

---
Changes in v2:
 - add Ack by tag
---
 drivers/usb/chipidea/otg.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index def933b73a90..fecc7d7e2f0d 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -190,8 +190,8 @@ void ci_handle_id_switch(struct ci_hdrc *ci)
 
 		ci_role_stop(ci);
 
-		if (role == CI_ROLE_GADGET &&
-				IS_ERR(ci->platdata->vbus_extcon.edev))
+		if (role == CI_ROLE_GADGET && !ci->role_switch &&
+		    IS_ERR(ci->platdata->vbus_extcon.edev))
 			/*
 			 * Wait vbus lower than OTGSC_BSV before connecting
 			 * to host. If connecting status is from an external
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 2/3] usb: chipidea: core: allow ci_irq_handler() handle both ID and VBUS change
From: Xu Yang @ 2026-04-02  7:14 UTC (permalink / raw)
  To: peter.chen, gregkh; +Cc: jun.li, linux-usb, linux-kernel, imx
In-Reply-To: <20260402071457.2516021-1-xu.yang_2@nxp.com>

For USB role switch-triggered IRQ, ID and VBUS change come together, for
example when switching from host to device mode. ID indicate a role switch
and VBUS is required to determine whether the device controller can start
operating. Currently, ci_irq_handler() handles only a single event per
invocation. This can cause an issue where switching to device mode results
in the device controller not working at all. Allowing ci_irq_handler() to
handle both ID and VBUS change in one call resolves this issue.

Meanwhile, this change also affects the VBUS event handling logic.
Previously, if an ID event indicated host mode the VBUS IRQ will be
ignored as the device disable BSE when stop() is called. With the new
behavior, if ID and VBUS IRQ occur together and the target mode is host,
the VBUS event is queued and ci_handle_vbus_change() will call
usb_gadget_vbus_connect(), after which USBMODE is switched to device mode,
causing host mode to stop working. To prevent this, an additional check is
added to skip handling VBUS event when current role is not device mode.

Suggested-by: Peter Chen <peter.chen@kernel.org>
Fixes: e1b5d2bed67c ("usb: chipidea: core: handle usb role switch in a common way")
Cc: stable@vger.kernel.org
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>

---
Changes in v2:
 - change ci_irq_handler() instead of assign id_event/b_sess_valid_event
   as true and queue otg work directly
---
 drivers/usb/chipidea/core.c | 45 +++++++++++++++++++------------------
 drivers/usb/chipidea/otg.c  |  3 +++
 2 files changed, 26 insertions(+), 22 deletions(-)

diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 87be716dff3e..7cfabb04a4fb 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -544,30 +544,31 @@ static irqreturn_t ci_irq_handler(int irq, void *data)
 			if (ret == IRQ_HANDLED)
 				return ret;
 		}
-	}
 
-	/*
-	 * Handle id change interrupt, it indicates device/host function
-	 * switch.
-	 */
-	if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
-		ci->id_event = true;
-		/* Clear ID change irq status */
-		hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
-		ci_otg_queue_work(ci);
-		return IRQ_HANDLED;
-	}
+		/*
+		 * Handle id change interrupt, it indicates device/host function
+		 * switch.
+		 */
+		if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
+			ci->id_event = true;
+			/* Clear ID change irq status */
+			hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
+		}
 
-	/*
-	 * Handle vbus change interrupt, it indicates device connection
-	 * and disconnection events.
-	 */
-	if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
-		ci->b_sess_valid_event = true;
-		/* Clear BSV irq */
-		hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
-		ci_otg_queue_work(ci);
-		return IRQ_HANDLED;
+		/*
+		 * Handle vbus change interrupt, it indicates device connection
+		 * and disconnection events.
+		 */
+		if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
+			ci->b_sess_valid_event = true;
+			/* Clear BSV irq */
+			hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
+		}
+
+		if (ci->id_event || ci->b_sess_valid_event) {
+			ci_otg_queue_work(ci);
+			return IRQ_HANDLED;
+		}
 	}
 
 	/* Handle device/host interrupt */
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 647e98f4e351..def933b73a90 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -130,6 +130,9 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
 
 void ci_handle_vbus_change(struct ci_hdrc *ci)
 {
+	if (ci->role != CI_ROLE_GADGET)
+		return;
+
 	if (!ci->is_otg) {
 		if (ci->platdata->flags & CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS)
 			usb_gadget_vbus_connect(&ci->gadget);
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 1/3] usb: chipidea: core: refactor ci_usb_role_switch_set()
From: Xu Yang @ 2026-04-02  7:14 UTC (permalink / raw)
  To: peter.chen, gregkh; +Cc: jun.li, linux-usb, linux-kernel, imx

Current code is redundant, refactor the code, no function change.

Signed-off-by: Xu Yang <xu.yang_2@nxp.com>

---
Changes in v2:
 - new patch
---
 drivers/usb/chipidea/core.c | 29 +++++++----------------------
 1 file changed, 7 insertions(+), 22 deletions(-)

diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index fac11f20cf0a..87be716dff3e 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -618,28 +618,13 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
 	struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
 	struct ci_hdrc_cable *cable;
 
-	if (role == USB_ROLE_HOST) {
-		cable = &ci->platdata->id_extcon;
-		cable->changed = true;
-		cable->connected = true;
-		cable = &ci->platdata->vbus_extcon;
-		cable->changed = true;
-		cable->connected = false;
-	} else if (role == USB_ROLE_DEVICE) {
-		cable = &ci->platdata->id_extcon;
-		cable->changed = true;
-		cable->connected = false;
-		cable = &ci->platdata->vbus_extcon;
-		cable->changed = true;
-		cable->connected = true;
-	} else {
-		cable = &ci->platdata->id_extcon;
-		cable->changed = true;
-		cable->connected = false;
-		cable = &ci->platdata->vbus_extcon;
-		cable->changed = true;
-		cable->connected = false;
-	}
+	cable = &ci->platdata->id_extcon;
+	cable->changed = true;
+	cable->connected = (role == USB_ROLE_HOST);
+
+	cable = &ci->platdata->vbus_extcon;
+	cable->changed = true;
+	cable->connected = (role == USB_ROLE_DEVICE);
 
 	ci_irq(ci);
 	return 0;
-- 
2.34.1


^ permalink raw reply related

* [PATCH] usb: rtl8150: avoid using uninitialized CSCR value
From: Morduan Zang @ 2026-04-02  7:07 UTC (permalink / raw)
  To: Petko Manolov
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, linux-usb, netdev, linux-kernel,
	syzbot+9db6c624635564ad813c, Morduan Zang

Check get_registers() when reading CSCR in set_carrier().
If the control transfer fails, don't use the stack value.

Reported-by: syzbot+9db6c624635564ad813c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9db6c624635564ad813c
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Morduan Zang <zhangdandan@uniontech.com>
---
 drivers/net/usb/rtl8150.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index 4cda0643afb6..7e32726d3e6f 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -722,7 +722,11 @@ static void set_carrier(struct net_device *netdev)
 	rtl8150_t *dev = netdev_priv(netdev);
 	short tmp;
 
-	get_registers(dev, CSCR, 2, &tmp);
+	if (get_registers(dev, CSCR, 2, &tmp) < 0) {
+		netif_carrier_off(netdev);
+		return;
+	}
+
 	if (tmp & CSCR_LINK_STATUS)
 		netif_carrier_on(netdev);
 	else
-- 
2.50.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox