bnxt_en-handle-partially-initialized-auxiliary-devic.patch
selftests-netconsole-only-restore-mac-when-it-change.patch
mpls-fix-null-deref-in-mpls_valid_fib_dump_req-on-co.patch
+usb-core-sysfs-add-lock-to-bos_descriptors_read.patch
+wifi-at76c50x-usb-avoid-length-underflow-in-at76_guess_freq.patch
+usb-core-port-deattach-type-c-connector-on-component-unbind.patch
+usb-musb-omap2430-do-not-put-borrowed-of_node-in-probe.patch
+usb-storage-add-no_ata_1x-quirk-for-longmai-usb-key.patch
+usb-chipidea-fix-usage_count-leak-when-autosuspend_delay-is-negative.patch
+usb-gadget-dummy_hcd-prevent-fifo_req-reuse-during-giveback.patch
+usb-gadget-f_midi-cancel-pending-in-work-before-freeing-the-midi-object.patch
+usb-gadget-printer-fix-infinite-loop-in-printer_read.patch
+usb-gadget-snps-udc-fix-device-name-leak-on-probe-failure.patch
+usb-gadget-fsl-udc-fix-device-name-leak-on-probe-failure.patch
+usb-gadget-fsl-udc-fix-dev_printk-device.patch
+usb-gadget-f_ncm-validate-datagram-bounds-in-ncm_unwrap_ntb.patch
+usb-gadget-f_tcm-synchronize-delayed-set_alt-with-teardown.patch
+usb-gadget-udc-bdc-free-irq-and-drain-func_wake_notify-before-teardown.patch
+usb-gadget-uvc-clamp-send_response-length-to-the-response-buffer.patch
+usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch
+usb-serial-io_edgeport-cap-received-transmit-credits.patch
+usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
+usb-serial-option-add-tdtech-mt5710-cn.patch
+usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch
+usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch
--- /dev/null
+From fc3afb5728e297994863f8a2a01b88a920bbf53e Mon Sep 17 00:00:00 2001
+From: Xu Yang <xu.yang_2@nxp.com>
+Date: Thu, 16 Jul 2026 18:41:26 +0800
+Subject: usb: chipidea: fix usage_count leak when autosuspend_delay is negative
+
+From: Xu Yang <xu.yang_2@nxp.com>
+
+commit fc3afb5728e297994863f8a2a01b88a920bbf53e upstream.
+
+The probe() calls pm_runtime_use_autosuspend(), but remove() does not call
+pm_runtime_dont_use_autosuspend(). This can lead to a usage_count leak if
+autosuspend_delay is set to a negative value.
+
+The pm_runtime_use_autosuspend() also notes that it's important to undo
+this with pm_runtime_dont_use_autosuspend() at driver exit time.
+
+Fixes: 1f874edcb731 ("usb: chipidea: add runtime power management support")
+Cc: stable <stable@kernel.org>
+Assisted-by: Claude:claude-sonnet-4.6
+Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260716104126.2763454-1-xu.yang_2@oss.nxp.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/chipidea/core.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/chipidea/core.c
++++ b/drivers/usb/chipidea/core.c
+@@ -1253,6 +1253,7 @@ static void ci_hdrc_remove(struct platfo
+ usb_role_switch_unregister(ci->role_switch);
+
+ if (ci->supports_runtime_pm) {
++ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
--- /dev/null
+From e0b291fe117964037e0ba382eff4bb365d531c3a Mon Sep 17 00:00:00 2001
+From: "Chia-Lin Kao (AceLan)" <acelan.kao@canonical.com>
+Date: Thu, 11 Jun 2026 15:12:01 +0800
+Subject: usb: core: port: Deattach Type-C connector on component unbind
+
+From: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+
+commit e0b291fe117964037e0ba382eff4bb365d531c3a upstream.
+
+connector_unbind() is the mirror of connector_bind(), but it is missing
+the symmetric call to typec_deattach() that connector_bind() makes via:
+
+ if (port_dev->child)
+ typec_attach(port_dev->connector, &port_dev->child->dev);
+
+When a Thunderbolt dock is unplugged, two teardown paths race:
+
+1. The component framework calls connector_unbind() first, which sets
+ port_dev->connector = NULL without calling typec_deattach(). This
+ leaves port->usb2_dev/port->usb3_dev in struct typec_port pointing at
+ the USB device that is about to be freed.
+
+2. usb_disconnect() then calls typec_deattach(port_dev->connector, ...),
+ but port_dev->connector is already NULL, so the call is a no-op and
+ port->usb2_dev is never cleared.
+
+3. Concurrently, UCSI detects a PD partner-disconnect event and calls
+ typec_unregister_partner(), which reads port->usb2_dev (now a dangling
+ pointer to freed memory) and passes it to typec_partner_unlink_device()
+ -> sysfs_remove_link() -> dev_name() on the freed device, corrupting
+ the typec/UCSI partner state.
+
+This corruption leaves the Thunderbolt tunnel in an inconsistent state on
+the next dock hot-plug. On affected hardware the dock's I225/igc NIC fails
+to enumerate: AER fires a slot reset while the igc driver is still
+initialising ("PCIe link lost"), and the subsequent igc_reset attempt hits
+igc_rd32 on an already-detached device:
+
+ igc 0000:2e:00.0 eth0: PCIe link lost, device now detached
+ igc: Failed to read reg 0x0!
+ WARNING: CPU: 9 PID: 129 at drivers/net/ethernet/intel/igc/igc_main.c:7005
+ igc_rd32+0xa4/0xc0 [igc]
+ Call Trace:
+ igc_disable_pcie_master+0x16/0xa0 [igc]
+ igc_reset_hw_base+0x14/0x170 [igc]
+ igc_reset+0x63/0x110 [igc]
+ igc_io_slot_reset+0x9e/0xd0 [igc]
+ report_slot_reset+0x5d/0xc0
+ pcie_do_recovery+0x209/0x400
+ aer_isr_one_error_type+0x235/0x430
+ aer_isr+0x4e/0x80
+ irq_thread+0xf4/0x1f0
+
+4. UCSI later handles the PD partner-disconnect and calls
+ typec_unregister_partner(), which still sees the stale port->usb2_dev
+ and tries to remove its sysfs link a second time:
+
+ kernfs: can not remove 'typec', no directory
+ WARNING: CPU: 6 PID: 55 at fs/kernfs/dir.c:1706 kernfs_remove_by_name_ns+0xe9/0xf0
+ Workqueue: events ucsi_handle_connector_change [typec_ucsi]
+ Call Trace:
+ sysfs_remove_link+0x19/0x50
+ typec_unregister_partner+0x6e/0x120 [typec]
+ ucsi_unregister_partner+0x107/0x150 [typec_ucsi]
+ ucsi_handle_connector_change+0x3ec/0x490 [typec_ucsi]
+ process_one_work+0x18e/0x3e0
+ worker_thread+0x2e3/0x420
+ kthread+0x10a/0x230
+ ret_from_fork+0x121/0x140
+ ret_from_fork_asm+0x1a/0x30
+
+ With worse timing the same stale pointer is dereferenced after the
+ backing memory is freed, turning the warning into a use-after-free.
+
+Fix the asymmetry: call typec_deattach() before clearing
+port_dev->connector, matching what connector_bind() does on the bind side.
+typec_partner_deattach() is already protected by port->partner_link_lock,
+so it serialises safely with the concurrent typec_unregister_partner() path.
+
+Fixes: 11110783f5ea ("usb: Inform the USB Type-C class about enumerated devices")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260611071201.1235545-1-acelan.kao@canonical.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/port.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/core/port.c
++++ b/drivers/usb/core/port.c
+@@ -740,6 +740,8 @@ static void connector_unbind(struct devi
+
+ sysfs_remove_link(&connector->kobj, dev_name(dev));
+ sysfs_remove_link(&dev->kobj, "connector");
++ if (port_dev->child)
++ typec_deattach(port_dev->connector, &port_dev->child->dev);
+ port_dev->connector = NULL;
+ }
+
--- /dev/null
+From 4e0197fbb0eec588795d5431716a244d9ac8fa93 Mon Sep 17 00:00:00 2001
+From: Griffin Kroah-Hartman <griffin@kroah.com>
+Date: Wed, 15 Jul 2026 16:59:05 +0200
+Subject: usb: core: sysfs: add lock to bos_descriptors_read()
+
+From: Griffin Kroah-Hartman <griffin@kroah.com>
+
+commit 4e0197fbb0eec588795d5431716a244d9ac8fa93 upstream.
+
+Add a lock to the function bos_descriptors_read().
+
+This function accesses udev->bos, which could be simultaneously freed in
+usb_reset_and_verify_device(), a function that is commonly called in
+drivers all over the kernel.
+
+Assisted-by: gkh_clanker_t1000
+Signed-off-by: Griffin Kroah-Hartman <griffin@kroah.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Cc: stable <stable@kernel.org>
+Link: https://patch.msgid.link/20260715-usb_core_patches_3-v1-1-53021f5576fd@kroah.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/sysfs.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/core/sysfs.c
++++ b/drivers/usb/core/sysfs.c
+@@ -899,10 +899,15 @@ bos_descriptors_read(struct file *filp,
+ {
+ struct device *dev = kobj_to_dev(kobj);
+ struct usb_device *udev = to_usb_device(dev);
+- struct usb_host_bos *bos = udev->bos;
++ struct usb_host_bos *bos;
+ struct usb_bos_descriptor *desc;
+ size_t desclen, n = 0;
++ int rc;
+
++ rc = usb_lock_device_interruptible(udev);
++ if (rc < 0)
++ return -EINTR;
++ bos = udev->bos;
+ if (bos) {
+ desc = bos->desc;
+ desclen = le16_to_cpu(desc->wTotalLength);
+@@ -911,6 +916,7 @@ bos_descriptors_read(struct file *filp,
+ memcpy(buf, (void *) desc + off, n);
+ }
+ }
++ usb_unlock_device(udev);
+ return n;
+ }
+ static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
--- /dev/null
+From d5e5cd3654d2b5359a12ea6586120f05b28634ee Mon Sep 17 00:00:00 2001
+From: Jinchao Wang <wangjinchao600@gmail.com>
+Date: Thu, 16 Jul 2026 06:42:17 -0400
+Subject: usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback
+
+From: Jinchao Wang <wangjinchao600@gmail.com>
+
+commit d5e5cd3654d2b5359a12ea6586120f05b28634ee upstream.
+
+dummy_hcd embeds a single shared usb_request (dum->fifo_req) that the
+"emulated single-request FIFO" fast-path in dummy_queue() reuses for
+small IN transfers: it copies the caller's request into it
+(req->req = *_req) and queues it, treating list_empty(&fifo_req.queue)
+as "the slot is free".
+
+The completion side (dummy_timer/transfer/nuke/dummy_dequeue) follows
+the standard pattern: list_del_init(&req->queue) unlinks the request,
+then the lock is dropped and usb_gadget_giveback_request() invokes
+req->complete(). But list_del_init() makes fifo_req.queue look empty
+*before* the completion callback returns, so a concurrent dummy_queue()
+on another CPU sees the slot as free, reuses fifo_req and runs
+req->req = *_req -- overwriting req->complete while dummy_timer is
+mid-calling it. The indirect call then jumps to a clobbered pointer,
+causing a general protection fault / page fault in dummy_timer
+(syzkaller extid faf3a6cf579fc65591ca). The clobbering write is an
+in-bounds memcpy on a live shared object, so KASAN cannot flag it.
+
+Add a fifo_req_busy bit covering the shared request's whole lifetime:
+set it in dummy_queue() when the FIFO fast-path takes fifo_req (making
+it the fast-path guard, replacing the list_empty(&fifo_req.queue)
+test), and clear it after the completion callback has returned, via a
+dummy_giveback() helper used at all four gadget-request giveback
+sites. The shared slot can no longer be reused until its completion
+callback has finished.
+
+Reported-by: syzbot+faf3a6cf579fc65591ca@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=faf3a6cf579fc65591ca
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Jinchao Wang <wangjinchao600@gmail.com>
+Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
+Link: https://patch.msgid.link/5db8bba5b3499a86cd2e776f9918126b68b2508b.1784198306.git.wangjinchao600@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/dummy_hcd.c | 40 ++++++++++++++++++++++++-------------
+ 1 file changed, 27 insertions(+), 13 deletions(-)
+
+--- a/drivers/usb/gadget/udc/dummy_hcd.c
++++ b/drivers/usb/gadget/udc/dummy_hcd.c
+@@ -278,6 +278,7 @@ struct dummy {
+ unsigned ints_enabled:1;
+ unsigned udc_suspended:1;
+ unsigned pullup:1;
++ unsigned fifo_req_busy:1;
+
+ /*
+ * HOST side support
+@@ -329,6 +330,26 @@ static inline struct dummy *gadget_dev_t
+
+ /* DEVICE/GADGET SIDE UTILITY ROUTINES */
+
++/*
++ * Give back a gadget request with dum->lock dropped around the callback.
++ * If @req is the shared fifo_req, clear fifo_req_busy afterward: the flag
++ * was set in dummy_queue() when the shared request was taken and must stay
++ * set until its completion callback has returned; list_del_init() alone
++ * makes the request look idle while the callback is still running.
++ * Caller holds dum->lock and has already done list_del_init() + status.
++ */
++static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep,
++ struct dummy_request *req)
++{
++ bool fifo = req == &dum->fifo_req;
++
++ spin_unlock(&dum->lock);
++ usb_gadget_giveback_request(_ep, &req->req);
++ spin_lock(&dum->lock);
++ if (fifo)
++ dum->fifo_req_busy = 0;
++}
++
+ /* called with spinlock held */
+ static void nuke(struct dummy *dum, struct dummy_ep *ep)
+ {
+@@ -339,9 +360,7 @@ static void nuke(struct dummy *dum, stru
+ list_del_init(&req->queue);
+ req->req.status = -ESHUTDOWN;
+
+- spin_unlock(&dum->lock);
+- usb_gadget_giveback_request(&ep->ep, &req->req);
+- spin_lock(&dum->lock);
++ dummy_giveback(dum, &ep->ep, req);
+ }
+ }
+
+@@ -728,10 +747,11 @@ static int dummy_queue(struct usb_ep *_e
+
+ /* implement an emulated single-request FIFO */
+ if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
+- list_empty(&dum->fifo_req.queue) &&
++ !dum->fifo_req_busy &&
+ list_empty(&ep->queue) &&
+ _req->length <= FIFO_SIZE) {
+ req = &dum->fifo_req;
++ dum->fifo_req_busy = 1;
+ req->req = *_req;
+ req->req.buf = dum->fifo_buf;
+ memcpy(dum->fifo_buf, _req->buf, _req->length);
+@@ -785,9 +805,7 @@ static int dummy_dequeue(struct usb_ep *
+ dev_dbg(udc_dev(dum),
+ "dequeued req %p from %s, len %d buf %p\n",
+ req, _ep->name, _req->length, _req->buf);
+- spin_unlock(&dum->lock);
+- usb_gadget_giveback_request(_ep, _req);
+- spin_lock(&dum->lock);
++ dummy_giveback(dum, _ep, req);
+ }
+ spin_unlock_irqrestore(&dum->lock, flags);
+ return retval;
+@@ -1523,9 +1541,7 @@ top:
+ if (req->req.status != -EINPROGRESS) {
+ list_del_init(&req->queue);
+
+- spin_unlock(&dum->lock);
+- usb_gadget_giveback_request(&ep->ep, &req->req);
+- spin_lock(&dum->lock);
++ dummy_giveback(dum, &ep->ep, req);
+
+ /* requests might have been unlinked... */
+ rescan = 1;
+@@ -1910,9 +1926,7 @@ restart:
+ dev_dbg(udc_dev(dum), "stale req = %p\n",
+ req);
+
+- spin_unlock(&dum->lock);
+- usb_gadget_giveback_request(&ep->ep, &req->req);
+- spin_lock(&dum->lock);
++ dummy_giveback(dum, &ep->ep, req);
+ ep->already_seen = 0;
+ goto restart;
+ }
--- /dev/null
+From 5650c18d93a1db7e27cb5a40b394747eb4686d5b Mon Sep 17 00:00:00 2001
+From: Fan Wu <fanwu01@zju.edu.cn>
+Date: Thu, 9 Jul 2026 15:07:17 +0000
+Subject: usb: gadget: f_midi: cancel pending IN work before freeing the midi object
+
+From: Fan Wu <fanwu01@zju.edu.cn>
+
+commit 5650c18d93a1db7e27cb5a40b394747eb4686d5b upstream.
+
+The f_midi driver embeds a work item (midi->work) whose handler,
+f_midi_in_work(), dereferences the enclosing struct f_midi through
+container_of(). This work is armed from two sites: f_midi_complete(),
+on a normal IN-endpoint completion, and f_midi_in_trigger(), on an ALSA
+rawmidi output-stream start.
+
+Neither f_midi_disable() nor f_midi_unbind() cancels midi->work.
+f_midi_disable() only disables the endpoints and drains the in_req_fifo;
+it does not synchronize the work item, and the sound card is released
+asynchronously to the final free of the midi object.
+
+The midi object is reference-counted (midi->free_ref) and is freed in
+f_midi_free() only once both the usb_function reference and the rawmidi
+private_data reference have been dropped. In f_midi_unbind(),
+f_midi_disable() runs before the sound card is released, so while the
+USB endpoints are already disabled the rawmidi device is still usable by
+an open substream. A concurrent userspace write on such a substream can
+reach f_midi_in_trigger() and queue midi->work again after
+f_midi_disable() has returned. A work item armed this way may still be
+pending when the last reference drops and f_midi_free() proceeds to
+kfree(midi), letting f_midi_in_work() dereference the struct after it
+has been freed, a use-after-free.
+
+For this reason cancelling midi->work in f_midi_disable() would not be
+sufficient: the ALSA trigger path can rearm the work after disable()
+returns. Cancelling at the refcount-zero free site is the boundary
+after which neither arming source can survive, because by then both
+references that keep the midi object alive have been dropped: the USB
+endpoints are already disabled and the rawmidi device has been released.
+
+Fix this by calling cancel_work_sync(&midi->work) in the refcount-zero
+block of f_midi_free(), before the embedded work_struct is freed along
+with the rest of the structure. opts->lock is a sleeping mutex, so
+calling cancel_work_sync() under it is permitted, and the handler takes
+midi->transmit_lock rather than opts->lock, so no self-deadlock can
+occur while it waits for a running instance of the work to finish.
+
+This issue was found by an in-house static analysis tool.
+
+Fixes: 8653d71ce3763 ("usb/gadget: f_midi: Replace tasklet with work")
+Cc: stable <stable@kernel.org>
+Assisted-by: Codex:gpt-5.5
+Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
+Link: https://patch.msgid.link/20260709150717.399083-1-fanwu01@zju.edu.cn
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_midi.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/gadget/function/f_midi.c
++++ b/drivers/usb/gadget/function/f_midi.c
+@@ -1309,6 +1309,7 @@ static void f_midi_free(struct usb_funct
+ opts = container_of(f->fi, struct f_midi_opts, func_inst);
+ mutex_lock(&opts->lock);
+ if (!--midi->free_ref) {
++ cancel_work_sync(&midi->work);
+ kfree(midi->id);
+ kfifo_free(&midi->in_req_fifo);
+ kfree(midi);
--- /dev/null
+From 1febec7e47cdcd01f43fb0211094e3010474666e Mon Sep 17 00:00:00 2001
+From: Sonali Pradhan <sonalipradhan@google.com>
+Date: Fri, 3 Jul 2026 08:37:24 +0000
+Subject: usb: gadget: f_ncm: validate datagram bounds in ncm_unwrap_ntb()
+
+From: Sonali Pradhan <sonalipradhan@google.com>
+
+commit 1febec7e47cdcd01f43fb0211094e3010474666e upstream.
+
+When unpacking host-supplied NTBs, ncm_unwrap_ntb() checks datagram length
+against frame_max but does not verify that the datagram fits within the
+declared block length. Additionally, when decoding multiple NTBs from a
+single socket buffer, subsequent block lengths are not checked against the
+actual remaining buffer data.
+
+With these checks missing, a malicious USB host can specify datagram
+offsets and lengths that point beyond the block, or supply secondary NTB
+headers declaring lengths larger than the buffer. skb_put_data() then
+copies adjacent kernel memory from skb_shared_info into the network skb.
+
+Fix this by verifying that sufficient buffer space remains for the NTB
+header before parsing, handling zero-length block declarations, ensuring
+that block lengths never exceed the remaining buffer space, and verifying
+that each datagram payload stays strictly within the block boundary.
+
+Fixes: 427694cfaafa ("usb: gadget: ncm: Handle decoding of multiple NTB's in unwrap call")
+Fixes: 2b74b0a04d3e ("USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()")
+Cc: stable <stable@kernel.org>
+Assisted-by: Jetski:Gemini-2.5-Pro
+Signed-off-by: Sonali Pradhan <sonalipradhan@google.com>
+Link: https://patch.msgid.link/20260703083725.1903850-1-sonalipradhan@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_ncm.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_ncm.c
++++ b/drivers/usb/gadget/function/f_ncm.c
+@@ -1189,6 +1189,10 @@ static int ncm_unwrap_ntb(struct gether
+ frame_max = ncm_opts->max_segment_size;
+
+ parse_ntb:
++ if (to_process < (int)opts->nth_size) {
++ INFO(port->func.config->cdev, "Packet too small for headers\n");
++ goto err;
++ }
+ tmp = (__le16 *)ntb_ptr;
+
+ /* dwSignature */
+@@ -1209,8 +1213,12 @@ parse_ntb:
+ tmp++; /* skip wSequence */
+
+ block_len = get_ncm(&tmp, opts->block_length);
++ if (block_len == 0)
++ block_len = to_process;
++
+ /* (d)wBlockLength */
+- if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) {
++ if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max) ||
++ (block_len > to_process)) {
+ INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len);
+ goto err;
+ }
+@@ -1273,7 +1281,7 @@ parse_ntb:
+ index = index2;
+ /* wDatagramIndex[0] */
+ if ((index < opts->nth_size) ||
+- (index > block_len - opts->dpe_size)) {
++ (index > block_len)) {
+ INFO(port->func.config->cdev,
+ "Bad index: %#X\n", index);
+ goto err;
+@@ -1285,7 +1293,8 @@ parse_ntb:
+ * ethernet hdr + crc or larger than max frame size
+ */
+ if ((dg_len < 14 + crc_len) ||
+- (dg_len > frame_max)) {
++ (dg_len > frame_max) ||
++ (dg_len > block_len - index)) {
+ INFO(port->func.config->cdev,
+ "Bad dgram length: %#X\n", dg_len);
+ goto err;
+@@ -1310,7 +1319,7 @@ parse_ntb:
+ dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+
+ /* wDatagramIndex[1] */
+- if (index2 > block_len - opts->dpe_size) {
++ if (index2 > block_len) {
+ INFO(port->func.config->cdev,
+ "Bad index: %#X\n", index2);
+ goto err;
--- /dev/null
+From 79e2d75725c85607f8a9d87ae9cace62a19f767d Mon Sep 17 00:00:00 2001
+From: Cen Zhang <zzzccc427@gmail.com>
+Date: Sat, 27 Jun 2026 18:41:53 +0800
+Subject: usb: gadget: f_tcm: synchronize delayed set_alt with teardown
+
+From: Cen Zhang <zzzccc427@gmail.com>
+
+commit 79e2d75725c85607f8a9d87ae9cace62a19f767d upstream.
+
+The f_tcm set_alt() path defers endpoint setup to a work item and
+completes the delayed status response from process context. The delayed
+work uses f_tcm private state and may complete the setup request after
+disconnect or function teardown has already moved on.
+
+Cancel and drain the delayed set_alt work when the function is unbound or
+freed. For disable paths, which are reached under the composite device
+lock, use a small state machine and a non-sleeping cancellation path
+instead of cancel_work_sync(). If the work is already running, mark it
+cancelled and let the worker own the cleanup; otherwise tcm_disable() can
+cancel the queued work and clean up immediately.
+
+Also serialize the final delayed-status completion with the cancellation
+check while holding the composite device lock. This prevents a disconnect
+from clearing delayed_status while the worker is about to complete the
+control request.
+
+Validation reproduced this kernel report:
+BUG: KASAN: slab-use-after-free in tcm_delayed_set_alt+0x6c/0xef0
+
+Call Trace:
+ <TASK>
+ dump_stack_lvl+0x66/0xa0
+ print_report+0xce/0x630
+ ? tcm_delayed_set_alt+0x6c/0xef0
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ ? __virt_addr_valid+0x188/0x320
+ ? tcm_delayed_set_alt+0x6c/0xef0
+ kasan_report+0xe0/0x110
+ ? tcm_delayed_set_alt+0x6c/0xef0
+ tcm_delayed_set_alt+0x6c/0xef0
+ ? __pfx_tcm_delayed_set_alt+0x10/0x10
+ ? process_one_work+0x4cb/0xb90
+ ? rcu_is_watching+0x20/0x50
+ ? tcm_delayed_set_alt+0x9/0xef0
+ process_one_work+0x4d7/0xb90
+ ? __pfx_process_one_work+0x10/0x10
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ ? __list_add_valid_or_report+0x37/0xf0
+ ? __pfx_tcm_delayed_set_alt+0x10/0x10
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ worker_thread+0x2d8/0x570
+ ? __pfx_worker_thread+0x10/0x10
+ kthread+0x1ad/0x1f0
+ ? __pfx_kthread+0x10/0x10
+ ret_from_fork+0x3c9/0x540
+ ? __pfx_ret_from_fork+0x10/0x10
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ ? __switch_to+0x2e9/0x730
+ ? __pfx_kthread+0x10/0x10
+ ret_from_fork_asm+0x1a/0x30
+ </TASK>
+
+Allocated by task 544:
+ kasan_save_stack+0x33/0x60
+ kasan_save_track+0x14/0x30
+ __kasan_kmalloc+0x8f/0xa0
+ tcm_alloc+0x68/0x180
+ usb_get_function+0x36/0x60
+ config_usb_cfg_link+0x125/0x1b0
+ configfs_symlink+0x322/0x890
+ vfs_symlink+0xc2/0x270
+ filename_symlinkat+0x295/0x2f0
+ __x64_sys_symlinkat+0x62/0x90
+ do_syscall_64+0x115/0x6a0
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+Freed by task 661:
+ kasan_save_stack+0x33/0x60
+ kasan_save_track+0x14/0x30
+ kasan_save_free_info+0x3b/0x60
+ __kasan_slab_free+0x43/0x70
+ kfree+0x2f9/0x530
+ config_usb_cfg_unlink+0x173/0x1e0
+ configfs_unlink+0x1fa/0x340
+ vfs_unlink+0x15c/0x510
+ filename_unlinkat+0x2ba/0x450
+ __x64_sys_unlinkat+0x63/0x90
+ do_syscall_64+0x115/0x6a0
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+Fixes: c52661d60f63 ("usb-gadget: Initial merge of target module for UASP + BOT")
+Cc: stable <stable@kernel.org>
+Assisted-by: Codex:gpt-5.5
+Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
+Link: https://patch.msgid.link/20260627104153.3822495-1-zzzccc427@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_tcm.c | 192 ++++++++++++++++++++++++++++++------
+ drivers/usb/gadget/function/tcm.h | 13 ++
+ 2 files changed, 177 insertions(+), 28 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_tcm.c
++++ b/drivers/usb/gadget/function/f_tcm.c
+@@ -2363,31 +2363,158 @@ ep_fail:
+ return -ENOTSUPP;
+ }
+
+-struct guas_setup_wq {
+- struct work_struct work;
+- struct f_uas *fu;
+- unsigned int alt;
+-};
++static void tcm_cleanup_old_alt(struct f_uas *fu)
++{
++ if (fu->flags & USBG_IS_UAS)
++ uasp_cleanup_old_alt(fu);
++ else if (fu->flags & USBG_IS_BOT)
++ bot_cleanup_old_alt(fu);
++ fu->flags = 0;
++}
++
++static void tcm_delayed_set_alt_done(struct f_uas *fu)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
++ fu->delayed_set_alt_cancel = false;
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
++}
++
++static bool tcm_delayed_set_alt_cancelled(struct f_uas *fu)
++{
++ bool cancelled;
++ unsigned long flags;
++
++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
++ cancelled = fu->delayed_set_alt_cancel;
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
++
++ return cancelled;
++}
++
++static bool tcm_complete_delayed_status(struct f_uas *fu)
++{
++ struct usb_composite_dev *cdev = fu->function.config->cdev;
++ struct usb_request *req = cdev->req;
++ unsigned long cdev_flags;
++ bool cancelled;
++ int ret;
++
++ spin_lock_irqsave(&cdev->lock, cdev_flags);
++ spin_lock(&fu->delayed_set_alt_lock);
++ cancelled = fu->delayed_set_alt_cancel;
++ if (!cancelled) {
++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
++ fu->delayed_set_alt_cancel = false;
++ }
++ spin_unlock(&fu->delayed_set_alt_lock);
++
++ if (cancelled) {
++ spin_unlock_irqrestore(&cdev->lock, cdev_flags);
++ return false;
++ }
++
++ if (cdev->delayed_status == 0) {
++ WARN(cdev, "%s: Unexpected call\n", __func__);
++ } else if (--cdev->delayed_status == 0) {
++ req->length = 0;
++ req->context = cdev;
++ ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
++ if (ret == 0) {
++ cdev->setup_pending = true;
++ } else {
++ req->status = 0;
++ req->complete(cdev->gadget->ep0, req);
++ }
++ }
++
++ spin_unlock_irqrestore(&cdev->lock, cdev_flags);
++
++ return true;
++}
++
++static bool tcm_cancel_delayed_set_alt(struct f_uas *fu)
++{
++ bool cleanup = false;
++ bool cancel = false;
++ unsigned long flags;
++
++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
++ switch (fu->delayed_set_alt_state) {
++ case USBG_DELAYED_SET_ALT_IDLE:
++ cleanup = true;
++ break;
++ case USBG_DELAYED_SET_ALT_QUEUED:
++ case USBG_DELAYED_SET_ALT_RUNNING:
++ fu->delayed_set_alt_cancel = true;
++ cancel = true;
++ break;
++ }
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
++
++ if (cancel && cancel_work(&fu->delayed_set_alt)) {
++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
++ if (fu->delayed_set_alt_state == USBG_DELAYED_SET_ALT_QUEUED) {
++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
++ fu->delayed_set_alt_cancel = false;
++ cleanup = true;
++ }
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
++ }
++
++ return cleanup;
++}
++
++static void tcm_cancel_delayed_set_alt_sync(struct f_uas *fu)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
++ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE)
++ fu->delayed_set_alt_cancel = true;
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
++
++ cancel_work_sync(&fu->delayed_set_alt);
++
++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
++ fu->delayed_set_alt_cancel = false;
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
++}
+
+ static void tcm_delayed_set_alt(struct work_struct *wq)
+ {
+- struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq,
+- work);
+- struct f_uas *fu = work->fu;
+- int alt = work->alt;
++ struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt);
++ unsigned long flags;
++ unsigned int alt;
++
++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
++ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_QUEUED) {
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
++ return;
++ }
++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_RUNNING;
++ alt = fu->delayed_alt;
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+
+- kfree(work);
++ tcm_cleanup_old_alt(fu);
+
+- if (fu->flags & USBG_IS_BOT)
+- bot_cleanup_old_alt(fu);
+- if (fu->flags & USBG_IS_UAS)
+- uasp_cleanup_old_alt(fu);
++ if (tcm_delayed_set_alt_cancelled(fu))
++ goto out_done;
+
+ if (alt == USB_G_ALT_INT_BBB)
+ bot_set_alt(fu);
+ else if (alt == USB_G_ALT_INT_UAS)
+ uasp_set_alt(fu);
+- usb_composite_setup_continue(fu->function.config->cdev);
++
++ if (tcm_complete_delayed_status(fu))
++ return;
++
++ tcm_cleanup_old_alt(fu);
++out_done:
++ tcm_delayed_set_alt_done(fu);
+ }
+
+ static int tcm_get_alt(struct usb_function *f, unsigned intf)
+@@ -2413,15 +2540,20 @@ static int tcm_set_alt(struct usb_functi
+ return -EOPNOTSUPP;
+
+ if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) {
+- struct guas_setup_wq *work;
++ unsigned long flags;
++
++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
++ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) {
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock,
++ flags);
++ return -EBUSY;
++ }
++ fu->delayed_alt = alt;
++ fu->delayed_set_alt_cancel = false;
++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_QUEUED;
++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+
+- work = kmalloc_obj(*work, GFP_ATOMIC);
+- if (!work)
+- return -ENOMEM;
+- INIT_WORK(&work->work, tcm_delayed_set_alt);
+- work->fu = fu;
+- work->alt = alt;
+- schedule_work(&work->work);
++ schedule_work(&fu->delayed_set_alt);
+ return USB_GADGET_DELAYED_STATUS;
+ }
+ return -EOPNOTSUPP;
+@@ -2431,11 +2563,8 @@ static void tcm_disable(struct usb_funct
+ {
+ struct f_uas *fu = to_f_uas(f);
+
+- if (fu->flags & USBG_IS_UAS)
+- uasp_cleanup_old_alt(fu);
+- else if (fu->flags & USBG_IS_BOT)
+- bot_cleanup_old_alt(fu);
+- fu->flags = 0;
++ if (tcm_cancel_delayed_set_alt(fu))
++ tcm_cleanup_old_alt(fu);
+ }
+
+ static int tcm_setup(struct usb_function *f,
+@@ -2583,11 +2712,16 @@ static void tcm_free(struct usb_function
+ {
+ struct f_uas *tcm = to_f_uas(f);
+
++ tcm_cancel_delayed_set_alt_sync(tcm);
+ kfree(tcm);
+ }
+
+ static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
+ {
++ struct f_uas *fu = to_f_uas(f);
++
++ tcm_cancel_delayed_set_alt_sync(fu);
++ tcm_cleanup_old_alt(fu);
+ usb_free_all_descriptors(f);
+ }
+
+@@ -2620,6 +2754,8 @@ static struct usb_function *tcm_alloc(st
+ fu->function.disable = tcm_disable;
+ fu->function.free_func = tcm_free;
+ fu->tpg = tpg_instances[i].tpg;
++ INIT_WORK(&fu->delayed_set_alt, tcm_delayed_set_alt);
++ spin_lock_init(&fu->delayed_set_alt_lock);
+
+ hash_init(fu->stream_hash);
+ mutex_unlock(&tpg_instances_lock);
+--- a/drivers/usb/gadget/function/tcm.h
++++ b/drivers/usb/gadget/function/tcm.h
+@@ -3,6 +3,7 @@
+ #define __TARGET_USB_GADGET_H__
+
+ #include <linux/kref.h>
++#include <linux/spinlock.h>
+ /* #include <linux/usb/uas.h> */
+ #include <linux/hashtable.h>
+ #include <linux/usb/composite.h>
+@@ -29,6 +30,12 @@ enum {
+
+ #define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS
+
++enum {
++ USBG_DELAYED_SET_ALT_IDLE = 0,
++ USBG_DELAYED_SET_ALT_QUEUED,
++ USBG_DELAYED_SET_ALT_RUNNING,
++};
++
+ struct tcm_usbg_nexus {
+ struct se_session *tvn_se_sess;
+ };
+@@ -132,6 +139,12 @@ struct f_uas {
+ #define USBG_BOT_CMD_PEND (1 << 4)
+ #define USBG_BOT_WEDGED (1 << 5)
+
++ struct work_struct delayed_set_alt;
++ spinlock_t delayed_set_alt_lock; /* protects delayed_set_alt_* */
++ unsigned int delayed_alt;
++ unsigned int delayed_set_alt_state;
++ bool delayed_set_alt_cancel;
++
+ struct usbg_cdb cmd[USBG_NUM_CMDS];
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
--- /dev/null
+From c4dd150fceab281496acb3a643ae712aacb74864 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Thu, 2 Jul 2026 16:15:35 +0200
+Subject: USB: gadget: fsl-udc: fix dev_printk() device
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Johan Hovold <johan@kernel.org>
+
+commit c4dd150fceab281496acb3a643ae712aacb74864 upstream.
+
+A change replacing custom printk() macros with dev_printk() incorrectly
+used the gadget struct device instead of the controller struct device
+(including for messages printed before the gadget device name has been
+initialised).
+
+Switch to using the controller platform device with dev_printk() so that
+the controller device and driver names are included in log messages as
+expected.
+
+Fixes: 6025f20f16c2 ("usb: gadget: fsl-udc: Replace custom log wrappers by dev_{err,warn,dbg,vdbg}")
+Cc: stable <stable@kernel.org>
+Cc: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Acked-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
+Link: https://patch.msgid.link/20260702141536.90887-4-johan@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/fsl_udc_core.c | 92 +++++++++++++++-------------------
+ drivers/usb/gadget/udc/fsl_usb2_udc.h | 1
+ 2 files changed, 43 insertions(+), 50 deletions(-)
+
+--- a/drivers/usb/gadget/udc/fsl_udc_core.c
++++ b/drivers/usb/gadget/udc/fsl_udc_core.c
+@@ -184,7 +184,7 @@ __acquires(ep->udc->lock)
+ usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
+
+ if (status && (status != -ESHUTDOWN))
+- dev_vdbg(&udc->gadget.dev, "complete %s req %p stat %d len %u/%u\n",
++ dev_vdbg(udc->dev, "complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+@@ -286,7 +286,7 @@ static int dr_controller_setup(struct fs
+ timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+ while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
+ if (time_after(jiffies, timeout)) {
+- dev_err(&udc->gadget.dev, "udc reset timeout!\n");
++ dev_err(udc->dev, "udc reset timeout!\n");
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+@@ -309,7 +309,7 @@ static int dr_controller_setup(struct fs
+ tmp &= USB_EP_LIST_ADDRESS_MASK;
+ fsl_writel(tmp, &dr_regs->endpointlistaddr);
+
+- dev_vdbg(&udc->gadget.dev,
++ dev_vdbg(udc->dev,
+ "vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x\n",
+ udc->ep_qh, (int)tmp,
+ fsl_readl(&dr_regs->endpointlistaddr));
+@@ -500,7 +500,7 @@ static void struct_ep_qh_setup(struct fs
+ tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
+ break;
+ default:
+- dev_vdbg(&udc->gadget.dev, "error ep type is %d\n", ep_type);
++ dev_vdbg(udc->dev, "error ep type is %d\n", ep_type);
+ return;
+ }
+ if (zlt)
+@@ -613,7 +613,7 @@ static int fsl_ep_enable(struct usb_ep *
+ spin_unlock_irqrestore(&udc->lock, flags);
+ retval = 0;
+
+- dev_vdbg(&udc->gadget.dev, "enabled %s (ep%d%s) maxpacket %d\n",
++ dev_vdbg(udc->dev, "enabled %s (ep%d%s) maxpacket %d\n",
+ ep->ep.name, ep->ep.desc->bEndpointAddress & 0x0f,
+ (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+ max);
+@@ -634,13 +634,8 @@ static int fsl_ep_disable(struct usb_ep
+ int ep_num;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+- if (!_ep || !ep->ep.desc) {
+- /*
+- * dev_vdbg(&udc->gadget.dev, "%s not enabled\n",
+- * _ep ? ep->ep.name : NULL);
+- */
++ if (!_ep || !ep->ep.desc)
+ return -EINVAL;
+- }
+
+ /* disable ep on controller */
+ ep_num = ep_index(ep);
+@@ -664,7 +659,7 @@ static int fsl_ep_disable(struct usb_ep
+ ep->stopped = 1;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+- dev_vdbg(&udc->gadget.dev, "disabled %s OK\n", _ep->name);
++ dev_vdbg(udc->dev, "disabled %s OK\n", _ep->name);
+ return 0;
+ }
+
+@@ -724,9 +719,6 @@ static void fsl_queue_td(struct fsl_ep *
+ {
+ u32 temp, bitmask, tmp_stat;
+
+- /* dev_vdbg(&udc->gadget.dev, "QH addr Register 0x%8x\n", dr_regs->endpointlistaddr);
+- dev_vdbg(&udc->gadget.dev, "ep_qh[%d] addr is 0x%8x\n", i, (u32)&(ep->udc->ep_qh[i])); */
+-
+ bitmask = ep_is_in(ep)
+ ? (1 << (ep_index(ep) + 16))
+ : (1 << (ep_index(ep)));
+@@ -813,7 +805,7 @@ static struct ep_td_struct *fsl_build_dt
+ *is_last = 0;
+
+ if ((*is_last) == 0)
+- dev_vdbg(&udc_controller->gadget.dev, "multi-dtd request!\n");
++ dev_vdbg(udc_controller->dev, "multi-dtd request!\n");
+ /* Fill in the transfer size; set active bit */
+ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+
+@@ -825,7 +817,7 @@ static struct ep_td_struct *fsl_build_dt
+
+ mb();
+
+- dev_vdbg(&udc_controller->gadget.dev, "length = %d address= 0x%x\n", *length, (int)*dma);
++ dev_vdbg(udc_controller->dev, "length = %d address= 0x%x\n", *length, (int)*dma);
+
+ return dtd;
+ }
+@@ -876,11 +868,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct
+ /* catch various bogus parameters */
+ if (!_req || !req->req.complete || !req->req.buf
+ || !list_empty(&req->queue)) {
+- dev_vdbg(&udc->gadget.dev, "%s, bad params\n", __func__);
++ dev_vdbg(udc->dev, "%s, bad params\n", __func__);
+ return -EINVAL;
+ }
+ if (unlikely(!ep->ep.desc)) {
+- dev_vdbg(&udc->gadget.dev, "%s, bad ep\n", __func__);
++ dev_vdbg(udc->dev, "%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+ if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
+@@ -1040,7 +1032,7 @@ static int fsl_ep_set_halt(struct usb_ep
+ udc->ep0_dir = 0;
+ }
+ out:
+- dev_vdbg(&udc->gadget.dev, "%s %s halt stat %d\n", ep->ep.name,
++ dev_vdbg(udc->dev, "%s %s halt stat %d\n", ep->ep.name,
+ value ? "set" : "clear", status);
+
+ return status;
+@@ -1109,7 +1101,7 @@ static void fsl_ep_fifo_flush(struct usb
+ /* Wait until flush complete */
+ while (fsl_readl(&dr_regs->endptflush)) {
+ if (time_after(jiffies, timeout)) {
+- dev_err(&udc_controller->gadget.dev,
++ dev_err(udc_controller->dev,
+ "ep flush timeout\n");
+ return;
+ }
+@@ -1182,7 +1174,7 @@ static int fsl_vbus_session(struct usb_g
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+- dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active));
++ dev_vdbg(udc->dev, "VBUS %s\n", str_on_off(is_active));
+ udc->vbus_active = (is_active != 0);
+ if (can_pullup(udc))
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+@@ -1548,7 +1540,7 @@ static void ep0_req_complete(struct fsl_
+ udc->ep0_state = WAIT_FOR_SETUP;
+ break;
+ case WAIT_FOR_SETUP:
+- dev_err(&udc->gadget.dev, "Unexpected ep0 packets\n");
++ dev_err(udc->dev, "Unexpected ep0 packets\n");
+ break;
+ default:
+ ep0stall(udc);
+@@ -1617,7 +1609,7 @@ static int process_ep_req(struct fsl_udc
+ errors = hc32_to_cpu(curr_td->size_ioc_sts);
+ if (errors & DTD_ERROR_MASK) {
+ if (errors & DTD_STATUS_HALTED) {
+- dev_err(&udc->gadget.dev, "dTD error %08x QH=%d\n", errors, pipe);
++ dev_err(udc->dev, "dTD error %08x QH=%d\n", errors, pipe);
+ /* Clear the errors and Halt condition */
+ tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts);
+ tmp &= ~errors;
+@@ -1628,26 +1620,26 @@ static int process_ep_req(struct fsl_udc
+ break;
+ }
+ if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+- dev_vdbg(&udc->gadget.dev, "Transfer overflow\n");
++ dev_vdbg(udc->dev, "Transfer overflow\n");
+ status = -EPROTO;
+ break;
+ } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+- dev_vdbg(&udc->gadget.dev, "ISO error\n");
++ dev_vdbg(udc->dev, "ISO error\n");
+ status = -EILSEQ;
+ break;
+ } else
+- dev_err(&udc->gadget.dev,
++ dev_err(udc->dev,
+ "Unknown error has occurred (0x%x)!\n",
+ errors);
+
+ } else if (hc32_to_cpu(curr_td->size_ioc_sts)
+ & DTD_STATUS_ACTIVE) {
+- dev_vdbg(&udc->gadget.dev, "Request not complete\n");
++ dev_vdbg(udc->dev, "Request not complete\n");
+ status = REQ_UNCOMPLETE;
+ return status;
+ } else if (remaining_length) {
+ if (direction) {
+- dev_vdbg(&udc->gadget.dev,
++ dev_vdbg(udc->dev,
+ "Transmit dTD remaining length not zero\n");
+ status = -EPROTO;
+ break;
+@@ -1655,8 +1647,7 @@ static int process_ep_req(struct fsl_udc
+ break;
+ }
+ } else {
+- dev_vdbg(&udc->gadget.dev,
+- "dTD transmitted successful\n");
++ dev_vdbg(udc->dev, "dTD transmitted successful\n");
+ }
+
+ if (j != curr_req->dtd_count - 1)
+@@ -1699,7 +1690,7 @@ static void dtd_complete_irq(struct fsl_
+
+ /* If the ep is configured */
+ if (!curr_ep->ep.name) {
+- dev_warn(&udc->gadget.dev, "Invalid EP?\n");
++ dev_warn(udc->dev, "Invalid EP?\n");
+ continue;
+ }
+
+@@ -1708,7 +1699,7 @@ static void dtd_complete_irq(struct fsl_
+ queue) {
+ status = process_ep_req(udc, i, curr_req);
+
+- dev_vdbg(&udc->gadget.dev,
++ dev_vdbg(udc->dev,
+ "status of process_ep_req= %d, ep = %d\n",
+ status, ep_num);
+ if (status == REQ_UNCOMPLETE)
+@@ -1829,7 +1820,7 @@ static void reset_irq(struct fsl_udc *ud
+ while (fsl_readl(&dr_regs->endpointprime)) {
+ /* Wait until all endptprime bits cleared */
+ if (time_after(jiffies, timeout)) {
+- dev_err(&udc->gadget.dev, "Timeout for reset\n");
++ dev_err(udc->dev, "Timeout for reset\n");
+ break;
+ }
+ cpu_relax();
+@@ -1839,7 +1830,7 @@ static void reset_irq(struct fsl_udc *ud
+ fsl_writel(0xffffffff, &dr_regs->endptflush);
+
+ if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
+- dev_vdbg(&udc->gadget.dev, "Bus reset\n");
++ dev_vdbg(udc->dev, "Bus reset\n");
+ /* Bus is reseting */
+ udc->bus_reset = 1;
+ /* Reset all the queues, include XD, dTD, EP queue
+@@ -1847,7 +1838,7 @@ static void reset_irq(struct fsl_udc *ud
+ reset_queues(udc, true);
+ udc->usb_state = USB_STATE_DEFAULT;
+ } else {
+- dev_vdbg(&udc->gadget.dev, "Controller reset\n");
++ dev_vdbg(udc->dev, "Controller reset\n");
+ /* initialize usb hw reg except for regs for EP, not
+ * touch usbintr reg */
+ dr_controller_setup(udc);
+@@ -1881,7 +1872,7 @@ static irqreturn_t fsl_udc_irq(int irq,
+ /* Clear notification bits */
+ fsl_writel(irq_src, &dr_regs->usbsts);
+
+- /* dev_vdbg(&udc->gadget.dev, "irq_src [0x%8x]", irq_src); */
++ /* dev_vdbg(udc->dev, "irq_src [0x%8x]", irq_src); */
+
+ /* Need to resume? */
+ if (udc->usb_state == USB_STATE_SUSPENDED)
+@@ -1890,7 +1881,7 @@ static irqreturn_t fsl_udc_irq(int irq,
+
+ /* USB Interrupt */
+ if (irq_src & USB_STS_INT) {
+- dev_vdbg(&udc->gadget.dev, "Packet int\n");
++ dev_vdbg(udc->dev, "Packet int\n");
+ /* Setup package, we only support ep0 as control ep */
+ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
+ tripwire_handler(udc, 0,
+@@ -1919,7 +1910,7 @@ static irqreturn_t fsl_udc_irq(int irq,
+
+ /* Reset Received */
+ if (irq_src & USB_STS_RESET) {
+- dev_vdbg(&udc->gadget.dev, "reset int\n");
++ dev_vdbg(udc->dev, "reset int\n");
+ reset_irq(udc);
+ status = IRQ_HANDLED;
+ }
+@@ -1931,7 +1922,7 @@ static irqreturn_t fsl_udc_irq(int irq,
+ }
+
+ if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+- dev_vdbg(&udc->gadget.dev, "Error IRQ %x\n", irq_src);
++ dev_vdbg(udc->dev, "Error IRQ %x\n", irq_src);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+@@ -1967,7 +1958,7 @@ static int fsl_udc_start(struct usb_gadg
+ udc_controller->transceiver->otg,
+ &udc_controller->gadget);
+ if (retval < 0) {
+- dev_err(&udc_controller->gadget.dev, "can't bind to transceiver\n");
++ dev_err(udc_controller->dev, "can't bind to transceiver\n");
+ udc_controller->driver = NULL;
+ return retval;
+ }
+@@ -2252,7 +2243,7 @@ static int struct_udc_setup(struct fsl_u
+
+ udc->eps = kzalloc_objs(struct fsl_ep, udc->max_ep);
+ if (!udc->eps) {
+- dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n");
++ dev_err(udc->dev, "kmalloc udc endpoint status failed\n");
+ goto eps_alloc_failed;
+ }
+
+@@ -2267,7 +2258,7 @@ static int struct_udc_setup(struct fsl_u
+ udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
+ &udc->ep_qh_dma, GFP_KERNEL);
+ if (!udc->ep_qh) {
+- dev_err(&udc->gadget.dev, "malloc QHs for udc failed\n");
++ dev_err(udc->dev, "malloc QHs for udc failed\n");
+ goto ep_queue_alloc_failed;
+ }
+
+@@ -2278,14 +2269,14 @@ static int struct_udc_setup(struct fsl_u
+ udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
+ struct fsl_req, req);
+ if (!udc->status_req) {
+- dev_err(&udc->gadget.dev, "kzalloc for udc status request failed\n");
++ dev_err(udc->dev, "kzalloc for udc status request failed\n");
+ goto udc_status_alloc_failed;
+ }
+
+ /* allocate a small amount of memory to get valid address */
+ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+ if (!udc->status_req->req.buf) {
+- dev_err(&udc->gadget.dev, "kzalloc for udc request buffer failed\n");
++ dev_err(udc->dev, "kzalloc for udc request buffer failed\n");
+ goto udc_req_buf_alloc_failed;
+ }
+
+@@ -2373,6 +2364,7 @@ static int fsl_udc_probe(struct platform
+ if (udc_controller == NULL)
+ return -ENOMEM;
+
++ udc_controller->dev = &pdev->dev;
+ pdata = dev_get_platdata(&pdev->dev);
+ udc_controller->pdata = pdata;
+ spin_lock_init(&udc_controller->lock);
+@@ -2382,7 +2374,7 @@ static int fsl_udc_probe(struct platform
+ if (pdata->operating_mode == FSL_USB2_DR_OTG) {
+ udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+ if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
+- dev_err(&udc_controller->gadget.dev, "Can't find OTG driver!\n");
++ dev_err(&pdev->dev, "Can't find OTG driver!\n");
+ ret = -ENODEV;
+ goto err_kfree;
+ }
+@@ -2398,7 +2390,7 @@ static int fsl_udc_probe(struct platform
+ if (pdata->operating_mode == FSL_USB2_DR_DEVICE) {
+ if (!request_mem_region(res->start, resource_size(res),
+ driver_name)) {
+- dev_err(&udc_controller->gadget.dev, "request mem region for %s failed\n", pdev->name);
++ dev_err(&pdev->dev, "failed to request mem region\n");
+ ret = -EBUSY;
+ goto err_kfree;
+ }
+@@ -2429,7 +2421,7 @@ static int fsl_udc_probe(struct platform
+ /* Read Device Controller Capability Parameters register */
+ dccparams = fsl_readl(&dr_regs->dccparams);
+ if (!(dccparams & DCCPARAMS_DC)) {
+- dev_err(&udc_controller->gadget.dev, "This SOC doesn't support device role\n");
++ dev_err(&pdev->dev, "This SOC doesn't support device role\n");
+ ret = -ENODEV;
+ goto err_exit;
+ }
+@@ -2447,14 +2439,14 @@ static int fsl_udc_probe(struct platform
+ ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
+ driver_name, udc_controller);
+ if (ret != 0) {
+- dev_err(&udc_controller->gadget.dev, "cannot request irq %d err %d\n",
++ dev_err(&pdev->dev, "cannot request irq %d err %d\n",
+ udc_controller->irq, ret);
+ goto err_exit;
+ }
+
+ /* Initialize the udc structure including QH member and other member */
+ if (struct_udc_setup(udc_controller, pdev)) {
+- dev_err(&udc_controller->gadget.dev, "Can't initialize udc data structure\n");
++ dev_err(&pdev->dev, "Can't initialize udc data structure\n");
+ ret = -ENOMEM;
+ goto err_free_irq;
+ }
+--- a/drivers/usb/gadget/udc/fsl_usb2_udc.h
++++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h
+@@ -470,6 +470,7 @@ struct fsl_ep {
+ #define EP_DIR_OUT 0
+
+ struct fsl_udc {
++ struct device *dev;
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct fsl_usb2_platform_data *pdata;
--- /dev/null
+From 6b874d00c466e73c6448a89856407fe46b2f50e4 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Thu, 2 Jul 2026 16:15:33 +0200
+Subject: USB: gadget: fsl-udc: fix device name leak on probe failure
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 6b874d00c466e73c6448a89856407fe46b2f50e4 upstream.
+
+The gadget device name is set by UDC core when registering the gadget
+and must not be set before to avoid leaking the name in intermediate
+error paths (e.g. on dma pool creation failure).
+
+Fixes: eab35c4e6d95 ("usb: gadget: fsl_udc_core: let udc-core manage gadget->dev")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Link: https://patch.msgid.link/20260702141536.90887-2-johan@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/fsl_udc_core.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/usb/gadget/udc/fsl_udc_core.c
++++ b/drivers/usb/gadget/udc/fsl_udc_core.c
+@@ -2474,7 +2474,6 @@ static int fsl_udc_probe(struct platform
+ udc_controller->gadget.name = driver_name;
+
+ /* Setup gadget.dev and register with kernel */
+- dev_set_name(&udc_controller->gadget.dev, "gadget");
+ udc_controller->gadget.dev.of_node = pdev->dev.of_node;
+
+ if (!IS_ERR_OR_NULL(udc_controller->transceiver))
--- /dev/null
+From c2e819be6a5c7f34344926b4bd7e3dfca58cf48a Mon Sep 17 00:00:00 2001
+From: Melbin K Mathew <mlbnkm1@gmail.com>
+Date: Thu, 9 Jul 2026 21:56:22 +0100
+Subject: usb: gadget: printer: fix infinite loop in printer_read()
+
+From: Melbin K Mathew <mlbnkm1@gmail.com>
+
+commit c2e819be6a5c7f34344926b4bd7e3dfca58cf48a upstream.
+
+printer_read() uses the same variable for the requested copy size and
+the number of bytes actually copied to user space. copy_to_user()
+returns the number of bytes not copied, so when it fails to copy
+anything, the computed copied length becomes zero.
+
+In that case len, buf, current_rx_bytes and current_rx_buf are left
+unchanged. If RX data is available and the user buffer remains
+unwritable, the read loop can repeat indefinitely.
+
+Track the copied length separately and return -EFAULT, or the number of
+bytes already copied, if an iteration makes no progress.
+
+Fixes: b185f01a9ab7 ("usb: gadget: printer: factor out f_printer")
+Cc: stable <stable@kernel.org>
+Reviewed-by: Peter Chen <peter.chen@kernel.org>
+Signed-off-by: Melbin K Mathew <mlbnkm1@gmail.com>
+Link: https://patch.msgid.link/20260709205622.55700-1-mlbnkm1@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_printer.c | 23 ++++++++++++++++++-----
+ 1 file changed, 18 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_printer.c
++++ b/drivers/usb/gadget/function/f_printer.c
+@@ -431,7 +431,7 @@ printer_read(struct file *fd, char __use
+ {
+ struct printer_dev *dev = fd->private_data;
+ unsigned long flags;
+- size_t size;
++ size_t size, not_copied, copied;
+ size_t bytes_copied;
+ struct usb_request *req;
+ /* This is a pointer to the current USB rx request. */
+@@ -524,10 +524,12 @@ printer_read(struct file *fd, char __use
+ else
+ size = len;
+
+- size -= copy_to_user(buf, current_rx_buf, size);
+- bytes_copied += size;
+- len -= size;
+- buf += size;
++ not_copied = copy_to_user(buf, current_rx_buf, size);
++ copied = size - not_copied;
++
++ bytes_copied += copied;
++ len -= copied;
++ buf += copied;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+@@ -542,6 +544,17 @@ printer_read(struct file *fd, char __use
+ if (dev->interface < 0)
+ goto out_disabled;
+
++ if (!copied) {
++ dev->current_rx_req = current_rx_req;
++ dev->current_rx_bytes = current_rx_bytes;
++ dev->current_rx_buf = current_rx_buf;
++ spin_unlock_irqrestore(&dev->lock, flags);
++ mutex_unlock(&dev->lock_printer_io);
++ return bytes_copied ? bytes_copied : -EFAULT;
++ }
++
++ size = copied;
++
+ /* If we not returning all the data left in this RX request
+ * buffer then adjust the amount of data left in the buffer.
+ * Othewise if we are done with this RX request buffer then
--- /dev/null
+From 29a142d3e8b35ebc9e0bcc78f4bc26c9b6a9ac0b Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Thu, 2 Jul 2026 16:15:34 +0200
+Subject: USB: gadget: snps-udc: fix device name leak on probe failure
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 29a142d3e8b35ebc9e0bcc78f4bc26c9b6a9ac0b upstream.
+
+The gadget device name is set by UDC core when registering the gadget
+and must not be set before to avoid leaking the name in intermediate
+error paths (e.g. when detecting an older chip revision).
+
+Fixes: 12ad0fcaf2fb ("usb: gadget: amd5536udc: let udc-core manage gadget->dev")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Link: https://patch.msgid.link/20260702141536.90887-3-johan@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/snps_udc_core.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/usb/gadget/udc/snps_udc_core.c
++++ b/drivers/usb/gadget/udc/snps_udc_core.c
+@@ -3133,7 +3133,6 @@ int udc_probe(struct udc *dev)
+ /* device struct setup */
+ dev->gadget.ops = &udc_ops;
+
+- dev_set_name(&dev->gadget.dev, "gadget");
+ dev->gadget.name = name;
+ dev->gadget.max_speed = USB_SPEED_HIGH;
+
--- /dev/null
+From 0583f2fbf8f86ae3a0ce054f96783dd83e65d9bb Mon Sep 17 00:00:00 2001
+From: Fan Wu <fanwu01@zju.edu.cn>
+Date: Thu, 9 Jul 2026 02:09:04 +0000
+Subject: usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown
+
+From: Fan Wu <fanwu01@zju.edu.cn>
+
+commit 0583f2fbf8f86ae3a0ce054f96783dd83e65d9bb upstream.
+
+The Broadcom BDC UDC driver registers its IRQ handler with
+devm_request_irq() in bdc_udc_init(), so the IRQ is released by devm
+only after bdc_remove() returns. devm releases resources in reverse
+LIFO order, but bdc_remove() runs bdc_udc_exit() and bdc_hw_exit() ->
+bdc_mem_free() manually before returning: bdc_udc_exit() tears down
+individual endpoint objects via bdc_free_ep(), while bdc_hw_exit() ->
+bdc_mem_free() frees and NULLs the DMA-coherent status-report ring
+(bdc->srr.sr_bds) and kfree()s bdc->bdc_ep_array. Both happen while
+the IRQ handler (bdc_udc_interrupt, requested with IRQF_SHARED)
+remains deliverable in the window up to the post-remove devm
+free_irq().
+
+On receipt of a shared interrupt in that window, bdc_udc_interrupt()
+dereferences bdc->srr.sr_bds[bdc->srr.dqp_index] (NULL or freed DMA)
+and dispatches sr_handler callbacks that index into bdc_ep_array,
+causing a NULL-deref or use-after-free.
+
+The same window affects the delayed_work bdc->func_wake_notify, which is
+armed from the IRQ handler via bdc_sr_uspc() -> handle_link_state_change()
+-> schedule_delayed_work() and may self-rearm from its own callback
+bdc_func_wake_timer(). No cancel exists anywhere in the driver, so a
+queued work item that fires after bdc_remove() returns and the bdc
+structure is devm-freed dereferences freed memory.
+
+Replace devm_request_irq() with request_irq() and add an explicit
+free_irq(bdc->irq, bdc) in bdc_remove(). Clear BDC_GIE before
+free_irq() to stop the device from asserting interrupts, then
+free_irq() drains any in-flight handler, then cancel_delayed_work_sync()
+drains the func_wake_notify delayed work. This ordering ensures the
+IRQ handler and delayed work cannot interfere with the subsequent
+endpoint and DMA teardown in bdc_udc_exit() and bdc_hw_exit(). Wire the
+matching free_irq() into the bdc_udc_init() error path so the IRQ is
+released on probe failure, and route the bdc_init_ep() failure through
+err0 instead of returning directly.
+
+This issue was found by an in-house static analysis tool.
+
+Fixes: efed421a94e6 ("usb: gadget: Add UDC driver for Broadcom USB3.0 device controller IP BDC")
+Cc: stable <stable@kernel.org>
+Assisted-by: Codex:gpt-5.5
+Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
+Link: https://patch.msgid.link/20260709020904.502611-1-fanwu01@zju.edu.cn
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/bdc/bdc_core.c | 20 ++++++++++++++++++++
+ drivers/usb/gadget/udc/bdc/bdc_udc.c | 7 ++++---
+ 2 files changed, 24 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
++++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
+@@ -585,9 +585,29 @@ disable_clk:
+ static void bdc_remove(struct platform_device *pdev)
+ {
+ struct bdc *bdc;
++ unsigned long flags;
++ u32 temp;
+
+ bdc = platform_get_drvdata(pdev);
+ dev_dbg(bdc->dev, "%s ()\n", __func__);
++ /*
++ * Disable the device interrupt source before freeing the IRQ:
++ * clear BDC_GIE so the controller stops asserting interrupts,
++ * then free_irq drains any in-flight handler.
++ */
++ spin_lock_irqsave(&bdc->lock, flags);
++ temp = bdc_readl(bdc->regs, BDC_BDCSC);
++ temp &= ~BDC_GIE;
++ bdc_writel(bdc->regs, BDC_BDCSC, temp);
++ spin_unlock_irqrestore(&bdc->lock, flags);
++ free_irq(bdc->irq, bdc);
++ /*
++ * Drain func_wake_notify after free_irq: the IRQ handler arms this
++ * delayed_work via bdc_sr_uspc -> handle_link_state_change ->
++ * schedule_delayed_work (self-rearmed in bdc_func_wake_timer), so
++ * the IRQ must be released first to prevent re-arm after cancel.
++ */
++ cancel_delayed_work_sync(&bdc->func_wake_notify);
+ bdc_udc_exit(bdc);
+ bdc_hw_exit(bdc);
+ bdc_phy_exit(bdc);
+--- a/drivers/usb/gadget/udc/bdc/bdc_udc.c
++++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
+@@ -530,8 +530,8 @@ int bdc_udc_init(struct bdc *bdc)
+
+
+ bdc->gadget.name = BRCM_BDC_NAME;
+- ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt,
+- IRQF_SHARED, BRCM_BDC_NAME, bdc);
++ ret = request_irq(bdc->irq, bdc_udc_interrupt, IRQF_SHARED,
++ BRCM_BDC_NAME, bdc);
+ if (ret) {
+ dev_err(bdc->dev,
+ "failed to request irq #%d %d\n",
+@@ -542,7 +542,7 @@ int bdc_udc_init(struct bdc *bdc)
+ ret = bdc_init_ep(bdc);
+ if (ret) {
+ dev_err(bdc->dev, "bdc init ep fail: %d\n", ret);
+- return ret;
++ goto err0;
+ }
+
+ ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget);
+@@ -571,6 +571,7 @@ int bdc_udc_init(struct bdc *bdc)
+ err1:
+ usb_del_gadget_udc(&bdc->gadget);
+ err0:
++ free_irq(bdc->irq, bdc);
+ bdc_free_ep(bdc);
+
+ return ret;
--- /dev/null
+From b70dc75e85ba968b7b76eebfe5d63000080b875b Mon Sep 17 00:00:00 2001
+From: Muhammad Bilal <meatuni001@gmail.com>
+Date: Tue, 30 Jun 2026 00:50:04 +0500
+Subject: usb: gadget: uvc: clamp SEND_RESPONSE length to the response buffer
+
+From: Muhammad Bilal <meatuni001@gmail.com>
+
+commit b70dc75e85ba968b7b76eebfe5d63000080b875b upstream.
+
+uvc_send_response() builds the UVC control response from a user-supplied
+struct uvc_request_data:
+
+ req->length = min_t(unsigned int, uvc->event_length, data->length);
+ ...
+ memcpy(req->buf, data->data, req->length);
+
+req->length is clamped to uvc->event_length, which is taken from the
+host control request wLength (up to UVC_MAX_REQUEST_SIZE, 64), and to
+data->length, which comes from the UVCIOC_SEND_RESPONSE ioctl and is
+only checked for being negative. The source buffer data->data is only
+60 bytes, so a response with uvc->event_length and data->length both
+greater than 60 makes memcpy() read past the end of data->data.
+
+Clamp req->length to sizeof(data->data) as well.
+
+Fixes: a5eaaa1f33e7 ("usb: gadget: uvc: use capped length value")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
+Link: https://patch.msgid.link/20260629195004.148405-1-meatuni001@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/uvc_v4l2.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/gadget/function/uvc_v4l2.c
++++ b/drivers/usb/gadget/function/uvc_v4l2.c
+@@ -200,6 +200,8 @@ uvc_send_response(struct uvc_device *uvc
+ return usb_ep_set_halt(cdev->gadget->ep0);
+
+ req->length = min_t(unsigned int, uvc->event_length, data->length);
++ if (req->length > sizeof(data->data))
++ req->length = sizeof(data->data);
+ req->zero = data->length < uvc->event_length;
+
+ memcpy(req->buf, data->data, req->length);
--- /dev/null
+From c947360ae63eee1c9eacc030dd6f5a53f717addf Mon Sep 17 00:00:00 2001
+From: Guangshuo Li <lgs201920130244@gmail.com>
+Date: Mon, 13 Jul 2026 19:47:11 +0800
+Subject: usb: musb: omap2430: Do not put borrowed of_node in probe
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+commit c947360ae63eee1c9eacc030dd6f5a53f717addf upstream.
+
+omap2430_probe() stores pdev->dev.of_node in a local np variable. This is
+a borrowed pointer and the probe function does not take a reference to
+it.
+
+The success and error paths nevertheless call of_node_put(np). This drops
+a reference that is owned by the platform device, and can leave
+pdev->dev.of_node with an unbalanced reference count.
+
+Do not put the borrowed platform device node from omap2430_probe().
+References taken for the child MUSB device are handled by the device core,
+and the ctrl-module phandle reference is still released separately.
+
+Fixes: ffbe2feac59b ("usb: musb: omap2430: Fix probe regression for missing resources")
+Cc: stable <stable@kernel.org>
+Reviewed-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
+Link: https://patch.msgid.link/20260713114711.955253-1-lgs201920130244@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/musb/omap2430.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/usb/musb/omap2430.c
++++ b/drivers/usb/musb/omap2430.c
+@@ -454,7 +454,6 @@ static int omap2430_probe(struct platfor
+ dev_err(&pdev->dev, "failed to register musb device\n");
+ goto err_disable_rpm;
+ }
+- of_node_put(np);
+
+ return 0;
+
+@@ -464,7 +463,6 @@ err_put_control_otghs:
+ if (!IS_ERR(glue->control_otghs))
+ put_device(glue->control_otghs);
+ err_put_musb:
+- of_node_put(np);
+ platform_device_put(musb);
+
+ return ret;
--- /dev/null
+From fad0fd120e29041b3e6cdf41bb12e3184fb524a2 Mon Sep 17 00:00:00 2001
+From: Tim Pambor <timpambor@gmail.com>
+Date: Sat, 11 Jul 2026 17:36:30 +0000
+Subject: USB: serial: ftdi_sio: add support for E+H FXA291
+
+From: Tim Pambor <timpambor@gmail.com>
+
+commit fad0fd120e29041b3e6cdf41bb12e3184fb524a2 upstream.
+
+The Commubox FXA291 by Endress+Hauser AG is a USB serial converter
+based on FT232B which is used to communicate with field devices.
+
+It enumerates using the FTDI vendor ID and a custom PID.
+
+usb 1-9: New USB device found, idVendor=0403, idProduct=e510, bcdDevice= 4.00
+usb 1-9: New USB device strings: Mfr=1, Product=2, SerialNumber=0
+usb 1-9: Product: FXA291
+usb 1-9: Manufacturer: Endress+Hauser
+usb 1-9: SerialNumber: 00000000
+ftdi_sio 1-9:1.0: FTDI USB Serial Device converter detected
+usb 1-9: Detected FT232B
+usb 1-9: FTDI USB Serial Device converter now attached to ttyUSB0
+
+Signed-off-by: Tim Pambor <timpambor@gmail.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/serial/ftdi_sio.c | 2 ++
+ drivers/usb/serial/ftdi_sio_ids.h | 5 +++++
+ 2 files changed, 7 insertions(+)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -1073,6 +1073,8 @@ static const struct usb_device_id id_tab
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) },
+ /* Abacus Electrics */
+ { USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) },
++ /* Endress+Hauser AG devices */
++ { USB_DEVICE(FTDI_VID, FTDI_EH_FXA291_PID) },
+ { } /* Terminating entry */
+ };
+
+--- a/drivers/usb/serial/ftdi_sio_ids.h
++++ b/drivers/usb/serial/ftdi_sio_ids.h
+@@ -314,6 +314,11 @@
+ #define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */
+
+ /*
++ * Endress+Hauser AG product ids (FTDI_VID)
++ */
++#define FTDI_EH_FXA291_PID 0xE510
++
++/*
+ * EVER Eco Pro UPS (http://www.ever.com.pl/)
+ */
+
--- /dev/null
+From faaddd811c5099f11a5f52e68a6b31a5898cda4f Mon Sep 17 00:00:00 2001
+From: Sunho Park <shpark061104@gmail.com>
+Date: Tue, 14 Jul 2026 19:42:30 +0900
+Subject: USB: serial: io_edgeport: cap received transmit credits
+
+From: Sunho Park <shpark061104@gmail.com>
+
+commit faaddd811c5099f11a5f52e68a6b31a5898cda4f upstream.
+
+The interrupt-status packet reports transmit credits returned by the
+device. edge_interrupt_callback() adds the 16-bit value to txCredits
+without checking maxTxCredits.
+
+edge_write() uses txCredits minus the software FIFO count as the amount
+of data that fits. Since the FIFO is allocated with maxTxCredits bytes,
+txCredits exceeding maxTxCredits can cause OOB write in ring buffer.
+
+Cap accumulated credits at maxTxCredits. Conforming devices should never
+hit the cap.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable@vger.kernel.org
+Assisted-by: Codex:GPT-5
+Signed-off-by: Sunho Park <shpark061104@gmail.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/serial/io_edgeport.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/io_edgeport.c
++++ b/drivers/usb/serial/io_edgeport.c
+@@ -646,7 +646,8 @@ static void edge_interrupt_callback(stru
+ if (edge_port && edge_port->open) {
+ spin_lock_irqsave(&edge_port->ep_lock,
+ flags);
+- edge_port->txCredits += txCredits;
++ edge_port->txCredits = min(edge_port->txCredits + txCredits,
++ edge_port->maxTxCredits);
+ spin_unlock_irqrestore(&edge_port->ep_lock,
+ flags);
+ dev_dbg(dev, "%s - txcredits for port%d = %d\n",
--- /dev/null
+From 42a97c0480f96a2977e6d51ce512adc780f1ef5d Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Wed, 8 Jul 2026 16:31:35 +0200
+Subject: USB: serial: keyspan_pda: fix data loss on receive throttling
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 42a97c0480f96a2977e6d51ce512adc780f1ef5d upstream.
+
+Killing the interrupt-in urb when the line disciple requests throttling
+may lead to data loss if an ongoing transfer is cancelled.
+
+Instead set a flag to prevent the completion handler from resubmitting
+the urb until the port is unthrottled.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable@vger.kernel.org
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/serial/keyspan_pda.c | 44 +++++++++++++++++++++++++++++++--------
+ 1 file changed, 35 insertions(+), 9 deletions(-)
+
+--- a/drivers/usb/serial/keyspan_pda.c
++++ b/drivers/usb/serial/keyspan_pda.c
+@@ -35,6 +35,8 @@ struct keyspan_pda_private {
+ struct work_struct unthrottle_work;
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
++ bool throttled;
++ bool throttle_req;
+ };
+
+ static int keyspan_pda_write_start(struct usb_serial_port *port);
+@@ -150,6 +152,7 @@ static void keyspan_pda_rx_interrupt(str
+ int retval;
+ int status = urb->status;
+ struct keyspan_pda_private *priv;
++ bool throttled = false;
+ unsigned long flags;
+
+ priv = usb_get_serial_port_data(port);
+@@ -211,16 +214,24 @@ static void keyspan_pda_rx_interrupt(str
+ }
+
+ exit:
+- retval = usb_submit_urb(urb, GFP_ATOMIC);
+- if (retval)
+- dev_err(&port->dev,
+- "%s - usb_submit_urb failed with result %d\n",
+- __func__, retval);
++ spin_lock_irqsave(&port->lock, flags);
++ if (priv->throttle_req) {
++ priv->throttled = true;
++ throttled = true;
++ }
++ spin_unlock_irqrestore(&port->lock, flags);
++
++ if (!throttled) {
++ retval = usb_submit_urb(urb, GFP_ATOMIC);
++ if (retval)
++ dev_err(&port->dev, "failed to resubmit in urb: %d\n", retval);
++ }
+ }
+
+ static void keyspan_pda_rx_throttle(struct tty_struct *tty)
+ {
+ struct usb_serial_port *port = tty->driver_data;
++ struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
+
+ /*
+ * Stop receiving characters. We just turn off the URB request, and
+@@ -230,16 +241,29 @@ static void keyspan_pda_rx_throttle(stru
+ * send an XOFF, although it might make sense to foist that off upon
+ * the device too.
+ */
+- usb_kill_urb(port->interrupt_in_urb);
++ spin_lock_irq(&port->lock);
++ priv->throttle_req = true;
++ spin_unlock_irq(&port->lock);
+ }
+
+ static void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
+ {
+ struct usb_serial_port *port = tty->driver_data;
++ struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
++ bool throttled;
++ int ret;
++
++ spin_lock_irq(&port->lock);
++ throttled = priv->throttled;
++ priv->throttled = false;
++ priv->throttle_req = false;
++ spin_unlock_irq(&port->lock);
+
+- /* just restart the receive interrupt URB */
+- if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
+- dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
++ if (throttled) {
++ ret = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
++ if (ret)
++ dev_err(&port->dev, "failed to submit in urb: %d\n", ret);
++ }
+ }
+
+ static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
+@@ -579,6 +603,8 @@ static int keyspan_pda_open(struct tty_s
+
+ spin_lock_irq(&port->lock);
+ priv->tx_room = rc;
++ priv->throttled = false;
++ priv->throttle_req = false;
+ spin_unlock_irq(&port->lock);
+
+ rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
--- /dev/null
+From 55645e4f3c6022ffb160ad3617d2b624eaa38501 Mon Sep 17 00:00:00 2001
+From: Chukun Pan <amadeus@jmu.edu.cn>
+Date: Wed, 8 Jul 2026 18:00:01 +0800
+Subject: USB: serial: option: add TDTECH MT5710-CN
+
+From: Chukun Pan <amadeus@jmu.edu.cn>
+
+commit 55645e4f3c6022ffb160ad3617d2b624eaa38501 upstream.
+
+Add support for the TDTECH MT5710-CN (5G redcap) module based on the
+Huawei HiSilicon Balong chip.
+
+T: Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=480 MxCh= 0
+D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
+P: Vendor=3466 ProdID=3301 Rev=ff.ff
+S: Manufacturer=TD Tech Ltd.
+S: Product=TDTECH MT571X
+S: SerialNumber=0123456789ABCDEF
+C:* #Ifs= 6 Cfg#= 1 Atr=c0 MxPwr= 0mA
+A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0d Prot=00
+I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=cdc_ncm
+E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms
+I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm
+I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm
+E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=13 Driver=option
+E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=12 Driver=option
+E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=1c Driver=option
+E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=14 Driver=option
+E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+
+Interface: ECM / NCM + DIAG + AT + SERIAL + GPS
+
+Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
+Cc: stable@vger.kernel.org
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/serial/option.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -2497,6 +2497,7 @@ static const struct usb_device_id option
+ .driver_info = RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */
+ .driver_info = RSVD(5) },
++ { USB_DEVICE_INTERFACE_CLASS(0x3466, 0x3301, 0xff) }, /* TDTECH MT5710-CN */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },
--- /dev/null
+From 3b4ca2e01c1dd8c00b675b794732945f460a471b Mon Sep 17 00:00:00 2001
+From: Huang Wei <huangwei@kylinos.cn>
+Date: Thu, 16 Jul 2026 11:33:41 +0800
+Subject: USB: storage: add NO_ATA_1X quirk for Longmai USB Key
+
+From: Huang Wei <huangwei@kylinos.cn>
+
+commit 3b4ca2e01c1dd8c00b675b794732945f460a471b upstream.
+
+The Longmai Technologies USB Key (0x04b4:0xb708) advertises itself as a
+SCSI/Bulk-only mass storage device but does not correctly handle ATA
+pass-through commands. When such a command (ATA_12 or ATA_16) is sent to
+the device it fails to respond and the transfer eventually times out,
+leaving the device unusable.
+
+Add an unusual_devs entry for this device that sets the US_FL_NO_ATA_1X
+flag, so usb-storage short-circuits ATA pass-through commands and returns
+INVALID COMMAND OPERATION CODE (0x20 0x05 0x24 0x00) instead of forwarding
+them to the device.
+
+Information about the device in /sys/kernel/debug/usb/devices:
+
+T: Bus=02 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 12 Spd=480 MxCh= 0
+D: Ver= 2.00 Cls=00(>ifc ) Sub=06 Prot=50 MxPS=64 #Cfgs= 1
+P: Vendor=04b4 ProdID=b708 Rev= 1.00
+S: Manufacturer=Longmai Technologies
+S: Product=USB Key
+C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
+I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
+E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+
+Reported-by: Ai Chao <aichao@kylinos.cn>
+Cc: stable <stable@kernel.org>
+Signed-off-by: Huang Wei <huangwei@kylinos.cn>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Link: https://patch.msgid.link/20260716033341.2830872-1-huangwei@kylinos.cn
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/storage/unusual_devs.h | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/usb/storage/unusual_devs.h
++++ b/drivers/usb/storage/unusual_devs.h
+@@ -395,6 +395,13 @@ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x
+ USB_SC_DEVICE, USB_PR_CB, NULL,
+ US_FL_MAX_SECTORS_MIN),
+
++/* Reported by Ai Chao <aichao@kylinos.cn> */
++UNUSUAL_DEV( 0x04b4, 0xb708, 0x0000, 0xffff,
++ "Longmai Technologies",
++ "USB Key",
++ USB_SC_SCSI, USB_PR_BULK, NULL,
++ US_FL_NO_ATA_1X),
++
+ /*
+ * Reported by Simon Levitt <simon@whattf.com>
+ * This entry needs Sub and Proto fields
--- /dev/null
+From f1aa17f72f9b9589bd724dc826c5b17d164193d1 Mon Sep 17 00:00:00 2001
+From: "Chia-Lin Kao (AceLan)" <acelan.kao@canonical.com>
+Date: Mon, 13 Jul 2026 16:43:22 +0800
+Subject: usb: typec: ucsi: Add duplicate detection to nvidia registration path
+
+From: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+
+commit f1aa17f72f9b9589bd724dc826c5b17d164193d1 upstream.
+
+Extend the duplicate altmode detection to ucsi_register_altmodes_nvidia()
+which is used when a driver provides the update_altmodes() callback.
+
+This ensures all drivers benefit from duplicate detection, whether they
+use the standard registration path or the nvidia path with update_altmodes
+callback.
+
+Without this fix, drivers using the nvidia path (like yoga_c630) would
+still encounter duplicate altmode registration errors from buggy firmware.
+
+Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+Link: https://patch.msgid.link/20260713084323.287516-2-acelan.kao@canonical.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/ucsi/ucsi.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+--- a/drivers/usb/typec/ucsi/ucsi.c
++++ b/drivers/usb/typec/ucsi/ucsi.c
+@@ -675,19 +675,25 @@ ucsi_register_altmodes_nvidia(struct ucs
+
+ /* now register altmodes */
+ for (i = 0; i < max_altmodes; i++) {
+- memset(&desc, 0, sizeof(desc));
+- if (multi_dp) {
+- desc.svid = updated[i].svid;
+- desc.vdo = updated[i].mid;
+- } else {
+- desc.svid = orig[i].svid;
+- desc.vdo = orig[i].mid;
+- }
+- desc.roles = TYPEC_PORT_DRD;
++ struct ucsi_altmode *altmode_array = multi_dp ? updated : orig;
+
+- if (!desc.svid)
++ if (!altmode_array[i].svid)
+ return 0;
+
++ /*
++ * Check for duplicates in current array and already
++ * registered altmodes. Skip if duplicate found.
++ */
++ if (ucsi_altmode_is_duplicate(con, recipient, altmode_array, i,
++ altmode_array[i].svid,
++ altmode_array[i].mid, i))
++ continue;
++
++ memset(&desc, 0, sizeof(desc));
++ desc.svid = altmode_array[i].svid;
++ desc.vdo = altmode_array[i].mid;
++ desc.roles = TYPEC_PORT_DRD;
++
+ ret = ucsi_register_altmode(con, &desc, recipient);
+ if (ret)
+ return ret;
--- /dev/null
+From 67c92c6419ea6dbc5b1f3e9691aecea956e3e81c Mon Sep 17 00:00:00 2001
+From: "Chia-Lin Kao (AceLan)" <acelan.kao@canonical.com>
+Date: Mon, 13 Jul 2026 16:43:21 +0800
+Subject: usb: typec: ucsi: Detect and skip duplicate altmodes from buggy firmware
+
+From: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+
+commit 67c92c6419ea6dbc5b1f3e9691aecea956e3e81c upstream.
+
+Some firmware implementations incorrectly return the same altmode
+multiple times at different offsets when queried via
+UCSI_GET_ALTERNATE_MODES. This causes sysfs duplicate filename errors
+and kernel call traces when the driver attempts to register the same
+altmode twice:
+
+ sysfs: cannot create duplicate filename '/devices/.../typec/port0/port0.0/partner'
+ typec-thunderbolt port0-partner.1: failed to create symlinks
+ typec-thunderbolt port0-partner.1: probe with driver typec-thunderbolt failed with error -17
+
+The matching rules differ by recipient:
+
+ - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug):
+ Two altmodes with identical SVID and VDO are byte-for-byte
+ duplicates and the second has no observable function, so drop it.
+
+ - UCSI_RECIPIENT_SOP (partner):
+ The typec class binds each partner altmode to a port altmode of
+ the same SVID via altmode_match()/device_find_child(), which
+ returns the first port altmode with a matching SVID. If the
+ partner advertises more altmodes for SVID X than the port
+ advertises, the surplus partner altmode(s) collapse onto an
+ already-paired port altmode and trigger the
+ "duplicate filename .../partner" sysfs error during
+ typec_altmode_create_links(). Use the port-side altmode count for
+ SVID X as the authoritative cap and reject any partner altmode
+ that would exceed it. This preserves legitimate multi-Mode
+ partner altmodes (vendor SVIDs that the port really does
+ advertise more than once) while filtering the firmware-generated
+ duplicates that have no port counterpart, and is therefore
+ stricter than a plain SVID+VDO comparison (which still admits the
+ Thunderbolt case where firmware reports the same SVID twice with
+ different VDOs) without being over-broad like a plain SVID match
+ (which would falsely drop legitimate vendor multi-Mode entries).
+
+If a duplicate is detected, skip it and emit a clean warning instead
+of generating a kernel call trace:
+
+ ucsi_acpi USBC000:00: con2: Firmware bug: duplicate partner altmode SVID 0x8087 at offset 1, ignoring.
+ ucsi_acpi USBC000:00: con2: VDO mismatch: 0x8087a043 vs 0x00000001
+
+The duplicate detection logic lives in a reusable helper
+ucsi_altmode_is_duplicate() and is invoked from
+ucsi_register_altmodes(). It applies to all three recipient types:
+partner (SOP), port (CON), and plug (SOP_P) altmodes.
+
+Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+unchanged: still SVID+VDO exact-dup match.
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260713084323.287516-1-acelan.kao@canonical.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/ucsi/ucsi.c | 132 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 132 insertions(+)
+
+--- a/drivers/usb/typec/ucsi/ucsi.c
++++ b/drivers/usb/typec/ucsi/ucsi.c
+@@ -498,6 +498,129 @@ err:
+ return ret;
+ }
+
++static void ucsi_dump_duplicate_altmode(struct ucsi_connector *con,
++ u8 recipient, u16 svid,
++ u32 existing_vdo, u32 new_vdo,
++ int offset)
++{
++ static const char * const recipient_names[] = {
++ [UCSI_RECIPIENT_CON] = "port",
++ [UCSI_RECIPIENT_SOP] = "partner",
++ [UCSI_RECIPIENT_SOP_P] = "plug",
++ [UCSI_RECIPIENT_SOP_PP] = "cable plug prime",
++ };
++
++ dev_warn(con->ucsi->dev,
++ "con%d: Firmware bug: duplicate %s altmode SVID 0x%04x at offset %d, ignoring but please contact the BIOS vendor to fix this issue.\n",
++ con->num, recipient_names[recipient], svid, offset);
++
++ if (existing_vdo != new_vdo)
++ dev_warn(con->ucsi->dev,
++ "con%d: VDO mismatch: 0x%08x vs 0x%08x\n",
++ con->num, existing_vdo, new_vdo);
++}
++
++/* Count altmodes in @altmodes that advertise @svid. */
++static int ucsi_altmode_count_svid(struct typec_altmode **altmodes, u16 svid)
++{
++ int count = 0;
++ int k;
++
++ for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
++ if (!altmodes[k])
++ break;
++ if (altmodes[k]->svid == svid)
++ count++;
++ }
++
++ return count;
++}
++
++/*
++ * Check if an altmode is a duplicate. Some firmware implementations
++ * incorrectly return the same altmode multiple times, causing sysfs errors.
++ * Returns true if the altmode should be skipped.
++ *
++ * The matching rules differ by recipient:
++ *
++ * - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug):
++ * Two altmodes with identical SVID and VDO are byte-for-byte duplicates
++ * and the second has no observable function. Drop them.
++ *
++ * - UCSI_RECIPIENT_SOP (partner):
++ * The typec class binds each partner altmode to a port altmode of the
++ * same SVID via altmode_match()/device_find_child(), which returns the
++ * first port altmode with a matching SVID. If the partner advertises
++ * more altmodes for SVID X than the port advertises, the surplus
++ * partner altmode(s) collapse onto an already-paired port altmode and
++ * trigger a "duplicate filename .../partner" sysfs error during
++ * typec_altmode_create_links(). Use the port-side altmode count for
++ * SVID X as the authoritative cap and reject any partner altmode that
++ * would exceed it. This preserves legitimate multi-Mode partner
++ * altmodes (e.g. vendor SVIDs that the port really does advertise
++ * twice) while filtering the firmware-generated duplicates that have
++ * no port counterpart.
++ */
++static bool ucsi_altmode_is_duplicate(struct ucsi_connector *con, u8 recipient,
++ const struct ucsi_altmode *alt_batch, int batch_idx,
++ u16 svid, u32 vdo, int offset)
++{
++ struct typec_altmode **altmodes;
++ int port_count, partner_count;
++ int k;
++
++ /* Check for duplicates within the current batch first */
++ for (k = 0; k < batch_idx; k++) {
++ if (alt_batch[k].svid == svid && alt_batch[k].mid == vdo) {
++ ucsi_dump_duplicate_altmode(con, recipient, svid,
++ vdo, vdo, offset);
++ return true;
++ }
++ }
++
++ switch (recipient) {
++ case UCSI_RECIPIENT_SOP:
++ /*
++ * Cap partner altmodes per SVID by the port-side count:
++ * any further partner altmode for that SVID would alias an
++ * already-paired port altmode and break typec sysfs.
++ */
++ port_count = ucsi_altmode_count_svid(con->port_altmode, svid);
++ partner_count = ucsi_altmode_count_svid(con->partner_altmode,
++ svid);
++ if (port_count && partner_count >= port_count) {
++ ucsi_dump_duplicate_altmode(con, recipient, svid,
++ con->partner_altmode[partner_count - 1]->vdo,
++ vdo, offset);
++ return true;
++ }
++ return false;
++ case UCSI_RECIPIENT_CON:
++ altmodes = con->port_altmode;
++ break;
++ case UCSI_RECIPIENT_SOP_P:
++ altmodes = con->plug_altmode;
++ break;
++ default:
++ return false;
++ }
++
++ /* CON and SOP_P: drop only exact SVID+VDO duplicates. */
++ for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
++ if (!altmodes[k])
++ break;
++
++ if (altmodes[k]->svid != svid || altmodes[k]->vdo != vdo)
++ continue;
++
++ ucsi_dump_duplicate_altmode(con, recipient, svid,
++ altmodes[k]->vdo, vdo, offset);
++ return true;
++ }
++
++ return false;
++}
++
+ static int
+ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
+ {
+@@ -622,6 +745,15 @@ static int ucsi_register_altmodes(struct
+ if (!alt[j].svid)
+ return 0;
+
++ /*
++ * Check for duplicates in current batch and already
++ * registered altmodes. Skip if duplicate found.
++ */
++ if (ucsi_altmode_is_duplicate(con, recipient, alt, j,
++ alt[j].svid, alt[j].mid,
++ i - num + j))
++ continue;
++
+ memset(&desc, 0, sizeof(desc));
+ desc.vdo = alt[j].mid;
+ desc.svid = alt[j].svid;
--- /dev/null
+From 61a799ffd1e5a4fd3702d547828b7ff3d161468e Mon Sep 17 00:00:00 2001
+From: Huihui Huang <hhhuang@smu.edu.sg>
+Date: Wed, 15 Jul 2026 22:08:10 +0800
+Subject: wifi: at76c50x-usb: avoid length underflow in at76_guess_freq()
+
+From: Huihui Huang <hhhuang@smu.edu.sg>
+
+commit 61a799ffd1e5a4fd3702d547828b7ff3d161468e upstream.
+
+at76_guess_freq() checks only that the received frame is at least a bare
+802.11 header (24 bytes) before subtracting the fixed management-body
+offset:
+
+ len -= el_off;
+
+For both beacon and probe response frames, el_off is 36. If the frame is
+shorter than el_off, subtracting it causes the calculated IE length to
+wrap. The length is eventually passed to cfg80211_find_elem_match() as a
+very large unsigned value, so the element walk runs beyond the RX skb.
+
+This path is reached from at76_rx_tasklet() while scanning. If the device
+delivers a truncated beacon or probe response, the oversized IE length
+causes an out-of-bounds read during scanning.
+
+Skip the IE lookup if the frame does not reach the variable elements,
+before subtracting el_off.
+
+Fixes: 1264b951463a ("at76c50x-usb: add driver")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Huihui Huang <hhhuang@smu.edu.sg>
+Link: https://patch.msgid.link/20260715140815.1242033-1-hhhuang@smu.edu.sg
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/atmel/at76c50x-usb.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/wireless/atmel/at76c50x-usb.c
++++ b/drivers/net/wireless/atmel/at76c50x-usb.c
+@@ -1521,13 +1521,16 @@ static inline int at76_guess_freq(struct
+
+ if (ieee80211_is_probe_resp(hdr->frame_control)) {
+ el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+- el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable;
+ } else if (ieee80211_is_beacon(hdr->frame_control)) {
+ el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+- el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable;
+ } else {
+ goto exit;
+ }
++
++ if (len < el_off)
++ goto exit;
++
++ el = priv->rx_skb->data + el_off;
+ len -= el_off;
+
+ el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len);