1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Common USB debugging functions |
4 | * |
5 | * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com |
6 | * |
7 | * Authors: Felipe Balbi <balbi@ti.com>, |
8 | * Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/usb/ch9.h> |
13 | |
14 | static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex, |
15 | __u16 wLength, char *str, size_t size) |
16 | { |
17 | switch (bRequestType & USB_RECIP_MASK) { |
18 | case USB_RECIP_DEVICE: |
19 | snprintf(buf: str, size, fmt: "Get Device Status(Length = %d)" , wLength); |
20 | break; |
21 | case USB_RECIP_INTERFACE: |
22 | snprintf(buf: str, size, |
23 | fmt: "Get Interface Status(Intf = %d, Length = %d)" , |
24 | wIndex, wLength); |
25 | break; |
26 | case USB_RECIP_ENDPOINT: |
27 | snprintf(buf: str, size, fmt: "Get Endpoint Status(ep%d%s)" , |
28 | wIndex & ~USB_DIR_IN, |
29 | wIndex & USB_DIR_IN ? "in" : "out" ); |
30 | break; |
31 | } |
32 | } |
33 | |
34 | static const char *usb_decode_device_feature(u16 wValue) |
35 | { |
36 | switch (wValue) { |
37 | case USB_DEVICE_SELF_POWERED: |
38 | return "Self Powered" ; |
39 | case USB_DEVICE_REMOTE_WAKEUP: |
40 | return "Remote Wakeup" ; |
41 | case USB_DEVICE_TEST_MODE: |
42 | return "Test Mode" ; |
43 | case USB_DEVICE_U1_ENABLE: |
44 | return "U1 Enable" ; |
45 | case USB_DEVICE_U2_ENABLE: |
46 | return "U2 Enable" ; |
47 | case USB_DEVICE_LTM_ENABLE: |
48 | return "LTM Enable" ; |
49 | default: |
50 | return "UNKNOWN" ; |
51 | } |
52 | } |
53 | |
54 | static const char *usb_decode_test_mode(u16 wIndex) |
55 | { |
56 | switch (wIndex) { |
57 | case USB_TEST_J: |
58 | return ": TEST_J" ; |
59 | case USB_TEST_K: |
60 | return ": TEST_K" ; |
61 | case USB_TEST_SE0_NAK: |
62 | return ": TEST_SE0_NAK" ; |
63 | case USB_TEST_PACKET: |
64 | return ": TEST_PACKET" ; |
65 | case USB_TEST_FORCE_ENABLE: |
66 | return ": TEST_FORCE_EN" ; |
67 | default: |
68 | return ": UNKNOWN" ; |
69 | } |
70 | } |
71 | |
72 | static void usb_decode_set_clear_feature(__u8 bRequestType, |
73 | __u8 bRequest, __u16 wValue, |
74 | __u16 wIndex, char *str, size_t size) |
75 | { |
76 | switch (bRequestType & USB_RECIP_MASK) { |
77 | case USB_RECIP_DEVICE: |
78 | snprintf(buf: str, size, fmt: "%s Device Feature(%s%s)" , |
79 | bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set" , |
80 | usb_decode_device_feature(wValue), |
81 | wValue == USB_DEVICE_TEST_MODE ? |
82 | usb_decode_test_mode(wIndex) : "" ); |
83 | break; |
84 | case USB_RECIP_INTERFACE: |
85 | snprintf(buf: str, size, fmt: "%s Interface Feature(%s)" , |
86 | bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set" , |
87 | wValue == USB_INTRF_FUNC_SUSPEND ? |
88 | "Function Suspend" : "UNKNOWN" ); |
89 | break; |
90 | case USB_RECIP_ENDPOINT: |
91 | snprintf(buf: str, size, fmt: "%s Endpoint Feature(%s ep%d%s)" , |
92 | bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set" , |
93 | wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN" , |
94 | wIndex & ~USB_DIR_IN, |
95 | wIndex & USB_DIR_IN ? "in" : "out" ); |
96 | break; |
97 | } |
98 | } |
99 | |
100 | static void usb_decode_set_address(__u16 wValue, char *str, size_t size) |
101 | { |
102 | snprintf(buf: str, size, fmt: "Set Address(Addr = %02x)" , wValue); |
103 | } |
104 | |
105 | static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest, |
106 | __u16 wValue, __u16 wIndex, |
107 | __u16 wLength, char *str, size_t size) |
108 | { |
109 | char *s; |
110 | |
111 | switch (wValue >> 8) { |
112 | case USB_DT_DEVICE: |
113 | s = "Device" ; |
114 | break; |
115 | case USB_DT_CONFIG: |
116 | s = "Configuration" ; |
117 | break; |
118 | case USB_DT_STRING: |
119 | s = "String" ; |
120 | break; |
121 | case USB_DT_INTERFACE: |
122 | s = "Interface" ; |
123 | break; |
124 | case USB_DT_ENDPOINT: |
125 | s = "Endpoint" ; |
126 | break; |
127 | case USB_DT_DEVICE_QUALIFIER: |
128 | s = "Device Qualifier" ; |
129 | break; |
130 | case USB_DT_OTHER_SPEED_CONFIG: |
131 | s = "Other Speed Config" ; |
132 | break; |
133 | case USB_DT_INTERFACE_POWER: |
134 | s = "Interface Power" ; |
135 | break; |
136 | case USB_DT_OTG: |
137 | s = "OTG" ; |
138 | break; |
139 | case USB_DT_DEBUG: |
140 | s = "Debug" ; |
141 | break; |
142 | case USB_DT_INTERFACE_ASSOCIATION: |
143 | s = "Interface Association" ; |
144 | break; |
145 | case USB_DT_BOS: |
146 | s = "BOS" ; |
147 | break; |
148 | case USB_DT_DEVICE_CAPABILITY: |
149 | s = "Device Capability" ; |
150 | break; |
151 | case USB_DT_PIPE_USAGE: |
152 | s = "Pipe Usage" ; |
153 | break; |
154 | case USB_DT_SS_ENDPOINT_COMP: |
155 | s = "SS Endpoint Companion" ; |
156 | break; |
157 | case USB_DT_SSP_ISOC_ENDPOINT_COMP: |
158 | s = "SSP Isochronous Endpoint Companion" ; |
159 | break; |
160 | default: |
161 | s = "UNKNOWN" ; |
162 | break; |
163 | } |
164 | |
165 | snprintf(buf: str, size, fmt: "%s %s Descriptor(Index = %d, Length = %d)" , |
166 | bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set" , |
167 | s, wValue & 0xff, wLength); |
168 | } |
169 | |
170 | static void usb_decode_get_configuration(__u16 wLength, char *str, size_t size) |
171 | { |
172 | snprintf(buf: str, size, fmt: "Get Configuration(Length = %d)" , wLength); |
173 | } |
174 | |
175 | static void usb_decode_set_configuration(__u8 wValue, char *str, size_t size) |
176 | { |
177 | snprintf(buf: str, size, fmt: "Set Configuration(Config = %d)" , wValue); |
178 | } |
179 | |
180 | static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str, |
181 | size_t size) |
182 | { |
183 | snprintf(buf: str, size, fmt: "Get Interface(Intf = %d, Length = %d)" , |
184 | wIndex, wLength); |
185 | } |
186 | |
187 | static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str, |
188 | size_t size) |
189 | { |
190 | snprintf(buf: str, size, fmt: "Set Interface(Intf = %d, Alt.Setting = %d)" , |
191 | wIndex, wValue); |
192 | } |
193 | |
194 | static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength, |
195 | char *str, size_t size) |
196 | { |
197 | snprintf(buf: str, size, fmt: "Synch Frame(Endpoint = %d, Length = %d)" , |
198 | wIndex, wLength); |
199 | } |
200 | |
201 | static void usb_decode_set_sel(__u16 wLength, char *str, size_t size) |
202 | { |
203 | snprintf(buf: str, size, fmt: "Set SEL(Length = %d)" , wLength); |
204 | } |
205 | |
206 | static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size) |
207 | { |
208 | snprintf(buf: str, size, fmt: "Set Isochronous Delay(Delay = %d ns)" , wValue); |
209 | } |
210 | |
211 | static void usb_decode_ctrl_generic(char *str, size_t size, __u8 bRequestType, |
212 | __u8 bRequest, __u16 wValue, __u16 wIndex, |
213 | __u16 wLength) |
214 | { |
215 | u8 recip = bRequestType & USB_RECIP_MASK; |
216 | u8 type = bRequestType & USB_TYPE_MASK; |
217 | |
218 | snprintf(buf: str, size, |
219 | fmt: "Type=%s Recipient=%s Dir=%s bRequest=%u wValue=%u wIndex=%u wLength=%u" , |
220 | (type == USB_TYPE_STANDARD) ? "Standard" : |
221 | (type == USB_TYPE_VENDOR) ? "Vendor" : |
222 | (type == USB_TYPE_CLASS) ? "Class" : "Unknown" , |
223 | (recip == USB_RECIP_DEVICE) ? "Device" : |
224 | (recip == USB_RECIP_INTERFACE) ? "Interface" : |
225 | (recip == USB_RECIP_ENDPOINT) ? "Endpoint" : "Unknown" , |
226 | (bRequestType & USB_DIR_IN) ? "IN" : "OUT" , |
227 | bRequest, wValue, wIndex, wLength); |
228 | } |
229 | |
230 | static void usb_decode_ctrl_standard(char *str, size_t size, __u8 bRequestType, |
231 | __u8 bRequest, __u16 wValue, __u16 wIndex, |
232 | __u16 wLength) |
233 | { |
234 | switch (bRequest) { |
235 | case USB_REQ_GET_STATUS: |
236 | usb_decode_get_status(bRequestType, wIndex, wLength, str, size); |
237 | break; |
238 | case USB_REQ_CLEAR_FEATURE: |
239 | case USB_REQ_SET_FEATURE: |
240 | usb_decode_set_clear_feature(bRequestType, bRequest, wValue, |
241 | wIndex, str, size); |
242 | break; |
243 | case USB_REQ_SET_ADDRESS: |
244 | usb_decode_set_address(wValue, str, size); |
245 | break; |
246 | case USB_REQ_GET_DESCRIPTOR: |
247 | case USB_REQ_SET_DESCRIPTOR: |
248 | usb_decode_get_set_descriptor(bRequestType, bRequest, wValue, |
249 | wIndex, wLength, str, size); |
250 | break; |
251 | case USB_REQ_GET_CONFIGURATION: |
252 | usb_decode_get_configuration(wLength, str, size); |
253 | break; |
254 | case USB_REQ_SET_CONFIGURATION: |
255 | usb_decode_set_configuration(wValue, str, size); |
256 | break; |
257 | case USB_REQ_GET_INTERFACE: |
258 | usb_decode_get_intf(wIndex, wLength, str, size); |
259 | break; |
260 | case USB_REQ_SET_INTERFACE: |
261 | usb_decode_set_intf(wValue, wIndex, str, size); |
262 | break; |
263 | case USB_REQ_SYNCH_FRAME: |
264 | usb_decode_synch_frame(wIndex, wLength, str, size); |
265 | break; |
266 | case USB_REQ_SET_SEL: |
267 | usb_decode_set_sel(wLength, str, size); |
268 | break; |
269 | case USB_REQ_SET_ISOCH_DELAY: |
270 | usb_decode_set_isoch_delay(wValue, str, size); |
271 | break; |
272 | default: |
273 | usb_decode_ctrl_generic(str, size, bRequestType, bRequest, |
274 | wValue, wIndex, wLength); |
275 | break; |
276 | } |
277 | } |
278 | |
279 | /** |
280 | * usb_decode_ctrl - Returns human readable representation of control request. |
281 | * @str: buffer to return a human-readable representation of control request. |
282 | * This buffer should have about 200 bytes. |
283 | * @size: size of str buffer. |
284 | * @bRequestType: matches the USB bmRequestType field |
285 | * @bRequest: matches the USB bRequest field |
286 | * @wValue: matches the USB wValue field (CPU byte order) |
287 | * @wIndex: matches the USB wIndex field (CPU byte order) |
288 | * @wLength: matches the USB wLength field (CPU byte order) |
289 | * |
290 | * Function returns decoded, formatted and human-readable description of |
291 | * control request packet. |
292 | * |
293 | * The usage scenario for this is for tracepoints, so function as a return |
294 | * use the same value as in parameters. This approach allows to use this |
295 | * function in TP_printk |
296 | * |
297 | * Important: wValue, wIndex, wLength parameters before invoking this function |
298 | * should be processed by le16_to_cpu macro. |
299 | */ |
300 | const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType, |
301 | __u8 bRequest, __u16 wValue, __u16 wIndex, |
302 | __u16 wLength) |
303 | { |
304 | switch (bRequestType & USB_TYPE_MASK) { |
305 | case USB_TYPE_STANDARD: |
306 | usb_decode_ctrl_standard(str, size, bRequestType, bRequest, |
307 | wValue, wIndex, wLength); |
308 | break; |
309 | case USB_TYPE_VENDOR: |
310 | case USB_TYPE_CLASS: |
311 | default: |
312 | usb_decode_ctrl_generic(str, size, bRequestType, bRequest, |
313 | wValue, wIndex, wLength); |
314 | break; |
315 | } |
316 | |
317 | return str; |
318 | } |
319 | EXPORT_SYMBOL_GPL(usb_decode_ctrl); |
320 | |