]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 10 Sep 2023 11:28:14 +0000 (12:28 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 10 Sep 2023 11:28:14 +0000 (12:28 +0100)
added patches:
usb-core-change-usb_get_device_descriptor-api.patch
usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch
usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch
usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch
usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch

queue-6.1/series
queue-6.1/usb-core-change-usb_get_device_descriptor-api.patch [new file with mode: 0644]
queue-6.1/usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch [new file with mode: 0644]
queue-6.1/usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch [new file with mode: 0644]
queue-6.1/usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch [new file with mode: 0644]
queue-6.1/usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch [new file with mode: 0644]

index aec7528443c328ccff98b28542bc63c4313afc69..eba1b617ab30f4a9638d1b64e35a93694ec1f6e5 100644 (file)
@@ -578,3 +578,8 @@ s390-dasd-fix-string-length-handling.patch
 crypto-stm32-fix-loop-iterating-through-scatterlist-for-dma.patch
 cpufreq-brcmstb-avs-cpufreq-fix-warray-bounds-bug.patch
 of-property-fw_devlink-add-a-devlink-for-panel-followers.patch
+usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch
+usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch
+usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch
+usb-core-change-usb_get_device_descriptor-api.patch
+usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch
diff --git a/queue-6.1/usb-core-change-usb_get_device_descriptor-api.patch b/queue-6.1/usb-core-change-usb_get_device_descriptor-api.patch
new file mode 100644 (file)
index 0000000..f41180a
--- /dev/null
@@ -0,0 +1,242 @@
+From de28e469da75359a2bb8cd8778b78aa64b1be1f4 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 4 Aug 2023 15:12:21 -0400
+Subject: USB: core: Change usb_get_device_descriptor() API
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit de28e469da75359a2bb8cd8778b78aa64b1be1f4 upstream.
+
+The usb_get_device_descriptor() routine reads the device descriptor
+from the udev device and stores it directly in udev->descriptor.  This
+interface is error prone, because the USB subsystem expects in-memory
+copies of a device's descriptors to be immutable once the device has
+been initialized.
+
+The interface is changed so that the device descriptor is left in a
+kmalloc-ed buffer, not copied into the usb_device structure.  A
+pointer to the buffer is returned to the caller, who is then
+responsible for kfree-ing it.  The corresponding changes needed in the
+various callers are fairly small.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Link: https://lore.kernel.org/r/d0111bb6-56c1-4f90-adf2-6cfe152f6561@rowland.harvard.edu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hcd.c     |   10 +++++++---
+ drivers/usb/core/hub.c     |   44 +++++++++++++++++++++++---------------------
+ drivers/usb/core/message.c |   29 ++++++++++++-----------------
+ drivers/usb/core/usb.h     |    4 ++--
+ 4 files changed, 44 insertions(+), 43 deletions(-)
+
+--- a/drivers/usb/core/hcd.c
++++ b/drivers/usb/core/hcd.c
+@@ -983,6 +983,7 @@ static int register_root_hub(struct usb_
+ {
+       struct device *parent_dev = hcd->self.controller;
+       struct usb_device *usb_dev = hcd->self.root_hub;
++      struct usb_device_descriptor *descr;
+       const int devnum = 1;
+       int retval;
+@@ -994,13 +995,16 @@ static int register_root_hub(struct usb_
+       mutex_lock(&usb_bus_idr_lock);
+       usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+-      retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
+-      if (retval != sizeof usb_dev->descriptor) {
++      descr = usb_get_device_descriptor(usb_dev);
++      if (IS_ERR(descr)) {
++              retval = PTR_ERR(descr);
+               mutex_unlock(&usb_bus_idr_lock);
+               dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
+                               dev_name(&usb_dev->dev), retval);
+-              return (retval < 0) ? retval : -EMSGSIZE;
++              return retval;
+       }
++      usb_dev->descriptor = *descr;
++      kfree(descr);
+       if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) {
+               retval = usb_get_bos_descriptor(usb_dev);
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2656,12 +2656,17 @@ int usb_authorize_device(struct usb_devi
+       }
+       if (usb_dev->wusb) {
+-              result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
+-              if (result < 0) {
++              struct usb_device_descriptor *descr;
++
++              descr = usb_get_device_descriptor(usb_dev);
++              if (IS_ERR(descr)) {
++                      result = PTR_ERR(descr);
+                       dev_err(&usb_dev->dev, "can't re-read device descriptor for "
+                               "authorization: %d\n", result);
+                       goto error_device_descriptor;
+               }
++              usb_dev->descriptor = *descr;
++              kfree(descr);
+       }
+       usb_dev->authorized = 1;
+@@ -4747,7 +4752,7 @@ hub_port_init(struct usb_hub *hub, struc
+       const char              *driver_name;
+       bool                    do_new_scheme;
+       int                     maxp0;
+-      struct usb_device_descriptor    *buf;
++      struct usb_device_descriptor    *buf, *descr;
+       buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
+       if (!buf)
+@@ -4984,15 +4989,16 @@ hub_port_init(struct usb_hub *hub, struc
+               usb_ep0_reinit(udev);
+       }
+-      retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
+-      if (retval < (signed)sizeof(udev->descriptor)) {
++      descr = usb_get_device_descriptor(udev);
++      if (IS_ERR(descr)) {
++              retval = PTR_ERR(descr);
+               if (retval != -ENODEV)
+                       dev_err(&udev->dev, "device descriptor read/all, error %d\n",
+                                       retval);
+-              if (retval >= 0)
+-                      retval = -ENOMSG;
+               goto fail;
+       }
++      udev->descriptor = *descr;
++      kfree(descr);
+       /*
+        * Some superspeed devices have finished the link training process
+@@ -5111,7 +5117,7 @@ hub_power_remaining(struct usb_hub *hub)
+ static int descriptors_changed(struct usb_device *udev,
+-              struct usb_device_descriptor *old_device_descriptor,
++              struct usb_device_descriptor *new_device_descriptor,
+               struct usb_host_bos *old_bos)
+ {
+       int             changed = 0;
+@@ -5122,8 +5128,8 @@ static int descriptors_changed(struct us
+       int             length;
+       char            *buf;
+-      if (memcmp(&udev->descriptor, old_device_descriptor,
+-                      sizeof(*old_device_descriptor)) != 0)
++      if (memcmp(&udev->descriptor, new_device_descriptor,
++                      sizeof(*new_device_descriptor)) != 0)
+               return 1;
+       if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
+@@ -5443,9 +5449,8 @@ static void hub_port_connect_change(stru
+ {
+       struct usb_port *port_dev = hub->ports[port1 - 1];
+       struct usb_device *udev = port_dev->child;
+-      struct usb_device_descriptor descriptor;
++      struct usb_device_descriptor *descr;
+       int status = -ENODEV;
+-      int retval;
+       dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
+                       portchange, portspeed(hub, portstatus));
+@@ -5472,23 +5477,20 @@ static void hub_port_connect_change(stru
+                        * changed device descriptors before resuscitating the
+                        * device.
+                        */
+-                      descriptor = udev->descriptor;
+-                      retval = usb_get_device_descriptor(udev,
+-                                      sizeof(udev->descriptor));
+-                      if (retval < 0) {
++                      descr = usb_get_device_descriptor(udev);
++                      if (IS_ERR(descr)) {
+                               dev_dbg(&udev->dev,
+-                                              "can't read device descriptor %d\n",
+-                                              retval);
++                                              "can't read device descriptor %ld\n",
++                                              PTR_ERR(descr));
+                       } else {
+-                              if (descriptors_changed(udev, &descriptor,
++                              if (descriptors_changed(udev, descr,
+                                               udev->bos)) {
+                                       dev_dbg(&udev->dev,
+                                                       "device descriptor has changed\n");
+-                                      /* for disconnect() calls */
+-                                      udev->descriptor = descriptor;
+                               } else {
+                                       status = 0; /* Nothing to do */
+                               }
++                              kfree(descr);
+                       }
+ #ifdef CONFIG_PM
+               } else if (udev->state == USB_STATE_SUSPENDED &&
+--- a/drivers/usb/core/message.c
++++ b/drivers/usb/core/message.c
+@@ -1039,40 +1039,35 @@ char *usb_cache_string(struct usb_device
+ }
+ /*
+- * usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
+- * @dev: the device whose device descriptor is being updated
+- * @size: how much of the descriptor to read
++ * usb_get_device_descriptor - read the device descriptor
++ * @udev: the device whose device descriptor should be read
+  *
+  * Context: task context, might sleep.
+  *
+- * Updates the copy of the device descriptor stored in the device structure,
+- * which dedicates space for this purpose.
+- *
+  * Not exported, only for use by the core.  If drivers really want to read
+  * the device descriptor directly, they can call usb_get_descriptor() with
+  * type = USB_DT_DEVICE and index = 0.
+  *
+- * This call is synchronous, and may not be used in an interrupt context.
+- *
+- * Return: The number of bytes received on success, or else the status code
+- * returned by the underlying usb_control_msg() call.
++ * Returns: a pointer to a dynamically allocated usb_device_descriptor
++ * structure (which the caller must deallocate), or an ERR_PTR value.
+  */
+-int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
++struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
+ {
+       struct usb_device_descriptor *desc;
+       int ret;
+-      if (size > sizeof(*desc))
+-              return -EINVAL;
+       desc = kmalloc(sizeof(*desc), GFP_NOIO);
+       if (!desc)
+-              return -ENOMEM;
++              return ERR_PTR(-ENOMEM);
++
++      ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc));
++      if (ret == sizeof(*desc))
++              return desc;
+-      ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
+       if (ret >= 0)
+-              memcpy(&dev->descriptor, desc, size);
++              ret = -EMSGSIZE;
+       kfree(desc);
+-      return ret;
++      return ERR_PTR(ret);
+ }
+ /*
+--- a/drivers/usb/core/usb.h
++++ b/drivers/usb/core/usb.h
+@@ -42,8 +42,8 @@ extern bool usb_endpoint_is_ignored(stru
+               struct usb_endpoint_descriptor *epd);
+ extern int usb_remove_device(struct usb_device *udev);
+-extern int usb_get_device_descriptor(struct usb_device *dev,
+-              unsigned int size);
++extern struct usb_device_descriptor *usb_get_device_descriptor(
++              struct usb_device *udev);
+ extern int usb_set_isoch_delay(struct usb_device *dev);
+ extern int usb_get_bos_descriptor(struct usb_device *dev);
+ extern void usb_release_bos_descriptor(struct usb_device *dev);
diff --git a/queue-6.1/usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch b/queue-6.1/usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch
new file mode 100644 (file)
index 0000000..ba2c9e0
--- /dev/null
@@ -0,0 +1,277 @@
+From ff33299ec8bb80cdcc073ad9c506bd79bb2ed20b Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 4 Aug 2023 15:14:14 -0400
+Subject: USB: core: Fix race by not overwriting udev->descriptor in hub_port_init()
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit ff33299ec8bb80cdcc073ad9c506bd79bb2ed20b upstream.
+
+Syzbot reported an out-of-bounds read in sysfs.c:read_descriptors():
+
+BUG: KASAN: slab-out-of-bounds in read_descriptors+0x263/0x280 drivers/usb/core/sysfs.c:883
+Read of size 8 at addr ffff88801e78b8c8 by task udevd/5011
+
+CPU: 0 PID: 5011 Comm: udevd Not tainted 6.4.0-rc6-syzkaller-00195-g40f71e7cd3c6 #0
+Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/27/2023
+Call Trace:
+ <TASK>
+ __dump_stack lib/dump_stack.c:88 [inline]
+ dump_stack_lvl+0xd9/0x150 lib/dump_stack.c:106
+ print_address_description.constprop.0+0x2c/0x3c0 mm/kasan/report.c:351
+ print_report mm/kasan/report.c:462 [inline]
+ kasan_report+0x11c/0x130 mm/kasan/report.c:572
+ read_descriptors+0x263/0x280 drivers/usb/core/sysfs.c:883
+...
+Allocated by task 758:
+...
+ __do_kmalloc_node mm/slab_common.c:966 [inline]
+ __kmalloc+0x5e/0x190 mm/slab_common.c:979
+ kmalloc include/linux/slab.h:563 [inline]
+ kzalloc include/linux/slab.h:680 [inline]
+ usb_get_configuration+0x1f7/0x5170 drivers/usb/core/config.c:887
+ usb_enumerate_device drivers/usb/core/hub.c:2407 [inline]
+ usb_new_device+0x12b0/0x19d0 drivers/usb/core/hub.c:2545
+
+As analyzed by Khazhy Kumykov, the cause of this bug is a race between
+read_descriptors() and hub_port_init(): The first routine uses a field
+in udev->descriptor, not expecting it to change, while the second
+overwrites it.
+
+Prior to commit 45bf39f8df7f ("USB: core: Don't hold device lock while
+reading the "descriptors" sysfs file") this race couldn't occur,
+because the routines were mutually exclusive thanks to the device
+locking.  Removing that locking from read_descriptors() exposed it to
+the race.
+
+The best way to fix the bug is to keep hub_port_init() from changing
+udev->descriptor once udev has been initialized and registered.
+Drivers expect the descriptors stored in the kernel to be immutable;
+we should not undermine this expectation.  In fact, this change should
+have been made long ago.
+
+So now hub_port_init() will take an additional argument, specifying a
+buffer in which to store the device descriptor it reads.  (If udev has
+not yet been initialized, the buffer pointer will be NULL and then
+hub_port_init() will store the device descriptor in udev as before.)
+This eliminates the data race responsible for the out-of-bounds read.
+
+The changes to hub_port_init() appear more extensive than they really
+are, because of indentation changes resulting from an attempt to avoid
+writing to other parts of the usb_device structure after it has been
+initialized.  Similar changes should be made to the code that reads
+the BOS descriptor, but that can be handled in a separate patch later
+on.  This patch is sufficient to fix the bug found by syzbot.
+
+Reported-and-tested-by: syzbot+18996170f8096c6174d0@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/linux-usb/000000000000c0ffe505fe86c9ca@google.com/#r
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Cc: Khazhy Kumykov <khazhy@google.com>
+Fixes: 45bf39f8df7f ("USB: core: Don't hold device lock while reading the "descriptors" sysfs file")
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/r/b958b47a-9a46-4c22-a9f9-e42e42c31251@rowland.harvard.edu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c |  114 ++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 70 insertions(+), 44 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -4736,10 +4736,17 @@ static int get_bMaxPacketSize0(struct us
+  * the port lock.  For a newly detected device that is not accessible
+  * through any global pointers, it's not necessary to lock the device,
+  * but it is still necessary to lock the port.
++ *
++ * For a newly detected device, @dev_descr must be NULL.  The device
++ * descriptor retrieved from the device will then be stored in
++ * @udev->descriptor.  For an already existing device, @dev_descr
++ * must be non-NULL.  The device descriptor will be stored there,
++ * not in @udev->descriptor, because descriptors for registered
++ * devices are meant to be immutable.
+  */
+ static int
+ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
+-              int retry_counter)
++              int retry_counter, struct usb_device_descriptor *dev_descr)
+ {
+       struct usb_device       *hdev = hub->hdev;
+       struct usb_hcd          *hcd = bus_to_hcd(hdev->bus);
+@@ -4751,6 +4758,7 @@ hub_port_init(struct usb_hub *hub, struc
+       int                     devnum = udev->devnum;
+       const char              *driver_name;
+       bool                    do_new_scheme;
++      const bool              initial = !dev_descr;
+       int                     maxp0;
+       struct usb_device_descriptor    *buf, *descr;
+@@ -4789,32 +4797,34 @@ hub_port_init(struct usb_hub *hub, struc
+       }
+       oldspeed = udev->speed;
+-      /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
+-       * it's fixed size except for full speed devices.
+-       * For Wireless USB devices, ep0 max packet is always 512 (tho
+-       * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
+-       */
+-      switch (udev->speed) {
+-      case USB_SPEED_SUPER_PLUS:
+-      case USB_SPEED_SUPER:
+-      case USB_SPEED_WIRELESS:        /* fixed at 512 */
+-              udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
+-              break;
+-      case USB_SPEED_HIGH:            /* fixed at 64 */
+-              udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+-              break;
+-      case USB_SPEED_FULL:            /* 8, 16, 32, or 64 */
+-              /* to determine the ep0 maxpacket size, try to read
+-               * the device descriptor to get bMaxPacketSize0 and
+-               * then correct our initial guess.
++      if (initial) {
++              /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
++               * it's fixed size except for full speed devices.
++               * For Wireless USB devices, ep0 max packet is always 512 (tho
++               * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
+                */
+-              udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+-              break;
+-      case USB_SPEED_LOW:             /* fixed at 8 */
+-              udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
+-              break;
+-      default:
+-              goto fail;
++              switch (udev->speed) {
++              case USB_SPEED_SUPER_PLUS:
++              case USB_SPEED_SUPER:
++              case USB_SPEED_WIRELESS:        /* fixed at 512 */
++                      udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
++                      break;
++              case USB_SPEED_HIGH:            /* fixed at 64 */
++                      udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
++                      break;
++              case USB_SPEED_FULL:            /* 8, 16, 32, or 64 */
++                      /* to determine the ep0 maxpacket size, try to read
++                       * the device descriptor to get bMaxPacketSize0 and
++                       * then correct our initial guess.
++                       */
++                      udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
++                      break;
++              case USB_SPEED_LOW:             /* fixed at 8 */
++                      udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
++                      break;
++              default:
++                      goto fail;
++              }
+       }
+       if (udev->speed == USB_SPEED_WIRELESS)
+@@ -4837,22 +4847,24 @@ hub_port_init(struct usb_hub *hub, struc
+       if (udev->speed < USB_SPEED_SUPER)
+               dev_info(&udev->dev,
+                               "%s %s USB device number %d using %s\n",
+-                              (udev->config) ? "reset" : "new", speed,
++                              (initial ? "new" : "reset"), speed,
+                               devnum, driver_name);
+-      /* Set up TT records, if needed  */
+-      if (hdev->tt) {
+-              udev->tt = hdev->tt;
+-              udev->ttport = hdev->ttport;
+-      } else if (udev->speed != USB_SPEED_HIGH
+-                      && hdev->speed == USB_SPEED_HIGH) {
+-              if (!hub->tt.hub) {
+-                      dev_err(&udev->dev, "parent hub has no TT\n");
+-                      retval = -EINVAL;
+-                      goto fail;
++      if (initial) {
++              /* Set up TT records, if needed  */
++              if (hdev->tt) {
++                      udev->tt = hdev->tt;
++                      udev->ttport = hdev->ttport;
++              } else if (udev->speed != USB_SPEED_HIGH
++                              && hdev->speed == USB_SPEED_HIGH) {
++                      if (!hub->tt.hub) {
++                              dev_err(&udev->dev, "parent hub has no TT\n");
++                              retval = -EINVAL;
++                              goto fail;
++                      }
++                      udev->tt = &hub->tt;
++                      udev->ttport = port1;
+               }
+-              udev->tt = &hub->tt;
+-              udev->ttport = port1;
+       }
+       /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
+@@ -4881,6 +4893,12 @@ hub_port_init(struct usb_hub *hub, struc
+                       maxp0 = get_bMaxPacketSize0(udev, buf,
+                                       GET_DESCRIPTOR_BUFSIZE, retries == 0);
++                      if (maxp0 > 0 && !initial &&
++                                      maxp0 != udev->descriptor.bMaxPacketSize0) {
++                              dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
++                              retval = -ENODEV;
++                              goto fail;
++                      }
+                       retval = hub_port_reset(hub, port1, udev, delay, false);
+                       if (retval < 0)         /* error or disconnect */
+@@ -4954,6 +4972,12 @@ hub_port_init(struct usb_hub *hub, struc
+               } else {
+                       u32 delay;
++                      if (!initial && maxp0 != udev->descriptor.bMaxPacketSize0) {
++                              dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
++                              retval = -ENODEV;
++                              goto fail;
++                      }
++
+                       delay = udev->parent->hub_delay;
+                       udev->hub_delay = min_t(u32, delay,
+                                               USB_TP_TRANSMISSION_DELAY_MAX);
+@@ -4997,7 +5021,10 @@ hub_port_init(struct usb_hub *hub, struc
+                                       retval);
+               goto fail;
+       }
+-      udev->descriptor = *descr;
++      if (initial)
++              udev->descriptor = *descr;
++      else
++              *dev_descr = *descr;
+       kfree(descr);
+       /*
+@@ -5302,7 +5329,7 @@ static void hub_port_connect(struct usb_
+               }
+               /* reset (non-USB 3.0 devices) and get descriptor */
+-              status = hub_port_init(hub, udev, port1, i);
++              status = hub_port_init(hub, udev, port1, i, NULL);
+               if (status < 0)
+                       goto loop;
+@@ -5928,7 +5955,7 @@ static int usb_reset_and_verify_device(s
+       struct usb_device               *parent_hdev = udev->parent;
+       struct usb_hub                  *parent_hub;
+       struct usb_hcd                  *hcd = bus_to_hcd(udev->bus);
+-      struct usb_device_descriptor    descriptor = udev->descriptor;
++      struct usb_device_descriptor    descriptor;
+       struct usb_host_bos             *bos;
+       int                             i, j, ret = 0;
+       int                             port1 = udev->portnum;
+@@ -5960,7 +5987,7 @@ static int usb_reset_and_verify_device(s
+               /* ep0 maxpacket size may change; let the HCD know about it.
+                * Other endpoints will be handled by re-enumeration. */
+               usb_ep0_reinit(udev);
+-              ret = hub_port_init(parent_hub, udev, port1, i);
++              ret = hub_port_init(parent_hub, udev, port1, i, &descriptor);
+               if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
+                       break;
+       }
+@@ -5972,7 +5999,6 @@ static int usb_reset_and_verify_device(s
+       /* Device might have changed firmware (DFU or similar) */
+       if (descriptors_changed(udev, &descriptor, bos)) {
+               dev_info(&udev->dev, "device firmware changed\n");
+-              udev->descriptor = descriptor;  /* for disconnect() calls */
+               goto re_enumerate;
+       }
diff --git a/queue-6.1/usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch b/queue-6.1/usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch
new file mode 100644 (file)
index 0000000..d19618b
--- /dev/null
@@ -0,0 +1,328 @@
+From 85d07c55621676d47d873d2749b88f783cd4d5a1 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 4 Aug 2023 15:10:59 -0400
+Subject: USB: core: Unite old scheme and new scheme descriptor reads
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 85d07c55621676d47d873d2749b88f783cd4d5a1 upstream.
+
+In preparation for reworking the usb_get_device_descriptor() routine,
+it is desirable to unite the two different code paths responsible for
+initially determining endpoint 0's maximum packet size in a newly
+discovered USB device.  Making this determination presents a
+chicken-and-egg sort of problem, in that the only way to learn the
+maxpacket value is to get it from the device descriptor retrieved from
+the device, but communicating with the device to retrieve a descriptor
+requires us to know beforehand the ep0 maxpacket size.
+
+In practice this problem is solved in two different ways, referred to
+in hub.c as the "old scheme" and the "new scheme".  The old scheme
+(which is the approach recommended by the USB-2 spec) involves asking
+the device to send just the first eight bytes of its device
+descriptor.  Such a transfer uses packets containing no more than
+eight bytes each, and every USB device must have an ep0 maxpacket size
+>= 8, so this should succeed.  Since the bMaxPacketSize0 field of the
+device descriptor lies within the first eight bytes, this is all we
+need.
+
+The new scheme is an imitation of the technique used in an early
+Windows USB implementation, giving it the happy advantage of working
+with a wide variety of devices (some of them at the time would not
+work with the old scheme, although that's probably less true now).  It
+involves making an initial guess of the ep0 maxpacket size, asking the
+device to send up to 64 bytes worth of its device descriptor (which is
+only 18 bytes long), and then resetting the device to clear any error
+condition that might have resulted from the guess being wrong.  The
+initial guess is determined by the connection speed; it should be
+correct in all cases other than full speed, for which the allowed
+values are 8, 16, 32, and 64 (in this case the initial guess is 64).
+
+The reason for this patch is that the old- and new-scheme parts of
+hub_port_init() use different code paths, one involving
+usb_get_device_descriptor() and one not, for their initial reads of
+the device descriptor.  Since these reads have essentially the same
+purpose and are made under essentially the same circumstances, this is
+illogical.  It makes more sense to have both of them use a common
+subroutine.
+
+This subroutine does basically what the new scheme's code did, because
+that approach is more general than the one used by the old scheme.  It
+only needs to know how many bytes to transfer and whether or not it is
+being called for the first iteration of a retry loop (in case of
+certain time-out errors).  There are two main differences from the
+former code:
+
+       We initialize the bDescriptorType field of the transfer buffer
+       to 0 before performing the transfer, to avoid possibly
+       accessing an uninitialized value afterward.
+
+       We read the device descriptor into a temporary buffer rather
+       than storing it directly into udev->descriptor, which the old
+       scheme implementation used to do.
+
+Since the whole point of this first read of the device descriptor is
+to determine the bMaxPacketSize0 value, that is what the new routine
+returns (or an error code).  The value is stored in a local variable
+rather than in udev->descriptor.  As a side effect, this necessitates
+moving a section of code that checks the bcdUSB field for SuperSpeed
+devices until after the full device descriptor has been retrieved.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Cc: Oliver Neukum <oneukum@suse.com>
+Link: https://lore.kernel.org/r/495cb5d4-f956-4f4a-a875-1e67e9489510@rowland.harvard.edu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c |  173 ++++++++++++++++++++++++++-----------------------
+ 1 file changed, 94 insertions(+), 79 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -4661,6 +4661,67 @@ static int hub_enable_device(struct usb_
+       return hcd->driver->enable_device(hcd, udev);
+ }
++/*
++ * Get the bMaxPacketSize0 value during initialization by reading the
++ * device's device descriptor.  Since we don't already know this value,
++ * the transfer is unsafe and it ignores I/O errors, only testing for
++ * reasonable received values.
++ *
++ * For "old scheme" initialization, size will be 8 so we read just the
++ * start of the device descriptor, which should work okay regardless of
++ * the actual bMaxPacketSize0 value.  For "new scheme" initialization,
++ * size will be 64 (and buf will point to a sufficiently large buffer),
++ * which might not be kosher according to the USB spec but it's what
++ * Windows does and what many devices expect.
++ *
++ * Returns: bMaxPacketSize0 or a negative error code.
++ */
++static int get_bMaxPacketSize0(struct usb_device *udev,
++              struct usb_device_descriptor *buf, int size, bool first_time)
++{
++      int i, rc;
++
++      /*
++       * Retry on all errors; some devices are flakey.
++       * 255 is for WUSB devices, we actually need to use
++       * 512 (WUSB1.0[4.8.1]).
++       */
++      for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) {
++              /* Start with invalid values in case the transfer fails */
++              buf->bDescriptorType = buf->bMaxPacketSize0 = 0;
++              rc = usb_control_msg(udev, usb_rcvaddr0pipe(),
++                              USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
++                              USB_DT_DEVICE << 8, 0,
++                              buf, size,
++                              initial_descriptor_timeout);
++              switch (buf->bMaxPacketSize0) {
++              case 8: case 16: case 32: case 64: case 255:
++                      if (buf->bDescriptorType == USB_DT_DEVICE) {
++                              rc = buf->bMaxPacketSize0;
++                              break;
++                      }
++                      fallthrough;
++              default:
++                      if (rc >= 0)
++                              rc = -EPROTO;
++                      break;
++              }
++
++              /*
++               * Some devices time out if they are powered on
++               * when already connected. They need a second
++               * reset, so return early. But only on the first
++               * attempt, lest we get into a time-out/reset loop.
++               */
++              if (rc > 0 || (rc == -ETIMEDOUT && first_time &&
++                              udev->speed > USB_SPEED_FULL))
++                      break;
++      }
++      return rc;
++}
++
++#define GET_DESCRIPTOR_BUFSIZE        64
++
+ /* Reset device, (re)assign address, get device descriptor.
+  * Device connection must be stable, no more debouncing needed.
+  * Returns device in USB_STATE_ADDRESS, except on error.
+@@ -4685,6 +4746,12 @@ hub_port_init(struct usb_hub *hub, struc
+       int                     devnum = udev->devnum;
+       const char              *driver_name;
+       bool                    do_new_scheme;
++      int                     maxp0;
++      struct usb_device_descriptor    *buf;
++
++      buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
++      if (!buf)
++              return -ENOMEM;
+       /* root hub ports have a slightly longer reset period
+        * (from USB 2.0 spec, section 7.1.7.5)
+@@ -4799,9 +4866,6 @@ hub_port_init(struct usb_hub *hub, struc
+       for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
+               if (do_new_scheme) {
+-                      struct usb_device_descriptor *buf;
+-                      int r = 0;
+-
+                       retval = hub_enable_device(udev);
+                       if (retval < 0) {
+                               dev_err(&udev->dev,
+@@ -4810,52 +4874,8 @@ hub_port_init(struct usb_hub *hub, struc
+                               goto fail;
+                       }
+-#define GET_DESCRIPTOR_BUFSIZE        64
+-                      buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
+-                      if (!buf) {
+-                              retval = -ENOMEM;
+-                              continue;
+-                      }
+-
+-                      /* Retry on all errors; some devices are flakey.
+-                       * 255 is for WUSB devices, we actually need to use
+-                       * 512 (WUSB1.0[4.8.1]).
+-                       */
+-                      for (operations = 0; operations < GET_MAXPACKET0_TRIES;
+-                                      ++operations) {
+-                              buf->bMaxPacketSize0 = 0;
+-                              r = usb_control_msg(udev, usb_rcvaddr0pipe(),
+-                                      USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+-                                      USB_DT_DEVICE << 8, 0,
+-                                      buf, GET_DESCRIPTOR_BUFSIZE,
+-                                      initial_descriptor_timeout);
+-                              switch (buf->bMaxPacketSize0) {
+-                              case 8: case 16: case 32: case 64: case 255:
+-                                      if (buf->bDescriptorType ==
+-                                                      USB_DT_DEVICE) {
+-                                              r = 0;
+-                                              break;
+-                                      }
+-                                      fallthrough;
+-                              default:
+-                                      if (r == 0)
+-                                              r = -EPROTO;
+-                                      break;
+-                              }
+-                              /*
+-                               * Some devices time out if they are powered on
+-                               * when already connected. They need a second
+-                               * reset. But only on the first attempt,
+-                               * lest we get into a time out/reset loop
+-                               */
+-                              if (r == 0 || (r == -ETIMEDOUT &&
+-                                              retries == 0 &&
+-                                              udev->speed > USB_SPEED_FULL))
+-                                      break;
+-                      }
+-                      udev->descriptor.bMaxPacketSize0 =
+-                                      buf->bMaxPacketSize0;
+-                      kfree(buf);
++                      maxp0 = get_bMaxPacketSize0(udev, buf,
++                                      GET_DESCRIPTOR_BUFSIZE, retries == 0);
+                       retval = hub_port_reset(hub, port1, udev, delay, false);
+                       if (retval < 0)         /* error or disconnect */
+@@ -4866,14 +4886,13 @@ hub_port_init(struct usb_hub *hub, struc
+                               retval = -ENODEV;
+                               goto fail;
+                       }
+-                      if (r) {
+-                              if (r != -ENODEV)
++                      if (maxp0 < 0) {
++                              if (maxp0 != -ENODEV)
+                                       dev_err(&udev->dev, "device descriptor read/64, error %d\n",
+-                                                      r);
+-                              retval = -EMSGSIZE;
++                                                      maxp0);
++                              retval = maxp0;
+                               continue;
+                       }
+-#undef GET_DESCRIPTOR_BUFSIZE
+               }
+               /*
+@@ -4919,19 +4938,17 @@ hub_port_init(struct usb_hub *hub, struc
+                               break;
+               }
+-              retval = usb_get_device_descriptor(udev, 8);
+-              if (retval < 8) {
++              /* !do_new_scheme || wusb */
++              maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0);
++              if (maxp0 < 0) {
++                      retval = maxp0;
+                       if (retval != -ENODEV)
+                               dev_err(&udev->dev,
+                                       "device descriptor read/8, error %d\n",
+                                       retval);
+-                      if (retval >= 0)
+-                              retval = -EMSGSIZE;
+               } else {
+                       u32 delay;
+-                      retval = 0;
+-
+                       delay = udev->parent->hub_delay;
+                       udev->hub_delay = min_t(u32, delay,
+                                               USB_TP_TRANSMISSION_DELAY_MAX);
+@@ -4948,27 +4965,10 @@ hub_port_init(struct usb_hub *hub, struc
+       if (retval)
+               goto fail;
+-      /*
+-       * Some superspeed devices have finished the link training process
+-       * and attached to a superspeed hub port, but the device descriptor
+-       * got from those devices show they aren't superspeed devices. Warm
+-       * reset the port attached by the devices can fix them.
+-       */
+-      if ((udev->speed >= USB_SPEED_SUPER) &&
+-                      (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
+-              dev_err(&udev->dev, "got a wrong device descriptor, "
+-                              "warm reset device\n");
+-              hub_port_reset(hub, port1, udev,
+-                              HUB_BH_RESET_TIME, true);
+-              retval = -EINVAL;
+-              goto fail;
+-      }
+-
+-      if (udev->descriptor.bMaxPacketSize0 == 0xff ||
+-                      udev->speed >= USB_SPEED_SUPER)
++      if (maxp0 == 0xff || udev->speed >= USB_SPEED_SUPER)
+               i = 512;
+       else
+-              i = udev->descriptor.bMaxPacketSize0;
++              i = maxp0;
+       if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
+               if (udev->speed == USB_SPEED_LOW ||
+                               !(i == 8 || i == 16 || i == 32 || i == 64)) {
+@@ -4994,6 +4994,20 @@ hub_port_init(struct usb_hub *hub, struc
+               goto fail;
+       }
++      /*
++       * Some superspeed devices have finished the link training process
++       * and attached to a superspeed hub port, but the device descriptor
++       * got from those devices show they aren't superspeed devices. Warm
++       * reset the port attached by the devices can fix them.
++       */
++      if ((udev->speed >= USB_SPEED_SUPER) &&
++                      (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
++              dev_err(&udev->dev, "got a wrong device descriptor, warm reset device\n");
++              hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true);
++              retval = -EINVAL;
++              goto fail;
++      }
++
+       usb_detect_quirks(udev);
+       if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
+@@ -5016,6 +5030,7 @@ fail:
+               hub_port_disable(hub, port1, 0);
+               update_devnum(udev, devnum);    /* for disconnect processing */
+       }
++      kfree(buf);
+       return retval;
+ }
diff --git a/queue-6.1/usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch b/queue-6.1/usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch
new file mode 100644 (file)
index 0000000..497579c
--- /dev/null
@@ -0,0 +1,83 @@
+From f23643306430f86e2f413ee2b986e0773e79da31 Mon Sep 17 00:00:00 2001
+From: RD Babiera <rdbabiera@google.com>
+Date: Mon, 14 Aug 2023 18:05:59 +0000
+Subject: usb: typec: bus: verify partner exists in typec_altmode_attention
+
+From: RD Babiera <rdbabiera@google.com>
+
+commit f23643306430f86e2f413ee2b986e0773e79da31 upstream.
+
+Some usb hubs will negotiate DisplayPort Alt mode with the device
+but will then negotiate a data role swap after entering the alt
+mode. The data role swap causes the device to unregister all alt
+modes, however the usb hub will still send Attention messages
+even after failing to reregister the Alt Mode. type_altmode_attention
+currently does not verify whether or not a device's altmode partner
+exists, which results in a NULL pointer error when dereferencing
+the typec_altmode and typec_altmode_ops belonging to the altmode
+partner.
+
+Verify the presence of a device's altmode partner before sending
+the Attention message to the Alt Mode driver.
+
+Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes")
+Cc: stable@vger.kernel.org
+Signed-off-by: RD Babiera <rdbabiera@google.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Reviewed-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/r/20230814180559.923475-1-rdbabiera@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/bus.c           |   12 ++++++++++--
+ drivers/usb/typec/tcpm/tcpm.c     |    3 ++-
+ include/linux/usb/typec_altmode.h |    2 +-
+ 3 files changed, 13 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/typec/bus.c
++++ b/drivers/usb/typec/bus.c
+@@ -154,12 +154,20 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit);
+  *
+  * Notifies the partner of @adev about Attention command.
+  */
+-void typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
++int typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
+ {
+-      struct typec_altmode *pdev = &to_altmode(adev)->partner->adev;
++      struct altmode *partner = to_altmode(adev)->partner;
++      struct typec_altmode *pdev;
++
++      if (!partner)
++              return -ENODEV;
++
++      pdev = &partner->adev;
+       if (pdev->ops && pdev->ops->attention)
+               pdev->ops->attention(pdev, vdo);
++
++      return 0;
+ }
+ EXPORT_SYMBOL_GPL(typec_altmode_attention);
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -1871,7 +1871,8 @@ static void tcpm_handle_vdm_request(stru
+                       }
+                       break;
+               case ADEV_ATTENTION:
+-                      typec_altmode_attention(adev, p[1]);
++                      if (typec_altmode_attention(adev, p[1]))
++                              tcpm_log(port, "typec_altmode_attention no port partner altmode");
+                       break;
+               }
+       }
+--- a/include/linux/usb/typec_altmode.h
++++ b/include/linux/usb/typec_altmode.h
+@@ -67,7 +67,7 @@ struct typec_altmode_ops {
+ int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo);
+ int typec_altmode_exit(struct typec_altmode *altmode);
+-void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
++int typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
+ int typec_altmode_vdm(struct typec_altmode *altmode,
+                     const u32 header, const u32 *vdo, int count);
+ int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf,
diff --git a/queue-6.1/usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch b/queue-6.1/usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch
new file mode 100644 (file)
index 0000000..aff892f
--- /dev/null
@@ -0,0 +1,100 @@
+From c97cd0b4b54eb42aed7f6c3c295a2d137f6d2416 Mon Sep 17 00:00:00 2001
+From: RD Babiera <rdbabiera@google.com>
+Date: Mon, 31 Jul 2023 16:59:23 +0000
+Subject: usb: typec: tcpm: set initial svdm version based on pd revision
+
+From: RD Babiera <rdbabiera@google.com>
+
+commit c97cd0b4b54eb42aed7f6c3c295a2d137f6d2416 upstream.
+
+When sending Discover Identity messages to a Port Partner that uses Power
+Delivery v2 and SVDM v1, we currently send PD v2 messages with SVDM v2.0,
+expecting the port partner to respond with its highest supported SVDM
+version as stated in Section 6.4.4.2.3 in the Power Delivery v3
+specification. However, sending SVDM v2 to some Power Delivery v2 port
+partners results in a NAK whereas sending SVDM v1 does not.
+
+NAK messages can be handled by the initiator (PD v3 section 6.4.4.2.5.1),
+and one solution could be to resend Discover Identity on a lower SVDM
+version if possible. But, Section 6.4.4.3 of PD v2 states that "A NAK
+response Should be taken as an indication not to retry that particular
+Command."
+
+Instead, we can set the SVDM version to the maximum one supported by the
+negotiated PD revision. When operating in PD v2, this obeys Section
+6.4.4.2.3, which states the SVDM field "Shall be set to zero to indicate
+Version 1.0." In PD v3, the SVDM field "Shall be set to 01b to indicate
+Version 2.0."
+
+Fixes: c34e85fa69b9 ("usb: typec: tcpm: Send DISCOVER_IDENTITY from dedicated work")
+Cc: stable@vger.kernel.org
+Signed-off-by: RD Babiera <rdbabiera@google.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://lore.kernel.org/r/20230731165926.1815338-1-rdbabiera@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/tcpm/tcpm.c |   35 +++++++++++++++++++++++++++++++----
+ 1 file changed, 31 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -3929,6 +3929,29 @@ static enum typec_cc_status tcpm_pwr_opm
+       }
+ }
++static void tcpm_set_initial_svdm_version(struct tcpm_port *port)
++{
++      switch (port->negotiated_rev) {
++      case PD_REV30:
++              break;
++      /*
++       * 6.4.4.2.3 Structured VDM Version
++       * 2.0 states "At this time, there is only one version (1.0) defined.
++       * This field Shall be set to zero to indicate Version 1.0."
++       * 3.0 states "This field Shall be set to 01b to indicate Version 2.0."
++       * To ensure that we follow the Power Delivery revision we are currently
++       * operating on, downgrade the SVDM version to the highest one supported
++       * by the Power Delivery revision.
++       */
++      case PD_REV20:
++              typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0);
++              break;
++      default:
++              typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0);
++              break;
++      }
++}
++
+ static void run_state_machine(struct tcpm_port *port)
+ {
+       int ret;
+@@ -4153,10 +4176,12 @@ static void run_state_machine(struct tcp
+                * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
+                * port->explicit_contract to decide whether to send the command.
+                */
+-              if (port->explicit_contract)
++              if (port->explicit_contract) {
++                      tcpm_set_initial_svdm_version(port);
+                       mod_send_discover_delayed_work(port, 0);
+-              else
++              } else {
+                       port->send_discover = false;
++              }
+               /*
+                * 6.3.5
+@@ -4439,10 +4464,12 @@ static void run_state_machine(struct tcp
+                * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
+                * port->explicit_contract.
+                */
+-              if (port->explicit_contract)
++              if (port->explicit_contract) {
++                      tcpm_set_initial_svdm_version(port);
+                       mod_send_discover_delayed_work(port, 0);
+-              else
++              } else {
+                       port->send_discover = false;
++              }
+               power_supply_changed(port->psy);
+               break;