Stop hardcoding the gadget's max_speed to USB_SPEED_HIGH on DEV_BIND; parse max_speed from the bind event sent by the host backend and clamp it to a supported set (LOW/FULL/HIGH/SUPER/SUPER_PLUS), defaulting to HIGH on invalid data. Update set_speed() to clamp the negotiated speed against gadget.max_speed and configure the EP0 max packet size for SuperSpeed and SuperSpeed Plus (512 bytes both, per USB 3.1), while preserving existing HS/FS behavior.
Also send the SuperSpeed endpoint companion descriptor together with the endpoint descriptor on EP_ENABLE, as an additional scatter-gather payload, so the host backend can configure burst/mult parameters instead of relying on the endpoint descriptor alone. This aligns the guest-side speed configuration with the host's capability and enables SuperSpeed path bring-up. Signed-off-by: Igor Skalkin <[email protected]> --- drivers/usb/virtio_usb/device.c | 54 +++++++++++++++++++++++++++++++++++----- include/uapi/linux/virtio_usb.h | 8 +++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/drivers/usb/virtio_usb/device.c b/drivers/usb/virtio_usb/device.c index 9ee1f05..ee41ea9 100644 --- a/drivers/usb/virtio_usb/device.c +++ b/drivers/usb/virtio_usb/device.c @@ -439,12 +439,14 @@ static void vep_enable_work(struct work_struct *work) container_of(work, struct virtio_usb_ep, enable_work); struct virtio_usb_dc *vudc = vep->vudc; const struct usb_endpoint_descriptor *desc = vep->ep.desc; - struct scatterlist sg; - struct scatterlist *psg_data = &sg; + const struct usb_ss_ep_comp_descriptor *comp = vep->ep.comp_desc; + struct scatterlist sgs[2]; + struct scatterlist *psg_data = sgs; struct virtio_usb_dev_cmd_hdr *hdr; struct virtio_usb_cmd *cmd; unsigned long flags; u16 endpoint; + u8 n_sgs = 1; int rc; if (!desc) @@ -461,7 +463,13 @@ static void vep_enable_work(struct work_struct *work) goto done; } - sg_init_one(psg_data, desc, sizeof(*desc)); + if (virtio_usb_ep_comp_valid(comp)) + n_sgs = 2; + + sg_init_table(psg_data, n_sgs); + sg_set_buf(&psg_data[0], desc, sizeof(*desc)); + if (n_sgs == 2) + sg_set_buf(&psg_data[1], comp, sizeof(*comp)); hdr = virtio_usb_cmd_request(cmd); hdr->endpoint = cpu_to_le16(endpoint); @@ -564,6 +572,7 @@ static int virtio_ep_enable(struct usb_ep *ep, { struct virtio_usb_ep *vep = usb_ep_to_virtio_ep(ep); struct virtio_usb_dc *vudc = vep->vudc; + const struct usb_ss_ep_comp_descriptor *comp = ep->comp_desc; unsigned long flags; spin_lock_irqsave(&vudc->lock, flags); @@ -579,6 +588,16 @@ static int virtio_ep_enable(struct usb_ep *ep, return -EBUSY; } + if (comp) { + if (!virtio_usb_ep_comp_valid(comp) || comp->bMaxBurst > 15) { + spin_unlock_irqrestore(&vudc->lock, flags); + return -EINVAL; + } + } else if (vudc->gadget.speed >= USB_SPEED_SUPER) { + spin_unlock_irqrestore(&vudc->lock, flags); + return -EINVAL; + } + ep->desc = desc; ep->maxpacket = usb_endpoint_maxp(desc); vep->state = EP_ENABLING; @@ -1119,9 +1138,13 @@ static void virtio_usb_dc_set_speed(struct usb_gadget *gadget, struct virtio_usb_dc *vudc = container_of(gadget, struct virtio_usb_dc, gadget); - vudc->gadget.speed = min_t(u8, USB_SPEED_HIGH, speed); + vudc->gadget.speed = min_t(u8, vudc->gadget.max_speed, speed); - switch (speed) { + switch (vudc->gadget.speed) { + case USB_SPEED_SUPER_PLUS: + case USB_SPEED_SUPER: + vudc->veps[0].ep.maxpacket = 512; + break; case USB_SPEED_HIGH: case USB_SPEED_FULL: vudc->veps[0].ep.maxpacket = 64; @@ -1375,6 +1398,8 @@ static int virtio_usb_dc_parent_create(struct virtio_usb_dc *vudc) static void virtio_usb_dc_event_process(struct virtio_usb_event *event) { struct virtio_usb_dev_event *evt = virtio_usb_event_buf(event); + struct virtio_usb_dev_bind_event *bind_evt = + (struct virtio_usb_dev_bind_event *)evt; struct virtio_usb_dev_setup_event *setup_evt; struct virtio_usb *vusb = event->vusb; struct virtio_usb_dc *vudc; @@ -1395,6 +1420,8 @@ static void virtio_usb_dc_event_process(struct virtio_usb_event *event) switch (le32_to_cpu(evt->code)) { case VIRTIO_USB_EVT_DEV_BIND: { + u8 max_speed; + if (vudc->registered) { dev_err(&vusb->vdev->dev, "port %d: BIND while still registered, ignoring\n", @@ -1405,6 +1432,8 @@ static void virtio_usb_dc_event_process(struct virtio_usb_event *event) memzero_explicit(&vudc->gadget, sizeof(struct usb_gadget)); vudc->gadget.sg_supported = 1; + max_speed = bind_evt->max_speed; + rc = virtio_usb_dc_get_endpoint_count(vudc); if (rc) { dev_err(&vusb->vdev->dev, @@ -1436,7 +1465,20 @@ static void virtio_usb_dc_event_process(struct virtio_usb_event *event) break; } vudc->gadget.ops = &virtio_gadget_ops; - vudc->gadget.max_speed = USB_SPEED_HIGH; + switch (max_speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: + vudc->gadget.max_speed = max_speed; + break; + default: + pr_info("virtio-usb: unrecognized max_speed %u from host, defaulting to HIGH\n", + max_speed); + vudc->gadget.max_speed = USB_SPEED_HIGH; + break; + } //vudc->gadget.dev.init_name = gadget_name; vudc->gadget.dev.parent = &vudc->pdev->dev; diff --git a/include/uapi/linux/virtio_usb.h b/include/uapi/linux/virtio_usb.h index 29cec5d..d89223d 100644 --- a/include/uapi/linux/virtio_usb.h +++ b/include/uapi/linux/virtio_usb.h @@ -222,6 +222,14 @@ struct virtio_usb_dev_event { __u8 padding[10]; }; +/* VIRTIO_USB_EVT_DEV_BIND */ +struct virtio_usb_dev_bind_event { + __le32 code; /* VIRTIO_USB_EVT_DEV_BIND */ + __le16 port; /* Device ID */ + __u8 max_speed; /* enum usb_device_speed */ + __u8 padding[9]; +}; + /* VIRTIO_USB_EVT_DEV_SETUP */ struct virtio_usb_dev_setup_event { __le32 code; /* VIRTIO_USB_EVT_DEV_SETUP */
