uhci-debug.c (16011B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * UHCI-specific debugging code. Invaluable when something 4 * goes wrong, but don't get in my face. 5 * 6 * Kernel visible pointers are surrounded in []s and bus 7 * visible pointers are surrounded in ()s 8 * 9 * (C) Copyright 1999 Linus Torvalds 10 * (C) Copyright 1999-2001 Johannes Erdfelt 11 */ 12 13#include <linux/slab.h> 14#include <linux/kernel.h> 15#include <linux/debugfs.h> 16#include <asm/io.h> 17 18#include "uhci-hcd.h" 19 20#define EXTRA_SPACE 1024 21 22static struct dentry *uhci_debugfs_root; 23 24#ifdef CONFIG_DYNAMIC_DEBUG 25 26/* Handle REALLY large printks so we don't overflow buffers */ 27static void lprintk(char *buf) 28{ 29 char *p; 30 31 /* Just write one line at a time */ 32 while (buf) { 33 p = strchr(buf, '\n'); 34 if (p) 35 *p = 0; 36 printk(KERN_DEBUG "%s\n", buf); 37 buf = p; 38 if (buf) 39 buf++; 40 } 41} 42 43static int uhci_show_td(struct uhci_hcd *uhci, struct uhci_td *td, char *buf, 44 int len, int space) 45{ 46 char *out = buf; 47 char *spid; 48 u32 status, token; 49 50 status = td_status(uhci, td); 51 out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, 52 hc32_to_cpu(uhci, td->link)); 53 out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", 54 ((status >> 27) & 3), 55 (status & TD_CTRL_SPD) ? "SPD " : "", 56 (status & TD_CTRL_LS) ? "LS " : "", 57 (status & TD_CTRL_IOC) ? "IOC " : "", 58 (status & TD_CTRL_ACTIVE) ? "Active " : "", 59 (status & TD_CTRL_STALLED) ? "Stalled " : "", 60 (status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", 61 (status & TD_CTRL_BABBLE) ? "Babble " : "", 62 (status & TD_CTRL_NAK) ? "NAK " : "", 63 (status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", 64 (status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", 65 status & 0x7ff); 66 if (out - buf > len) 67 goto done; 68 69 token = td_token(uhci, td); 70 switch (uhci_packetid(token)) { 71 case USB_PID_SETUP: 72 spid = "SETUP"; 73 break; 74 case USB_PID_OUT: 75 spid = "OUT"; 76 break; 77 case USB_PID_IN: 78 spid = "IN"; 79 break; 80 default: 81 spid = "?"; 82 break; 83 } 84 85 out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ", 86 token >> 21, 87 ((token >> 19) & 1), 88 (token >> 15) & 15, 89 (token >> 8) & 127, 90 (token & 0xff), 91 spid); 92 out += sprintf(out, "(buf=%08x)\n", hc32_to_cpu(uhci, td->buffer)); 93 94done: 95 if (out - buf > len) 96 out += sprintf(out, " ...\n"); 97 return out - buf; 98} 99 100static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, 101 char *buf, int len, int space) 102{ 103 char *out = buf; 104 struct uhci_td *td; 105 int i, nactive, ninactive; 106 char *ptype; 107 108 109 out += sprintf(out, "urb_priv [%p] ", urbp); 110 out += sprintf(out, "urb [%p] ", urbp->urb); 111 out += sprintf(out, "qh [%p] ", urbp->qh); 112 out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe)); 113 out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), 114 (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT")); 115 if (out - buf > len) 116 goto done; 117 118 switch (usb_pipetype(urbp->urb->pipe)) { 119 case PIPE_ISOCHRONOUS: ptype = "ISO"; break; 120 case PIPE_INTERRUPT: ptype = "INT"; break; 121 case PIPE_BULK: ptype = "BLK"; break; 122 default: 123 case PIPE_CONTROL: ptype = "CTL"; break; 124 } 125 126 out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : "")); 127 out += sprintf(out, " Actlen=%d%s", urbp->urb->actual_length, 128 (urbp->qh->type == USB_ENDPOINT_XFER_CONTROL ? 129 "-8" : "")); 130 131 if (urbp->urb->unlinked) 132 out += sprintf(out, " Unlinked=%d", urbp->urb->unlinked); 133 out += sprintf(out, "\n"); 134 135 if (out - buf > len) 136 goto done; 137 138 i = nactive = ninactive = 0; 139 list_for_each_entry(td, &urbp->td_list, list) { 140 if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC && 141 (++i <= 10 || debug > 2)) { 142 out += sprintf(out, "%*s%d: ", space + 2, "", i); 143 out += uhci_show_td(uhci, td, out, 144 len - (out - buf), 0); 145 if (out - buf > len) 146 goto tail; 147 } else { 148 if (td_status(uhci, td) & TD_CTRL_ACTIVE) 149 ++nactive; 150 else 151 ++ninactive; 152 } 153 } 154 if (nactive + ninactive > 0) 155 out += sprintf(out, 156 "%*s[skipped %d inactive and %d active TDs]\n", 157 space, "", ninactive, nactive); 158done: 159 if (out - buf > len) 160 out += sprintf(out, " ...\n"); 161tail: 162 return out - buf; 163} 164 165static int uhci_show_qh(struct uhci_hcd *uhci, 166 struct uhci_qh *qh, char *buf, int len, int space) 167{ 168 char *out = buf; 169 int i, nurbs; 170 __hc32 element = qh_element(qh); 171 char *qtype; 172 173 switch (qh->type) { 174 case USB_ENDPOINT_XFER_ISOC: qtype = "ISO"; break; 175 case USB_ENDPOINT_XFER_INT: qtype = "INT"; break; 176 case USB_ENDPOINT_XFER_BULK: qtype = "BLK"; break; 177 case USB_ENDPOINT_XFER_CONTROL: qtype = "CTL"; break; 178 default: qtype = "Skel" ; break; 179 } 180 181 out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n", 182 space, "", qh, qtype, 183 hc32_to_cpu(uhci, qh->link), 184 hc32_to_cpu(uhci, element)); 185 if (qh->type == USB_ENDPOINT_XFER_ISOC) 186 out += sprintf(out, 187 "%*s period %d phase %d load %d us, frame %x desc [%p]\n", 188 space, "", qh->period, qh->phase, qh->load, 189 qh->iso_frame, qh->iso_packet_desc); 190 else if (qh->type == USB_ENDPOINT_XFER_INT) 191 out += sprintf(out, "%*s period %d phase %d load %d us\n", 192 space, "", qh->period, qh->phase, qh->load); 193 if (out - buf > len) 194 goto done; 195 196 if (element & UHCI_PTR_QH(uhci)) 197 out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); 198 199 if (element & UHCI_PTR_DEPTH(uhci)) 200 out += sprintf(out, "%*s Depth traverse\n", space, ""); 201 202 if (element & cpu_to_hc32(uhci, 8)) 203 out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, ""); 204 205 if (!(element & ~(UHCI_PTR_QH(uhci) | UHCI_PTR_DEPTH(uhci)))) 206 out += sprintf(out, "%*s Element is NULL (bug?)\n", space, ""); 207 208 if (out - buf > len) 209 goto done; 210 211 if (list_empty(&qh->queue)) { 212 out += sprintf(out, "%*s queue is empty\n", space, ""); 213 if (qh == uhci->skel_async_qh) { 214 out += uhci_show_td(uhci, uhci->term_td, out, 215 len - (out - buf), 0); 216 if (out - buf > len) 217 goto tail; 218 } 219 } else { 220 struct urb_priv *urbp = list_entry(qh->queue.next, 221 struct urb_priv, node); 222 struct uhci_td *td = list_entry(urbp->td_list.next, 223 struct uhci_td, list); 224 225 if (element != LINK_TO_TD(uhci, td)) 226 out += sprintf(out, "%*s Element != First TD\n", 227 space, ""); 228 i = nurbs = 0; 229 list_for_each_entry(urbp, &qh->queue, node) { 230 if (++i <= 10) { 231 out += uhci_show_urbp(uhci, urbp, out, 232 len - (out - buf), space + 2); 233 if (out - buf > len) 234 goto tail; 235 } 236 else 237 ++nurbs; 238 } 239 if (nurbs > 0) 240 out += sprintf(out, "%*s Skipped %d URBs\n", 241 space, "", nurbs); 242 } 243 244 if (out - buf > len) 245 goto done; 246 247 if (qh->dummy_td) { 248 out += sprintf(out, "%*s Dummy TD\n", space, ""); 249 out += uhci_show_td(uhci, qh->dummy_td, out, 250 len - (out - buf), 0); 251 if (out - buf > len) 252 goto tail; 253 } 254 255done: 256 if (out - buf > len) 257 out += sprintf(out, " ...\n"); 258tail: 259 return out - buf; 260} 261 262static int uhci_show_sc(int port, unsigned short status, char *buf) 263{ 264 return sprintf(buf, " stat%d = %04x %s%s%s%s%s%s%s%s%s%s\n", 265 port, 266 status, 267 (status & USBPORTSC_SUSP) ? " Suspend" : "", 268 (status & USBPORTSC_OCC) ? " OverCurrentChange" : "", 269 (status & USBPORTSC_OC) ? " OverCurrent" : "", 270 (status & USBPORTSC_PR) ? " Reset" : "", 271 (status & USBPORTSC_LSDA) ? " LowSpeed" : "", 272 (status & USBPORTSC_RD) ? " ResumeDetect" : "", 273 (status & USBPORTSC_PEC) ? " EnableChange" : "", 274 (status & USBPORTSC_PE) ? " Enabled" : "", 275 (status & USBPORTSC_CSC) ? " ConnectChange" : "", 276 (status & USBPORTSC_CCS) ? " Connected" : ""); 277} 278 279static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf) 280{ 281 char *rh_state; 282 283 switch (uhci->rh_state) { 284 case UHCI_RH_RESET: 285 rh_state = "reset"; break; 286 case UHCI_RH_SUSPENDED: 287 rh_state = "suspended"; break; 288 case UHCI_RH_AUTO_STOPPED: 289 rh_state = "auto-stopped"; break; 290 case UHCI_RH_RESUMING: 291 rh_state = "resuming"; break; 292 case UHCI_RH_SUSPENDING: 293 rh_state = "suspending"; break; 294 case UHCI_RH_RUNNING: 295 rh_state = "running"; break; 296 case UHCI_RH_RUNNING_NODEVS: 297 rh_state = "running, no devs"; break; 298 default: 299 rh_state = "?"; break; 300 } 301 return sprintf(buf, "Root-hub state: %s FSBR: %d\n", 302 rh_state, uhci->fsbr_is_on); 303} 304 305static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) 306{ 307 char *out = buf; 308 unsigned short usbcmd, usbstat, usbint, usbfrnum; 309 unsigned int flbaseadd; 310 unsigned char sof; 311 unsigned short portsc1, portsc2; 312 313 314 usbcmd = uhci_readw(uhci, USBCMD); 315 usbstat = uhci_readw(uhci, USBSTS); 316 usbint = uhci_readw(uhci, USBINTR); 317 usbfrnum = uhci_readw(uhci, USBFRNUM); 318 flbaseadd = uhci_readl(uhci, USBFLBASEADD); 319 sof = uhci_readb(uhci, USBSOF); 320 portsc1 = uhci_readw(uhci, USBPORTSC1); 321 portsc2 = uhci_readw(uhci, USBPORTSC2); 322 323 out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n", 324 usbcmd, 325 (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", 326 (usbcmd & USBCMD_CF) ? "CF " : "", 327 (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", 328 (usbcmd & USBCMD_FGR) ? "FGR " : "", 329 (usbcmd & USBCMD_EGSM) ? "EGSM " : "", 330 (usbcmd & USBCMD_GRESET) ? "GRESET " : "", 331 (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", 332 (usbcmd & USBCMD_RS) ? "RS " : ""); 333 if (out - buf > len) 334 goto done; 335 336 out += sprintf(out, " usbstat = %04x %s%s%s%s%s%s\n", 337 usbstat, 338 (usbstat & USBSTS_HCH) ? "HCHalted " : "", 339 (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", 340 (usbstat & USBSTS_HSE) ? "HostSystemError " : "", 341 (usbstat & USBSTS_RD) ? "ResumeDetect " : "", 342 (usbstat & USBSTS_ERROR) ? "USBError " : "", 343 (usbstat & USBSTS_USBINT) ? "USBINT " : ""); 344 if (out - buf > len) 345 goto done; 346 347 out += sprintf(out, " usbint = %04x\n", usbint); 348 out += sprintf(out, " usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, 349 0xfff & (4*(unsigned int)usbfrnum)); 350 out += sprintf(out, " flbaseadd = %08x\n", flbaseadd); 351 out += sprintf(out, " sof = %02x\n", sof); 352 if (out - buf > len) 353 goto done; 354 355 out += uhci_show_sc(1, portsc1, out); 356 if (out - buf > len) 357 goto done; 358 359 out += uhci_show_sc(2, portsc2, out); 360 if (out - buf > len) 361 goto done; 362 363 out += sprintf(out, 364 "Most recent frame: %x (%d) Last ISO frame: %x (%d)\n", 365 uhci->frame_number, uhci->frame_number & 1023, 366 uhci->last_iso_frame, uhci->last_iso_frame & 1023); 367 368done: 369 if (out - buf > len) 370 out += sprintf(out, " ...\n"); 371 return out - buf; 372} 373 374static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) 375{ 376 char *out = buf; 377 int i, j; 378 struct uhci_qh *qh; 379 struct uhci_td *td; 380 struct list_head *tmp, *head; 381 int nframes, nerrs; 382 __hc32 link; 383 __hc32 fsbr_link; 384 385 static const char * const qh_names[] = { 386 "unlink", "iso", "int128", "int64", "int32", "int16", 387 "int8", "int4", "int2", "async", "term" 388 }; 389 390 out += uhci_show_root_hub_state(uhci, out); 391 if (out - buf > len) 392 goto done; 393 out += sprintf(out, "HC status\n"); 394 out += uhci_show_status(uhci, out, len - (out - buf)); 395 if (out - buf > len) 396 goto tail; 397 398 out += sprintf(out, "Periodic load table\n"); 399 for (i = 0; i < MAX_PHASE; ++i) { 400 out += sprintf(out, "\t%d", uhci->load[i]); 401 if (i % 8 == 7) 402 *out++ = '\n'; 403 } 404 out += sprintf(out, "Total: %d, #INT: %d, #ISO: %d\n", 405 uhci->total_load, 406 uhci_to_hcd(uhci)->self.bandwidth_int_reqs, 407 uhci_to_hcd(uhci)->self.bandwidth_isoc_reqs); 408 if (debug <= 1) 409 goto tail; 410 411 out += sprintf(out, "Frame List\n"); 412 nframes = 10; 413 nerrs = 0; 414 for (i = 0; i < UHCI_NUMFRAMES; ++i) { 415 __hc32 qh_dma; 416 417 if (out - buf > len) 418 goto done; 419 j = 0; 420 td = uhci->frame_cpu[i]; 421 link = uhci->frame[i]; 422 if (!td) 423 goto check_link; 424 425 if (nframes > 0) { 426 out += sprintf(out, "- Frame %d -> (%08x)\n", 427 i, hc32_to_cpu(uhci, link)); 428 j = 1; 429 } 430 431 head = &td->fl_list; 432 tmp = head; 433 do { 434 td = list_entry(tmp, struct uhci_td, fl_list); 435 tmp = tmp->next; 436 if (link != LINK_TO_TD(uhci, td)) { 437 if (nframes > 0) { 438 out += sprintf(out, 439 " link does not match list entry!\n"); 440 if (out - buf > len) 441 goto done; 442 } else 443 ++nerrs; 444 } 445 if (nframes > 0) { 446 out += uhci_show_td(uhci, td, out, 447 len - (out - buf), 4); 448 if (out - buf > len) 449 goto tail; 450 } 451 link = td->link; 452 } while (tmp != head); 453 454check_link: 455 qh_dma = uhci_frame_skel_link(uhci, i); 456 if (link != qh_dma) { 457 if (nframes > 0) { 458 if (!j) { 459 out += sprintf(out, 460 "- Frame %d -> (%08x)\n", 461 i, hc32_to_cpu(uhci, link)); 462 j = 1; 463 } 464 out += sprintf(out, 465 " link does not match QH (%08x)!\n", 466 hc32_to_cpu(uhci, qh_dma)); 467 if (out - buf > len) 468 goto done; 469 } else 470 ++nerrs; 471 } 472 nframes -= j; 473 } 474 if (nerrs > 0) 475 out += sprintf(out, "Skipped %d bad links\n", nerrs); 476 477 out += sprintf(out, "Skeleton QHs\n"); 478 479 if (out - buf > len) 480 goto done; 481 482 fsbr_link = 0; 483 for (i = 0; i < UHCI_NUM_SKELQH; ++i) { 484 int cnt = 0; 485 486 qh = uhci->skelqh[i]; 487 out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); 488 out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4); 489 if (out - buf > len) 490 goto tail; 491 492 /* Last QH is the Terminating QH, it's different */ 493 if (i == SKEL_TERM) { 494 if (qh_element(qh) != LINK_TO_TD(uhci, uhci->term_td)) { 495 out += sprintf(out, 496 " skel_term_qh element is not set to term_td!\n"); 497 if (out - buf > len) 498 goto done; 499 } 500 link = fsbr_link; 501 if (!link) 502 link = LINK_TO_QH(uhci, uhci->skel_term_qh); 503 goto check_qh_link; 504 } 505 506 head = &qh->node; 507 tmp = head->next; 508 509 while (tmp != head) { 510 qh = list_entry(tmp, struct uhci_qh, node); 511 tmp = tmp->next; 512 if (++cnt <= 10) { 513 out += uhci_show_qh(uhci, qh, out, 514 len - (out - buf), 4); 515 if (out - buf > len) 516 goto tail; 517 } 518 if (!fsbr_link && qh->skel >= SKEL_FSBR) 519 fsbr_link = LINK_TO_QH(uhci, qh); 520 } 521 if ((cnt -= 10) > 0) 522 out += sprintf(out, " Skipped %d QHs\n", cnt); 523 524 link = UHCI_PTR_TERM(uhci); 525 if (i <= SKEL_ISO) 526 ; 527 else if (i < SKEL_ASYNC) 528 link = LINK_TO_QH(uhci, uhci->skel_async_qh); 529 else if (!uhci->fsbr_is_on) 530 ; 531 else 532 link = LINK_TO_QH(uhci, uhci->skel_term_qh); 533check_qh_link: 534 if (qh->link != link) 535 out += sprintf(out, 536 " last QH not linked to next skeleton!\n"); 537 538 if (out - buf > len) 539 goto done; 540 } 541 542done: 543 if (out - buf > len) 544 out += sprintf(out, " ...\n"); 545tail: 546 return out - buf; 547} 548 549#ifdef CONFIG_DEBUG_FS 550 551#define MAX_OUTPUT (64 * 1024) 552 553struct uhci_debug { 554 int size; 555 char *data; 556}; 557 558static int uhci_debug_open(struct inode *inode, struct file *file) 559{ 560 struct uhci_hcd *uhci = inode->i_private; 561 struct uhci_debug *up; 562 unsigned long flags; 563 564 up = kmalloc(sizeof(*up), GFP_KERNEL); 565 if (!up) 566 return -ENOMEM; 567 568 up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL); 569 if (!up->data) { 570 kfree(up); 571 return -ENOMEM; 572 } 573 574 up->size = 0; 575 spin_lock_irqsave(&uhci->lock, flags); 576 if (uhci->is_initialized) 577 up->size = uhci_sprint_schedule(uhci, up->data, 578 MAX_OUTPUT - EXTRA_SPACE); 579 spin_unlock_irqrestore(&uhci->lock, flags); 580 581 file->private_data = up; 582 583 return 0; 584} 585 586static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence) 587{ 588 struct uhci_debug *up = file->private_data; 589 return no_seek_end_llseek_size(file, off, whence, up->size); 590} 591 592static ssize_t uhci_debug_read(struct file *file, char __user *buf, 593 size_t nbytes, loff_t *ppos) 594{ 595 struct uhci_debug *up = file->private_data; 596 return simple_read_from_buffer(buf, nbytes, ppos, up->data, up->size); 597} 598 599static int uhci_debug_release(struct inode *inode, struct file *file) 600{ 601 struct uhci_debug *up = file->private_data; 602 603 kfree(up->data); 604 kfree(up); 605 606 return 0; 607} 608 609static const struct file_operations uhci_debug_operations = { 610 .owner = THIS_MODULE, 611 .open = uhci_debug_open, 612 .llseek = uhci_debug_lseek, 613 .read = uhci_debug_read, 614 .release = uhci_debug_release, 615}; 616#define UHCI_DEBUG_OPS 617 618#endif /* CONFIG_DEBUG_FS */ 619 620#else /* CONFIG_DYNAMIC_DEBUG*/ 621 622static inline void lprintk(char *buf) 623{} 624 625static inline int uhci_show_qh(struct uhci_hcd *uhci, 626 struct uhci_qh *qh, char *buf, int len, int space) 627{ 628 return 0; 629} 630 631static inline int uhci_sprint_schedule(struct uhci_hcd *uhci, 632 char *buf, int len) 633{ 634 return 0; 635} 636 637#endif