// SPDX-License-Identifier: GPL-2.0
/*
* Released under the GPLv2 only.
*/
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/hcd.h>
#include <linux/usb/quirks.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/device.h>
#include <asm/byteorder.h>
#include "usb.h"
#define USB_MAXALTSETTING 128 /* Hard limit */
#define USB_MAXCONFIG 8 /* Arbitrary limit */
static int find_next_descriptor(unsigned char *buffer, int size,
int dt1, int dt2, int *num_skipped)
{
struct usb_descriptor_header *h;
int n = 0;
unsigned char *buffer0 = buffer;
/* Find the next descriptor of type dt1 or dt2 */
while (size > 0) {
h = (struct usb_descriptor_header *) buffer;
if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
break;
buffer += h->bLength;
size -= h->bLength;
++n;
}
/* Store the number of descriptors skipped and return the
* number of bytes skipped */
if (num_skipped)
*num_skipped = n;
return buffer - buffer0;
}
static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
unsigned char *buffer, int size)
{
struct usb_ssp_isoc_ep_comp_descriptor *desc;
/*
* The SuperSpeedPlus Isoc endpoint companion descriptor immediately
* follows the SuperSpeed Endpoint Companion descriptor
*/
desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer;
if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP ||
size < USB_DT_SSP_ISOC_EP_COMP_SIZE) {
dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion"
"for config %d interface %d altsetting %d ep %d.\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);
return;
}
memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE);
}
static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev,
int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
unsigned char *buffer, int size)
{
struct usb_eusb2_isoc_ep_comp_descriptor *desc;
struct usb_descriptor_header *h;
/*
* eUSB2 isochronous endpoint companion descriptor for this endpoint
* shall be declared before the next endpoint or interface descriptor
*/
while (size >= USB_DT_EUSB2_ISOC_EP_COMP_SIZE) {
h = (struct usb_descriptor_header *)buffer;
if (h->bDescriptorType == USB_DT_EUSB2_ISOC_ENDPOINT_COMP) {
desc = (struct usb_eusb2_isoc_ep_comp_descriptor *)buffer;
ep->eusb2_isoc_ep_comp = *desc;
return;
}
if (h->bDescriptorType == USB_DT_ENDPOINT ||
h->bDescriptorType == USB_DT_INTERFACE)
break;
buffer += h->bLength;
size -= h->bLength;
}
dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n",
ep->desc.bEndpointAddress, cfgno, inum, asnum);
}
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
int inum, int asnum, struct usb_host_endpoint