Merge tag 'asoc-fix-v6.4-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-starfive.git] / sound / usb / helper.c
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  */
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  */
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  */
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  */
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
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_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
123 struct usb_host_interface *
124 snd_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 }