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(descstart: buffer, desclen: buflen, after: 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(p: 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, data: buf, size, timeout); |
94 | |
95 | if (size > 0) { |
96 | memcpy(data, buf, size); |
97 | kfree(objp: 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(dev: chip->dev, ifnum); |
129 | if (!iface) |
130 | return NULL; |
131 | return usb_altnum_to_altsetting(intf: iface, altnum: altsetting); |
132 | } |
133 | |