summaryrefslogtreecommitdiffstats
path: root/drivers/usb/common/debug.c
blob: f204cec8d380aa63a60c7be0903ab5565612b811 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
// SPDX-License-Identifier: GPL-2.0
/*
 * Common USB debugging functions
 *
 * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
 *
 * Authors: Felipe Balbi <balbi@ti.com>,
 *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
 */

#include <linux/kernel.h>
#include <linux/usb/ch9.h>

static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
				  __u16 wLength, char *str, size_t size)
{
	switch (bRequestType & USB_RECIP_MASK) {
	case USB_RECIP_DEVICE:
		snprintf(str, size, "Get Device Status(Length = %d)", wLength);
		break;
	case USB_RECIP_INTERFACE:
		snprintf(str, size,
			 "Get Interface Status(Intf = %d, Length = %d)",
			 wIndex, wLength);
		break;
	case USB_RECIP_ENDPOINT:
		snprintf(str, size, "Get Endpoint Status(ep%d%s)",
			 wIndex & ~USB_DIR_IN,
			 wIndex & USB_DIR_IN ? "in" : "out");
		break;
	}
}

static const char *usb_decode_device_feature(u16 wValue)
{
	switch (wValue) {
	case USB_DEVICE_SELF_POWERED:
		return "Self Powered";
	case USB_DEVICE_REMOTE_WAKEUP:
		return "Remote Wakeup";
	case USB_DEVICE_TEST_MODE:
		return "Test Mode";
	case USB_DEVICE_U1_ENABLE:
		return "U1 Enable";
	case USB_DEVICE_U2_ENABLE:
		return "U2 Enable";
	case USB_DEVICE_LTM_ENABLE:
		return "LTM Enable";
	default:
		return "UNKNOWN";
	}
}

static const char *usb_decode_test_mode(u16 wIndex)
{
	switch (wIndex) {
	case USB_TEST_J:
		return ": TEST_J";
	case USB_TEST_K:
		return ": TEST_K";
	case USB_TEST_SE0_NAK:
		return ": TEST_SE0_NAK";
	case USB_TEST_PACKET:
		return ": TEST_PACKET";
	case USB_TEST_FORCE_ENABLE:
		return ": TEST_FORCE_EN";
	default:
		return ": UNKNOWN";
	}
}

static void usb_decode_set_clear_feature(__u8 bRequestType,
					 __u8 bRequest, __u16 wValue,
					 __u16 wIndex, char *str, size_t size)
{
	switch (bRequestType & USB_RECIP_MASK) {
	case USB_RECIP_DEVICE:
		snprintf(str, size, "%s Device Feature(%s%s)",
			 bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
			 usb_decode_device_feature(wValue),
			 wValue == USB_DEVICE_TEST_MODE ?
			 usb_decode_test_mode(wIndex) : "");
		break;
	case USB_RECIP_INTERFACE:
		snprintf(str, size, "%s Interface Feature(%s)",
			 bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
			 wValue == USB_INTRF_FUNC_SUSPEND ?
			 "Function Suspend" : "UNKNOWN");
		break;
	case USB_RECIP_ENDPOINT:
		snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
			 bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
			 wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
			 wIndex & ~USB_DIR_IN,
			 wIndex & USB_DIR_IN ? "in" : "out");
		break;
	}
}

static void usb_decode_set_address(__u16 wValue, char *str, size_t size)
{
	snprintf(str, size, "Set Address(Addr = %02x)", wValue);
}

static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
					  __u16 wValue, __u16 wIndex,
					  __u16 wLength, char *str, size_t size)
{
	char *s;

	switch (wValue >> 8) {
	case USB_DT_DEVICE:
		s = "Device";
		break;
	case USB_DT_CONFIG:
		s = "Configuration";
		break;
	case USB_DT_STRING:
		s = "String";
		break;
	case USB_DT_INTERFACE:
		s = "Interface";
		break;
	case USB_DT_ENDPOINT:
		s = "Endpoint";
		break;
	case USB_DT_DEVICE_QUALIFIER:
		s = "Device Qualifier";
		break;
	case USB_DT_OTHER_SPEED_CONFIG:
		s = "Other Speed Config";
		break;
	case USB_DT_INTERFACE_POWER:
		s = "Interface Power";
		break;
	case USB_DT_OTG:
		s = "OTG";
		break;
	case USB_DT_DEBUG:
		s = "Debug";
		break;
	case USB_DT_INTERFACE_ASSOCIATION:
		s = "Interface Association";
		break;
	case USB_DT_BOS:
		s = "BOS";
		break;
	case USB_DT_DEVICE_CAPABILITY:
		s = "Device Capability";
		break;
	case USB_DT_PIPE_USAGE:
		s = "Pipe Usage";
		break;
	case USB_DT_SS_ENDPOINT_COMP:
		s = "SS Endpoint Companion";
		break;
	case USB_DT_SSP_ISOC_ENDPOINT_COMP:
		s = "SSP Isochronous Endpoint Companion";
		break;
	default:
		s = "UNKNOWN";
		break;
	}

	snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
		bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
		s, wValue & 0xff, wLength);
}

static void usb_decode_get_configuration(__u16 wLength, char *str, size_t size)
{
	snprintf(str, size, "Get Configuration(Length = %d)", wLength);
}

static void usb_decode_set_configuration(__u8 wValue, char *str, size_t size)
{
	snprintf(str, size, "Set Configuration(Config = %d)", wValue);
}

static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str,
				size_t size)
{
	snprintf(str, size, "Get Interface(Intf = %d, Length = %d)",
		 wIndex, wLength);
}

static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str,
				size_t size)
{
	snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)",
		 wIndex, wValue);
}

static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength,
				   char *str, size_t size)
{
	snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)",
		 wIndex, wLength);
}

static void usb_decode_set_sel(__u16 wLength, char *str, size_t size)
{
	snprintf(str, size, "Set SEL(Length = %d)", wLength);
}

static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size)
{
	snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue);
}

static void usb_decode_ctrl_generic(char *str, size_t size, __u8 bRequestType,
				    __u8 bRequest, __u16 wValue, __u16 wIndex,
				    __u16 wLength)
{
	u8 recip = bRequestType & USB_RECIP_MASK;
	u8 type = bRequestType & USB_TYPE_MASK;

	snprintf(str, size,
		 "Type=%s Recipient=%s Dir=%s bRequest=%u wValue=%u wIndex=%u wLength=%u",
		 (type == USB_TYPE_STANDARD)    ? "Standard" :
		 (type == USB_TYPE_VENDOR)      ? "Vendor" :
		 (type == USB_TYPE_CLASS)       ? "Class" : "Unknown",
		 (recip == USB_RECIP_DEVICE)    ? "Device" :
		 (recip == USB_RECIP_INTERFACE) ? "Interface" :
		 (recip == USB_RECIP_ENDPOINT)  ? "Endpoint" : "Unknown",
		 (bRequestType & USB_DIR_IN)    ? "IN" : "OUT",
		 bRequest, wValue, wIndex, wLength);
}

static void usb_decode_ctrl_standard(char *str, size_t size, __u8 bRequestType,
				     __u8 bRequest, __u16 wValue, __u16 wIndex,
				     __u16 wLength)
{
	switch (bRequest) {
	case USB_REQ_GET_STATUS:
		usb_decode_get_status(bRequestType, wIndex, wLength, str, size);
		break;
	case USB_REQ_CLEAR_FEATURE:
	case USB_REQ_SET_FEATURE:
		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
					     wIndex, str, size);
		break;
	case USB_REQ_SET_ADDRESS:
		usb_decode_set_address(wValue, str, size);
		break;
	case USB_REQ_GET_DESCRIPTOR:
	case USB_REQ_SET_DESCRIPTOR:
		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
					      wIndex, wLength, str, size);
		break;
	case USB_REQ_GET_CONFIGURATION:
		usb_decode_get_configuration(wLength, str, size);
		break;
	case USB_REQ_SET_CONFIGURATION:
		usb_decode_set_configuration(wValue, str, size);
		break;
	case USB_REQ_GET_INTERFACE:
		usb_decode_get_intf(wIndex, wLength, str, size);
		break;
	case USB_REQ_SET_INTERFACE:
		usb_decode_set_intf(wValue, wIndex, str, size);
		break;
	case USB_REQ_SYNCH_FRAME:
		usb_decode_synch_frame(wIndex, wLength, str, size);
		break;
	case USB_REQ_SET_SEL:
		usb_decode_set_sel(wLength, str, size);
		break;
	case USB_REQ_SET_ISOCH_DELAY:
		usb_decode_set_isoch_delay(wValue, str, size);
		break;
	default:
		usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
					wValue, wIndex, wLength);
		break;
	}
}

/**
 * usb_decode_ctrl - Returns human readable representation of control request.
 * @str: buffer to return a human-readable representation of control request.
 *       This buffer should have about 200 bytes.
 * @size: size of str buffer.
 * @bRequestType: matches the USB bmRequestType field
 * @bRequest: matches the USB bRequest field
 * @wValue: matches the USB wValue field (CPU byte order)
 * @wIndex: matches the USB wIndex field (CPU byte order)
 * @wLength: matches the USB wLength field (CPU byte order)
 *
 * Function returns decoded, formatted and human-readable description of
 * control request packet.
 *
 * The usage scenario for this is for tracepoints, so function as a return
 * use the same value as in parameters. This approach allows to use this
 * function in TP_printk
 *
 * Important: wValue, wIndex, wLength parameters before invoking this function
 * should be processed by le16_to_cpu macro.
 */
const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
			    __u8 bRequest, __u16 wValue, __u16 wIndex,
			    __u16 wLength)
{
	switch (bRequestType & USB_TYPE_MASK) {
	case USB_TYPE_STANDARD:
		usb_decode_ctrl_standard(str, size, bRequestType, bRequest,
					 wValue, wIndex, wLength);
		break;
	case USB_TYPE_VENDOR:
	case USB_TYPE_CLASS:
	default:
		usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
					wValue, wIndex, wLength);
		break;
	}

	return str;
}
EXPORT_SYMBOL_GPL(usb_decode_ctrl);