1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 */
4
5#include <linux/init.h>
6#include <linux/slab.h>
7#include <linux/usb.h>
8
9#include "usbaudio.h"
10#include "helper.h"
11#include "quirks.h"
12
13/*
14 * combine bytes and get an integer value
15 */
16unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size)
17{
18	switch (size) {
19	case 1:  return *bytes;
20	case 2:  return combine_word(bytes);
21	case 3:  return combine_triple(bytes);
22	case 4:  return combine_quad(bytes);
23	default: return 0;
24	}
25}
26
27/*
28 * parse descriptor buffer and return the pointer starting the given
29 * descriptor type.
30 */
31void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype)
32{
33	u8 *p, *end, *next;
34
35	p = descstart;
36	end = p + desclen;
37	for (; p < end;) {
38		if (p[0] < 2)
39			return NULL;
40		next = p + p[0];
41		if (next > end)
42			return NULL;
43		if (p[1] == dtype && (!after || (void *)p > after)) {
44			return p;
45		}
46		p = next;
47	}
48	return NULL;
49}
50
51/*
52 * find a class-specified interface descriptor with the given subtype.
53 */
54void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype)
55{
56	unsigned char *p = after;
57
58	while ((p = snd_usb_find_desc(buffer, buflen, p,
59				      USB_DT_CS_INTERFACE)) != NULL) {
60		if (p[0] >= 3 && p[2] == dsubtype)
61			return p;
62	}
63	return NULL;
64}
65
66/*
67 * Wrapper for usb_control_msg().
68 * Allocates a temp buffer to prevent dmaing from/to the stack.
69 */
70int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
71		    __u8 requesttype, __u16 value, __u16 index, void *data,
72		    __u16 size)
73{
74	int err;
75	void *buf = NULL;
76	int timeout;
77
78	if (usb_pipe_type_check(dev, pipe))
79		return -EINVAL;
80
81	if (size > 0) {
82		buf = kmemdup(data, size, GFP_KERNEL);
83		if (!buf)
84			return -ENOMEM;
85	}
86
87	if (requesttype & USB_DIR_IN)
88		timeout = USB_CTRL_GET_TIMEOUT;
89	else
90		timeout = USB_CTRL_SET_TIMEOUT;
91
92	err = usb_control_msg(dev, pipe, request, requesttype,
93			      value, index, buf, size, timeout);
94
95	if (size > 0) {
96		memcpy(data, buf, size);
97		kfree(buf);
98	}
99
100	snd_usb_ctl_msg_quirk(dev, pipe, request, requesttype,
101			      value, index, data, size);
102
103	return err;
104}
105
106unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
107					 struct usb_host_interface *alts)
108{
109	switch (snd_usb_get_speed(chip->dev)) {
110	case USB_SPEED_HIGH:
111	case USB_SPEED_SUPER:
112	case USB_SPEED_SUPER_PLUS:
113		if (get_endpoint(alts, 0)->bInterval >= 1 &&
114		    get_endpoint(alts, 0)->bInterval <= 4)
115			return get_endpoint(alts, 0)->bInterval - 1;
116		break;
117	default:
118		break;
119	}
120	return 0;
121}
122
123struct usb_host_interface *
124snd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting)
125{
126	struct usb_interface *iface;
127
128	iface = usb_ifnum_to_if(chip->dev, ifnum);
129	if (!iface)
130		return NULL;
131	return usb_altnum_to_altsetting(iface, altsetting);
132}
133