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