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   */
snd_usb_combine_bytes(unsigned char * bytes,int size)16  unsigned 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   */
snd_usb_find_desc(void * descstart,int desclen,void * after,u8 dtype)31  void *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   */
snd_usb_find_csint_desc(void * buffer,int buflen,void * after,u8 dsubtype)54  void *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   */
snd_usb_ctl_msg(struct usb_device * dev,unsigned int pipe,__u8 request,__u8 requesttype,__u16 value,__u16 index,void * data,__u16 size)70  int 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  
snd_usb_parse_datainterval(struct snd_usb_audio * chip,struct usb_host_interface * alts)106  unsigned 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_WIRELESS:
112  	case USB_SPEED_SUPER:
113  	case USB_SPEED_SUPER_PLUS:
114  		if (get_endpoint(alts, 0)->bInterval >= 1 &&
115  		    get_endpoint(alts, 0)->bInterval <= 4)
116  			return get_endpoint(alts, 0)->bInterval - 1;
117  		break;
118  	default:
119  		break;
120  	}
121  	return 0;
122  }
123  
124  struct usb_host_interface *
snd_usb_get_host_interface(struct snd_usb_audio * chip,int ifnum,int altsetting)125  snd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting)
126  {
127  	struct usb_interface *iface;
128  
129  	iface = usb_ifnum_to_if(chip->dev, ifnum);
130  	if (!iface)
131  		return NULL;
132  	return usb_altnum_to_altsetting(iface, altsetting);
133  }
134