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