aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Petter Selasky <hselasky@FreeBSD.org>2020-06-08 09:28:26 +0000
committerHans Petter Selasky <hselasky@FreeBSD.org>2020-06-08 09:28:26 +0000
commited33b8c8a505b702a9183482b1d61c7d0b315944 (patch)
tree6f009687891d5cdb54383e1d58a7a86fca88240d
parent8e51c3499e0273806331f4172c5db90b061734e0 (diff)
downloadsrc-ed33b8c8a505b702a9183482b1d61c7d0b315944.tar.gz
src-ed33b8c8a505b702a9183482b1d61c7d0b315944.zip
MFC r361581:
Implement helper function, usbd_get_max_frame_length(), which allows kernel device drivers to correctly predict the default USB transfer frame length. Sponsored by: Mellanox Technologies
Notes
Notes: svn path=/stable/10/; revision=361912
-rw-r--r--sys/dev/usb/usb_transfer.c75
-rw-r--r--sys/dev/usb/usbdi.h3
2 files changed, 78 insertions, 0 deletions
diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c
index 4ee88efdf900..8c9815d1eefa 100644
--- a/sys/dev/usb/usb_transfer.c
+++ b/sys/dev/usb/usb_transfer.c
@@ -373,6 +373,81 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm,
#endif
/*------------------------------------------------------------------------*
+ * usbd_get_max_frame_length
+ *
+ * This function returns the maximum single frame length as computed by
+ * usbd_transfer_setup(). It is useful when computing buffer sizes for
+ * devices having multiple alternate settings. The SuperSpeed endpoint
+ * companion pointer is allowed to be NULL.
+ *------------------------------------------------------------------------*/
+uint32_t
+usbd_get_max_frame_length(const struct usb_endpoint_descriptor *edesc,
+ const struct usb_endpoint_ss_comp_descriptor *ecomp,
+ enum usb_dev_speed speed)
+{
+ uint32_t max_packet_size;
+ uint32_t max_packet_count;
+ uint8_t type;
+
+ max_packet_size = UGETW(edesc->wMaxPacketSize);
+ max_packet_count = 1;
+ type = (edesc->bmAttributes & UE_XFERTYPE);
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ switch (type) {
+ case UE_ISOCHRONOUS:
+ case UE_INTERRUPT:
+ max_packet_count +=
+ (max_packet_size >> 11) & 3;
+
+ /* check for invalid max packet count */
+ if (max_packet_count > 3)
+ max_packet_count = 3;
+ break;
+ default:
+ break;
+ }
+ max_packet_size &= 0x7FF;
+ break;
+ case USB_SPEED_SUPER:
+ max_packet_count += (max_packet_size >> 11) & 3;
+
+ if (ecomp != NULL)
+ max_packet_count += ecomp->bMaxBurst;
+
+ if ((max_packet_count == 0) ||
+ (max_packet_count > 16))
+ max_packet_count = 16;
+
+ switch (type) {
+ case UE_CONTROL:
+ max_packet_count = 1;
+ break;
+ case UE_ISOCHRONOUS:
+ if (ecomp != NULL) {
+ uint8_t mult;
+
+ mult = UE_GET_SS_ISO_MULT(
+ ecomp->bmAttributes) + 1;
+ if (mult > 3)
+ mult = 3;
+
+ max_packet_count *= mult;
+ }
+ break;
+ default:
+ break;
+ }
+ max_packet_size &= 0x7FF;
+ break;
+ default:
+ break;
+ }
+ return (max_packet_size * max_packet_count);
+}
+
+/*------------------------------------------------------------------------*
* usbd_transfer_setup_sub - transfer setup subroutine
*
* This function must be called from the "xfer_setup" callback of the
diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h
index a125184fd87d..b5098f2c2307 100644
--- a/sys/dev/usb/usbdi.h
+++ b/sys/dev/usb/usbdi.h
@@ -519,6 +519,9 @@ uint8_t usbd_get_interface_altindex(struct usb_interface *iface);
usb_error_t usbd_set_alt_interface_index(struct usb_device *udev,
uint8_t iface_index, uint8_t alt_index);
uint32_t usbd_get_isoc_fps(struct usb_device *udev);
+uint32_t usbd_get_max_frame_length(const struct usb_endpoint_descriptor *,
+ const struct usb_endpoint_ss_comp_descriptor *,
+ enum usb_dev_speed);
usb_error_t usbd_transfer_setup(struct usb_device *udev,
const uint8_t *ifaces, struct usb_xfer **pxfer,
const struct usb_config *setup_start, uint16_t n_setup,