162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/init.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/usb.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "usbaudio.h"
1062306a36Sopenharmony_ci#include "helper.h"
1162306a36Sopenharmony_ci#include "quirks.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * combine bytes and get an integer value
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ciunsigned int snd_usb_combine_bytes(unsigned char *bytes, int size)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	switch (size) {
1962306a36Sopenharmony_ci	case 1:  return *bytes;
2062306a36Sopenharmony_ci	case 2:  return combine_word(bytes);
2162306a36Sopenharmony_ci	case 3:  return combine_triple(bytes);
2262306a36Sopenharmony_ci	case 4:  return combine_quad(bytes);
2362306a36Sopenharmony_ci	default: return 0;
2462306a36Sopenharmony_ci	}
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * parse descriptor buffer and return the pointer starting the given
2962306a36Sopenharmony_ci * descriptor type.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_civoid *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	u8 *p, *end, *next;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	p = descstart;
3662306a36Sopenharmony_ci	end = p + desclen;
3762306a36Sopenharmony_ci	for (; p < end;) {
3862306a36Sopenharmony_ci		if (p[0] < 2)
3962306a36Sopenharmony_ci			return NULL;
4062306a36Sopenharmony_ci		next = p + p[0];
4162306a36Sopenharmony_ci		if (next > end)
4262306a36Sopenharmony_ci			return NULL;
4362306a36Sopenharmony_ci		if (p[1] == dtype && (!after || (void *)p > after)) {
4462306a36Sopenharmony_ci			return p;
4562306a36Sopenharmony_ci		}
4662306a36Sopenharmony_ci		p = next;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci	return NULL;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * find a class-specified interface descriptor with the given subtype.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_civoid *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	unsigned char *p = after;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	while ((p = snd_usb_find_desc(buffer, buflen, p,
5962306a36Sopenharmony_ci				      USB_DT_CS_INTERFACE)) != NULL) {
6062306a36Sopenharmony_ci		if (p[0] >= 3 && p[2] == dsubtype)
6162306a36Sopenharmony_ci			return p;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	return NULL;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * Wrapper for usb_control_msg().
6862306a36Sopenharmony_ci * Allocates a temp buffer to prevent dmaing from/to the stack.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ciint snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
7162306a36Sopenharmony_ci		    __u8 requesttype, __u16 value, __u16 index, void *data,
7262306a36Sopenharmony_ci		    __u16 size)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	int err;
7562306a36Sopenharmony_ci	void *buf = NULL;
7662306a36Sopenharmony_ci	int timeout;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (usb_pipe_type_check(dev, pipe))
7962306a36Sopenharmony_ci		return -EINVAL;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (size > 0) {
8262306a36Sopenharmony_ci		buf = kmemdup(data, size, GFP_KERNEL);
8362306a36Sopenharmony_ci		if (!buf)
8462306a36Sopenharmony_ci			return -ENOMEM;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (requesttype & USB_DIR_IN)
8862306a36Sopenharmony_ci		timeout = USB_CTRL_GET_TIMEOUT;
8962306a36Sopenharmony_ci	else
9062306a36Sopenharmony_ci		timeout = USB_CTRL_SET_TIMEOUT;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	err = usb_control_msg(dev, pipe, request, requesttype,
9362306a36Sopenharmony_ci			      value, index, buf, size, timeout);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (size > 0) {
9662306a36Sopenharmony_ci		memcpy(data, buf, size);
9762306a36Sopenharmony_ci		kfree(buf);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	snd_usb_ctl_msg_quirk(dev, pipe, request, requesttype,
10162306a36Sopenharmony_ci			      value, index, data, size);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return err;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciunsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
10762306a36Sopenharmony_ci					 struct usb_host_interface *alts)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	switch (snd_usb_get_speed(chip->dev)) {
11062306a36Sopenharmony_ci	case USB_SPEED_HIGH:
11162306a36Sopenharmony_ci	case USB_SPEED_SUPER:
11262306a36Sopenharmony_ci	case USB_SPEED_SUPER_PLUS:
11362306a36Sopenharmony_ci		if (get_endpoint(alts, 0)->bInterval >= 1 &&
11462306a36Sopenharmony_ci		    get_endpoint(alts, 0)->bInterval <= 4)
11562306a36Sopenharmony_ci			return get_endpoint(alts, 0)->bInterval - 1;
11662306a36Sopenharmony_ci		break;
11762306a36Sopenharmony_ci	default:
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistruct usb_host_interface *
12462306a36Sopenharmony_cisnd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct usb_interface *iface;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	iface = usb_ifnum_to_if(chip->dev, ifnum);
12962306a36Sopenharmony_ci	if (!iface)
13062306a36Sopenharmony_ci		return NULL;
13162306a36Sopenharmony_ci	return usb_altnum_to_altsetting(iface, altsetting);
13262306a36Sopenharmony_ci}
133