1/* 2 * ADM5120 HCD (Host Controller Driver) for USB 3 * 4 * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org> 5 * 6 * This file was derived from: drivers/usb/host/ohci-dbg.c 7 * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> 8 * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License version 2 as published 12 * by the Free Software Foundation. 13 * 14 */ 15 16/*-------------------------------------------------------------------------*/ 17 18static inline char *ed_typestring(int ed_type) 19{ 20 switch (ed_type) { 21 case PIPE_CONTROL: 22 return "ctrl"; 23 case PIPE_BULK: 24 return "bulk"; 25 case PIPE_INTERRUPT: 26 return "intr"; 27 case PIPE_ISOCHRONOUS: 28 return "isoc"; 29 } 30 return "(bad ed_type)"; 31} 32 33static inline char *ed_statestring(int state) 34{ 35 switch (state) { 36 case ED_IDLE: 37 return "IDLE"; 38 case ED_UNLINK: 39 return "UNLINK"; 40 case ED_OPER: 41 return "OPER"; 42 } 43 return "?STATE"; 44} 45 46static inline char *pipestring(int pipe) 47{ 48 return ed_typestring(usb_pipetype(pipe)); 49} 50 51static inline char *td_pidstring(u32 info) 52{ 53 switch (info & TD_DP) { 54 case TD_DP_SETUP: 55 return "SETUP"; 56 case TD_DP_IN: 57 return "IN"; 58 case TD_DP_OUT: 59 return "OUT"; 60 } 61 return "?PID"; 62} 63 64static inline char *td_togglestring(u32 info) 65{ 66 switch (info & TD_T) { 67 case TD_T_DATA0: 68 return "DATA0"; 69 case TD_T_DATA1: 70 return "DATA1"; 71 case TD_T_CARRY: 72 return "CARRY"; 73 } 74 return "?TOGGLE"; 75} 76 77/*-------------------------------------------------------------------------*/ 78 79#ifdef DEBUG 80 81/* debug| print the main components of an URB 82 * small: 0) header + data packets 1) just header 83 */ 84static void __attribute__((unused)) 85urb_print(struct admhcd *ahcd, struct urb *urb, char *str, int small, int status) 86{ 87 unsigned int pipe = urb->pipe; 88 89 if (!urb->dev || !urb->dev->bus) { 90 admhc_dbg(ahcd, "%s URB: no dev", str); 91 return; 92 } 93 94#ifndef ADMHC_VERBOSE_DEBUG 95 if (status != 0) 96#endif 97 admhc_dbg(ahcd, "URB-%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d " 98 "stat=%d\n", 99 str, 100 urb, 101 usb_pipedevice(pipe), 102 usb_pipeendpoint(pipe), 103 usb_pipeout(pipe) ? "out" : "in", 104 pipestring(pipe), 105 urb->transfer_flags, 106 urb->actual_length, 107 urb->transfer_buffer_length, 108 status); 109 110#ifdef ADMHC_VERBOSE_DEBUG 111 if (!small) { 112 int i, len; 113 114 if (usb_pipecontrol(pipe)) { 115 admhc_dbg(ahcd, "setup(8):"); 116 for (i = 0; i < 8 ; i++) 117 printk(KERN_INFO" %02x", ((__u8 *)urb->setup_packet)[i]); 118 printk(KERN_INFO "\n"); 119 } 120 if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { 121 admhc_dbg(ahcd, "data(%d/%d):", 122 urb->actual_length, 123 urb->transfer_buffer_length); 124 len = usb_pipeout(pipe) ? 125 urb->transfer_buffer_length : urb->actual_length; 126 for (i = 0; i < 16 && i < len; i++) 127 printk(KERN_INFO " %02x", ((__u8 *)urb->transfer_buffer)[i]); 128 printk(KERN_INFO "%s stat:%d\n", i < len ? "..." : "", status); 129 } 130 } 131#endif /* ADMHC_VERBOSE_DEBUG */ 132} 133 134#define admhc_dbg_sw(ahcd, next, size, format, arg...) \ 135 do { \ 136 if (next) { \ 137 unsigned s_len; \ 138 s_len = scnprintf(*next, *size, format, ## arg); \ 139 *size -= s_len; *next += s_len; \ 140 } else \ 141 admhc_dbg(ahcd, format, ## arg); \ 142 } while (0); 143 144 145static void admhc_dump_intr_mask(struct admhcd *ahcd, char *label, u32 mask, 146 char **next, unsigned *size) 147{ 148 admhc_dbg_sw(ahcd, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", 149 label, 150 mask, 151 (mask & ADMHC_INTR_INTA) ? " INTA" : "", 152 (mask & ADMHC_INTR_FATI) ? " FATI" : "", 153 (mask & ADMHC_INTR_SWI) ? " SWI" : "", 154 (mask & ADMHC_INTR_TDC) ? " TDC" : "", 155 (mask & ADMHC_INTR_FNO) ? " FNO" : "", 156 (mask & ADMHC_INTR_SO) ? " SO" : "", 157 (mask & ADMHC_INTR_INSM) ? " INSM" : "", 158 (mask & ADMHC_INTR_BABI) ? " BABI" : "", 159 (mask & ADMHC_INTR_7) ? " !7!" : "", 160 (mask & ADMHC_INTR_6) ? " !6!" : "", 161 (mask & ADMHC_INTR_RESI) ? " RESI" : "", 162 (mask & ADMHC_INTR_SOFI) ? " SOFI" : "" 163 ); 164} 165 166static void maybe_print_eds(struct admhcd *ahcd, char *label, u32 value, 167 char **next, unsigned *size) 168{ 169 if (value) 170 admhc_dbg_sw(ahcd, next, size, "%s %08x\n", label, value); 171} 172 173static char *buss2string(int state) 174{ 175 switch (state) { 176 case ADMHC_BUSS_RESET: 177 return "reset"; 178 case ADMHC_BUSS_RESUME: 179 return "resume"; 180 case ADMHC_BUSS_OPER: 181 return "operational"; 182 case ADMHC_BUSS_SUSPEND: 183 return "suspend"; 184 } 185 return "?state"; 186} 187 188static void 189admhc_dump_status(struct admhcd *ahcd, char **next, unsigned *size) 190{ 191 struct admhcd_regs __iomem *regs = ahcd->regs; 192 u32 temp; 193 194 temp = admhc_readl(ahcd, ®s->gencontrol); 195 admhc_dbg_sw(ahcd, next, size, 196 "gencontrol 0x%08x%s%s%s%s\n", 197 temp, 198 (temp & ADMHC_CTRL_UHFE) ? " UHFE" : "", 199 (temp & ADMHC_CTRL_SIR) ? " SIR" : "", 200 (temp & ADMHC_CTRL_DMAA) ? " DMAA" : "", 201 (temp & ADMHC_CTRL_SR) ? " SR" : "" 202 ); 203 204 temp = admhc_readl(ahcd, ®s->host_control); 205 admhc_dbg_sw(ahcd, next, size, 206 "host_control 0x%08x BUSS=%s%s\n", 207 temp, 208 buss2string(temp & ADMHC_HC_BUSS), 209 (temp & ADMHC_HC_DMAE) ? " DMAE" : "" 210 ); 211 212 admhc_dump_intr_mask(ahcd, "int_status", 213 admhc_readl(ahcd, ®s->int_status), 214 next, size); 215 admhc_dump_intr_mask(ahcd, "int_enable", 216 admhc_readl(ahcd, ®s->int_enable), 217 next, size); 218 219 maybe_print_eds(ahcd, "hosthead", 220 admhc_readl(ahcd, ®s->hosthead), next, size); 221} 222 223#define dbg_port_sw(hc, num, value, next, size) \ 224 admhc_dbg_sw(hc, next, size, \ 225 "portstatus [%d] " \ 226 "0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \ 227 num, temp, \ 228 (temp & ADMHC_PS_PRSC) ? " PRSC" : "", \ 229 (temp & ADMHC_PS_OCIC) ? " OCIC" : "", \ 230 (temp & ADMHC_PS_PSSC) ? " PSSC" : "", \ 231 (temp & ADMHC_PS_PESC) ? " PESC" : "", \ 232 (temp & ADMHC_PS_CSC) ? " CSC" : "", \ 233 \ 234 (temp & ADMHC_PS_LSDA) ? " LSDA" : "", \ 235 (temp & ADMHC_PS_PPS) ? " PPS" : "", \ 236 (temp & ADMHC_PS_PRS) ? " PRS" : "", \ 237 (temp & ADMHC_PS_POCI) ? " POCI" : "", \ 238 (temp & ADMHC_PS_PSS) ? " PSS" : "", \ 239 \ 240 (temp & ADMHC_PS_PES) ? " PES" : "", \ 241 (temp & ADMHC_PS_CCS) ? " CCS" : "" \ 242 ); 243 244 245static void 246admhc_dump_roothub( 247 struct admhcd *ahcd, 248 int verbose, 249 char **next, 250 unsigned *size) 251{ 252 u32 temp, i; 253 254 temp = admhc_read_rhdesc(ahcd); 255 if (temp == ~(u32)0) 256 return; 257 258 if (verbose) { 259 admhc_dbg_sw(ahcd, next, size, 260 "rhdesc %08x%s%s%s%s%s%s PPCM=%02x%s%s%s%s NUMP=%d(%d)\n", 261 temp, 262 (temp & ADMHC_RH_CRWE) ? " CRWE" : "", 263 (temp & ADMHC_RH_OCIC) ? " OCIC" : "", 264 (temp & ADMHC_RH_LPSC) ? " LPSC" : "", 265 (temp & ADMHC_RH_LPSC) ? " DRWE" : "", 266 (temp & ADMHC_RH_LPSC) ? " OCI" : "", 267 (temp & ADMHC_RH_LPSC) ? " LPS" : "", 268 ((temp & ADMHC_RH_PPCM) >> 16), 269 (temp & ADMHC_RH_NOCP) ? " NOCP" : "", 270 (temp & ADMHC_RH_OCPM) ? " OCPM" : "", 271 (temp & ADMHC_RH_NPS) ? " NPS" : "", 272 (temp & ADMHC_RH_PSM) ? " PSM" : "", 273 (temp & ADMHC_RH_NUMP), ahcd->num_ports 274 ); 275 } 276 277 for (i = 0; i < ahcd->num_ports; i++) { 278 temp = admhc_read_portstatus(ahcd, i); 279 dbg_port_sw(ahcd, i, temp, next, size); 280 } 281} 282 283static void admhc_dump(struct admhcd *ahcd, int verbose) 284{ 285 admhc_dbg(ahcd, "ADMHC ahcd state\n"); 286 287 /* dumps some of the state we know about */ 288 admhc_dump_status(ahcd, NULL, NULL); 289 admhc_dbg(ahcd, "current frame #%04x\n", 290 admhc_frame_no(ahcd)); 291 292 admhc_dump_roothub(ahcd, verbose, NULL, NULL); 293} 294 295static const char data0[] = "DATA0"; 296static const char data1[] = "DATA1"; 297 298static void admhc_dump_td(const struct admhcd *ahcd, const char *label, 299 const struct td *td) 300{ 301 u32 tmp; 302 303 admhc_dbg(ahcd, "%s td %p; urb %p index %d; hwNextTD %08x\n", 304 label, td, 305 td->urb, td->index, 306 hc32_to_cpup(ahcd, &td->hwNextTD)); 307 308 tmp = hc32_to_cpup(ahcd, &td->hwINFO); 309 admhc_dbg(ahcd, " status %08x%s CC=%x EC=%d %s %s ISI=%x FN=%x\n", 310 tmp, 311 (tmp & TD_OWN) ? " OWN" : "", 312 TD_CC_GET(tmp), 313 TD_EC_GET(tmp), 314 td_togglestring(tmp), 315 td_pidstring(tmp), 316 TD_ISI_GET(tmp), 317 TD_FN_GET(tmp)); 318 319 tmp = hc32_to_cpup(ahcd, &td->hwCBL); 320 admhc_dbg(ahcd, " dbp %08x; cbl %08x; LEN=%d%s\n", 321 hc32_to_cpup(ahcd, &td->hwDBP), 322 tmp, 323 TD_BL_GET(tmp), 324 (tmp & TD_IE) ? " IE" : ""); 325} 326 327/* caller MUST own hcd spinlock if verbose is set! */ 328static void __attribute__((unused)) 329admhc_dump_ed(const struct admhcd *ahcd, const char *label, 330 const struct ed *ed, int verbose) 331{ 332 u32 tmp = hc32_to_cpu(ahcd, ed->hwINFO); 333 334 admhc_dbg(ahcd, "%s ed %p %s type %s; next ed %08x\n", 335 label, 336 ed, ed_statestring(ed->state), ed_typestring(ed->type), 337 hc32_to_cpup(ahcd, &ed->hwNextED)); 338 339 admhc_dbg(ahcd, " info %08x MAX=%d%s%s%s%s EP=%d DEV=%d\n", tmp, 340 ED_MPS_GET(tmp), 341 (tmp & ED_ISO) ? " ISO" : "", 342 (tmp & ED_SKIP) ? " SKIP" : "", 343 (tmp & ED_SPEED_FULL) ? " FULL" : " LOW", 344 (tmp & ED_INT) ? " INT" : "", 345 ED_EN_GET(tmp), 346 ED_FA_GET(tmp)); 347 348 tmp = hc32_to_cpup(ahcd, &ed->hwHeadP); 349 admhc_dbg(ahcd, " tds: head %08x tail %08x %s%s%s\n", 350 tmp & TD_MASK, 351 hc32_to_cpup(ahcd, &ed->hwTailP), 352 (tmp & ED_C) ? data1 : data0, 353 (tmp & ED_H) ? " HALT" : "", 354 verbose ? " td list follows" : " (not listing)"); 355 356 if (verbose) { 357 struct list_head *tmp; 358 359 /* use ed->td_list because HC concurrently modifies 360 * hwNextTD as it accumulates ed_donelist. 361 */ 362 list_for_each(tmp, &ed->td_list) { 363 struct td *td; 364 td = list_entry(tmp, struct td, td_list); 365 admhc_dump_td(ahcd, " ->", td); 366 } 367 } 368} 369 370#else /* ifdef DEBUG */ 371 372static inline void urb_print(struct admhcd *ahcd, struct urb * urb, char * str, 373 int small, int status) {} 374static inline void admhc_dump_ed(const struct admhcd *ahcd, const char *label, 375 const struct ed *ed, int verbose) {} 376static inline void admhc_dump_td(const struct admhcd *ahcd, const char *label, 377 const struct td *td) {} 378static inline void admhc_dump(struct admhcd *ahcd, int verbose) {} 379 380#undef ADMHC_VERBOSE_DEBUG 381 382#endif /* DEBUG */ 383 384/*-------------------------------------------------------------------------*/ 385 386#ifdef STUB_DEBUG_FILES 387 388static inline void create_debug_files(struct admhcd *bus) { } 389static inline void remove_debug_files(struct admhcd *bus) { } 390 391#else 392 393static int debug_async_open(struct inode *, struct file *); 394static int debug_periodic_open(struct inode *, struct file *); 395static int debug_registers_open(struct inode *, struct file *); 396static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); 397static int debug_close(struct inode *, struct file *); 398 399static const struct file_operations debug_async_fops = { 400 .owner = THIS_MODULE, 401 .open = debug_async_open, 402 .read = debug_output, 403 .release = debug_close, 404 .llseek = default_llseek, 405}; 406static const struct file_operations debug_periodic_fops = { 407 .owner = THIS_MODULE, 408 .open = debug_periodic_open, 409 .read = debug_output, 410 .release = debug_close, 411 .llseek = default_llseek, 412}; 413static const struct file_operations debug_registers_fops = { 414 .owner = THIS_MODULE, 415 .open = debug_registers_open, 416 .read = debug_output, 417 .release = debug_close, 418 .llseek = default_llseek, 419}; 420 421static struct dentry *admhc_debug_root; 422 423struct debug_buffer { 424 ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ 425 struct admhcd *ahcd; 426 struct mutex mutex; /* protect filling of buffer */ 427 size_t count; /* number of characters filled into buffer */ 428 char *page; 429}; 430 431static ssize_t 432show_list(struct admhcd *ahcd, char *buf, size_t count, struct ed *ed) 433{ 434 unsigned temp; 435 unsigned size = count; 436 437 if (!ed) 438 return 0; 439 440 /* dump a snapshot of the bulk or control schedule */ 441 while (ed) { 442 u32 info = hc32_to_cpu(ahcd, ed->hwINFO); 443 u32 headp = hc32_to_cpu(ahcd, ed->hwHeadP); 444 u32 tailp = hc32_to_cpu(ahcd, ed->hwTailP); 445 struct list_head *entry; 446 struct td *td; 447 448 temp = scnprintf(buf, size, 449 "ed/%p %s %s %cs dev%d ep%d %s%smax %d %08x%s%s %s" 450 " h:%08x t:%08x", 451 ed, 452 ed_statestring(ed->state), 453 ed_typestring(ed->type), 454 (info & ED_SPEED_FULL) ? 'f' : 'l', 455 info & ED_FA_MASK, 456 (info >> ED_EN_SHIFT) & ED_EN_MASK, 457 (info & ED_INT) ? "INT " : "", 458 (info & ED_ISO) ? "ISO " : "", 459 (info >> ED_MPS_SHIFT) & ED_MPS_MASK , 460 info, 461 (info & ED_SKIP) ? " S" : "", 462 (headp & ED_H) ? " H" : "", 463 (headp & ED_C) ? data1 : data0, 464 headp & ED_MASK, tailp); 465 size -= temp; 466 buf += temp; 467 468 list_for_each(entry, &ed->td_list) { 469 u32 dbp, cbl; 470 471 td = list_entry(entry, struct td, td_list); 472 info = hc32_to_cpup(ahcd, &td->hwINFO); 473 dbp = hc32_to_cpup(ahcd, &td->hwDBP); 474 cbl = hc32_to_cpup(ahcd, &td->hwCBL); 475 476 temp = scnprintf(buf, size, 477 "\n\ttd/%p %s %d %s%scc=%x urb %p (%08x,%08x)", 478 td, 479 td_pidstring(info), 480 TD_BL_GET(cbl), 481 (info & TD_OWN) ? "" : "DONE ", 482 (cbl & TD_IE) ? "IE " : "", 483 TD_CC_GET(info), td->urb, info, cbl); 484 size -= temp; 485 buf += temp; 486 } 487 488 temp = scnprintf(buf, size, "\n"); 489 size -= temp; 490 buf += temp; 491 492 ed = ed->ed_next; 493 } 494 495 return count - size; 496} 497 498static ssize_t fill_async_buffer(struct debug_buffer *buf) 499{ 500 struct admhcd *ahcd; 501 size_t temp; 502 unsigned long flags; 503 504 ahcd = buf->ahcd; 505 506 spin_lock_irqsave(&ahcd->lock, flags); 507 temp = show_list(ahcd, buf->page, PAGE_SIZE, ahcd->ed_head); 508 spin_unlock_irqrestore(&ahcd->lock, flags); 509 510 return temp; 511} 512 513 514#define DBG_SCHED_LIMIT 64 515 516static ssize_t fill_periodic_buffer(struct debug_buffer *buf) 517{ 518 struct admhcd *ahcd; 519 struct ed **seen, *ed; 520 unsigned long flags; 521 unsigned temp, size, seen_count; 522 char *next; 523 unsigned i; 524 525 seen = kmalloc(DBG_SCHED_LIMIT * sizeof(*seen), GFP_ATOMIC); 526 if (!seen) 527 return 0; 528 seen_count = 0; 529 530 ahcd = buf->ahcd; 531 next = buf->page; 532 size = PAGE_SIZE; 533 534 temp = scnprintf(next, size, "size = %d\n", NUM_INTS); 535 size -= temp; 536 next += temp; 537 538 /* dump a snapshot of the periodic schedule (and load) */ 539 spin_lock_irqsave(&ahcd->lock, flags); 540 for (i = 0; i < NUM_INTS; i++) { 541 ed = ahcd->periodic[i]; 542 if (!ed) 543 continue; 544 545 temp = scnprintf(next, size, "%2d [%3d]:", i, ahcd->load[i]); 546 size -= temp; 547 next += temp; 548 549 do { 550 temp = scnprintf(next, size, " ed%d/%p", 551 ed->interval, ed); 552 size -= temp; 553 next += temp; 554 for (temp = 0; temp < seen_count; temp++) { 555 if (seen[temp] == ed) 556 break; 557 } 558 559 /* show more info the first time around */ 560 if (temp == seen_count) { 561 u32 info = hc32_to_cpu(ahcd, ed->hwINFO); 562 struct list_head *entry; 563 unsigned qlen = 0; 564 565 /* qlen measured here in TDs, not urbs */ 566 list_for_each(entry, &ed->td_list) 567 qlen++; 568 temp = scnprintf(next, size, 569 " (%cs dev%d ep%d%s qlen %u" 570 " max %d %08x%s%s)", 571 (info & ED_SPEED_FULL) ? 'f' : 'l', 572 ED_FA_GET(info), 573 ED_EN_GET(info), 574 (info & ED_ISO) ? "iso" : "int", 575 qlen, 576 ED_MPS_GET(info), 577 info, 578 (info & ED_SKIP) ? " K" : "", 579 (ed->hwHeadP & 580 cpu_to_hc32(ahcd, ED_H)) ? 581 " H" : ""); 582 size -= temp; 583 next += temp; 584 585 if (seen_count < DBG_SCHED_LIMIT) 586 seen[seen_count++] = ed; 587 588 ed = ed->ed_next; 589 590 } else { 591 /* we've seen it and what's after */ 592 temp = 0; 593 ed = NULL; 594 } 595 596 } while (ed); 597 598 temp = scnprintf(next, size, "\n"); 599 size -= temp; 600 next += temp; 601 } 602 spin_unlock_irqrestore(&ahcd->lock, flags); 603 kfree(seen); 604 605 return PAGE_SIZE - size; 606} 607 608 609#undef DBG_SCHED_LIMIT 610 611static ssize_t fill_registers_buffer(struct debug_buffer *buf) 612{ 613 struct usb_hcd *hcd; 614 struct admhcd *ahcd; 615 struct admhcd_regs __iomem *regs; 616 unsigned long flags; 617 unsigned temp, size; 618 char *next; 619 u32 rdata; 620 621 ahcd = buf->ahcd; 622 hcd = admhc_to_hcd(ahcd); 623 regs = ahcd->regs; 624 next = buf->page; 625 size = PAGE_SIZE; 626 627 spin_lock_irqsave(&ahcd->lock, flags); 628 629 /* dump driver info, then registers in spec order */ 630 631 admhc_dbg_sw(ahcd, &next, &size, 632 "bus %s, device %s\n" 633 "%s\n" 634 "%s\n", 635 hcd->self.controller->bus->name, 636 dev_name(hcd->self.controller), 637 hcd->product_desc, 638 hcd_name); 639 640 if (!HCD_HW_ACCESSIBLE(hcd)) { 641 size -= scnprintf(next, size, 642 "SUSPENDED (no register access)\n"); 643 goto done; 644 } 645 646 admhc_dump_status(ahcd, &next, &size); 647 648 /* other registers mostly affect frame timings */ 649 rdata = admhc_readl(ahcd, ®s->fminterval); 650 temp = scnprintf(next, size, 651 "fmintvl 0x%08x %sFSLDP=0x%04x FI=0x%04x\n", 652 rdata, (rdata & ADMHC_SFI_FIT) ? "FIT " : "", 653 (rdata >> ADMHC_SFI_FSLDP_SHIFT) & ADMHC_SFI_FSLDP_MASK, 654 rdata & ADMHC_SFI_FI_MASK); 655 size -= temp; 656 next += temp; 657 658 rdata = admhc_readl(ahcd, ®s->fmnumber); 659 temp = scnprintf(next, size, "fmnumber 0x%08x %sFR=0x%04x FN=%04x\n", 660 rdata, (rdata & ADMHC_SFN_FRT) ? "FRT " : "", 661 (rdata >> ADMHC_SFN_FR_SHIFT) & ADMHC_SFN_FR_MASK, 662 rdata & ADMHC_SFN_FN_MASK); 663 size -= temp; 664 next += temp; 665 666 /* TODO: use predefined bitmask */ 667 rdata = admhc_readl(ahcd, ®s->lsthresh); 668 temp = scnprintf(next, size, "lsthresh 0x%04x\n", 669 rdata & 0x3fff); 670 size -= temp; 671 next += temp; 672 673 temp = scnprintf(next, size, "hub poll timer: %s\n", 674 admhcd_to_hcd(ahcd)->poll_rh ? "ON" : "OFF"); 675 size -= temp; 676 next += temp; 677 678 /* roothub */ 679 admhc_dump_roothub(ahcd, 1, &next, &size); 680 681done: 682 spin_unlock_irqrestore(&ahcd->lock, flags); 683 return PAGE_SIZE - size; 684} 685 686 687static struct debug_buffer *alloc_buffer(struct admhcd *ahcd, 688 ssize_t (*fill_func)(struct debug_buffer *)) 689{ 690 struct debug_buffer *buf; 691 692 buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); 693 694 if (buf) { 695 buf->ahcd = ahcd; 696 buf->fill_func = fill_func; 697 mutex_init(&buf->mutex); 698 } 699 700 return buf; 701} 702 703static int fill_buffer(struct debug_buffer *buf) 704{ 705 int ret = 0; 706 707 if (!buf->page) 708 buf->page = (char *)get_zeroed_page(GFP_KERNEL); 709 710 if (!buf->page) { 711 ret = -ENOMEM; 712 goto out; 713 } 714 715 ret = buf->fill_func(buf); 716 717 if (ret >= 0) { 718 buf->count = ret; 719 ret = 0; 720 } 721 722out: 723 return ret; 724} 725 726static ssize_t debug_output(struct file *file, char __user *user_buf, 727 size_t len, loff_t *offset) 728{ 729 struct debug_buffer *buf = file->private_data; 730 int ret = 0; 731 732 mutex_lock(&buf->mutex); 733 if (buf->count == 0) { 734 ret = fill_buffer(buf); 735 if (ret != 0) { 736 mutex_unlock(&buf->mutex); 737 goto out; 738 } 739 } 740 mutex_unlock(&buf->mutex); 741 742 ret = simple_read_from_buffer(user_buf, len, offset, 743 buf->page, buf->count); 744 745out: 746 return ret; 747} 748 749static int debug_close(struct inode *inode, struct file *file) 750{ 751 struct debug_buffer *buf = file->private_data; 752 753 if (buf) { 754 if (buf->page) 755 free_page((unsigned long)buf->page); 756 kfree(buf); 757 } 758 759 return 0; 760} 761 762static int debug_async_open(struct inode *inode, struct file *file) 763{ 764 file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); 765 766 return file->private_data ? 0 : -ENOMEM; 767} 768 769static int debug_periodic_open(struct inode *inode, struct file *file) 770{ 771 file->private_data = alloc_buffer(inode->i_private, 772 fill_periodic_buffer); 773 774 return file->private_data ? 0 : -ENOMEM; 775} 776 777static int debug_registers_open(struct inode *inode, struct file *file) 778{ 779 file->private_data = alloc_buffer(inode->i_private, 780 fill_registers_buffer); 781 782 return file->private_data ? 0 : -ENOMEM; 783} 784 785static inline void create_debug_files(struct admhcd *ahcd) 786{ 787 struct usb_bus *bus = &admhcd_to_hcd(ahcd)->self; 788 789 ahcd->debug_dir = debugfs_create_dir(bus->bus_name, admhc_debug_root); 790 if (!ahcd->debug_dir) 791 goto dir_error; 792 793 ahcd->debug_async = debugfs_create_file("async", S_IRUGO, 794 ahcd->debug_dir, ahcd, 795 &debug_async_fops); 796 if (!ahcd->debug_async) 797 goto async_error; 798 799 ahcd->debug_periodic = debugfs_create_file("periodic", S_IRUGO, 800 ahcd->debug_dir, ahcd, 801 &debug_periodic_fops); 802 if (!ahcd->debug_periodic) 803 goto periodic_error; 804 805 ahcd->debug_registers = debugfs_create_file("registers", S_IRUGO, 806 ahcd->debug_dir, ahcd, 807 &debug_registers_fops); 808 if (!ahcd->debug_registers) 809 goto registers_error; 810 811 admhc_dbg(ahcd, "created debug files\n"); 812 return; 813 814registers_error: 815 debugfs_remove(ahcd->debug_periodic); 816periodic_error: 817 debugfs_remove(ahcd->debug_async); 818async_error: 819 debugfs_remove(ahcd->debug_dir); 820dir_error: 821 ahcd->debug_periodic = NULL; 822 ahcd->debug_async = NULL; 823 ahcd->debug_dir = NULL; 824} 825 826static inline void remove_debug_files(struct admhcd *ahcd) 827{ 828 debugfs_remove(ahcd->debug_registers); 829 debugfs_remove(ahcd->debug_periodic); 830 debugfs_remove(ahcd->debug_async); 831 debugfs_remove(ahcd->debug_dir); 832} 833 834#endif 835 836/*-------------------------------------------------------------------------*/ 837