windows-nt/Source/XPSP1/NT/base/tools/kdexts2/usblog.c
2020-09-26 16:20:57 +08:00

1102 lines
24 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
USBLOG.c
Abstract:
WinDbg Extension Api
Author:
Chris Robinson (crobins) February 1999
Environment:
User Mode.
Revision History:
--*/
#include "precomp.h"
#ifndef MAKE_SYMBOL
#define MAKE_SYMBOL(m, s) #m "!" #s
#endif
#define DUMP_STRING(s) dprintf((s))
#define DUMP_DWORD(d) dprintf("0x%08x", (d))
#define DUMP_ADDRESS(a) dprintf("0x%08p", (a))
#define END_LINE() dprintf("\n")
#define TRACE_SPEW
#define DECLARE_LOG(logname, name, start, end, ptr, lines, desc) \
static struct _USB_LOG logname = { name, start, end, ptr, \
0, 0, 0, 0, \
0, 0, lines, desc };
#define DEFAULT_LINES_TO_DUMP 16
#define TAG_CHAR_LENGTH 4
#define TAG_STRING_LENGTH TAG_CHAR_LENGTH+1
#define LOG_SEARCH_DELIMS ","
#define LogHasRolledOver(log) (IsValidEntry((log) -> LogStart) && ((log) -> LogStart != (log) -> LogPtr))
#define CalcNumberLines(pe1, pe2) (((pe2) - (pe1)) + 1)
#define TAG(str) dprintf("%-6s", str)
#define PARAM(str) dprintf("%-12s", str)
#define DESC(str) dprintf("%-12s", str)
#define USBHUB_LOG_NAME USBHUBLog
#define USBD_LOG_NAME USBDLog
#define OHCI_LOG_NAME OHCILog
#define UHCD_LOG_NAME UHCDLog
#define USBHUB_LNAME "USBHUB"
#define USBHUB_START MAKE_SYMBOL(usbhub, hublstart)
#define USBHUB_END MAKE_SYMBOL(usbhub, hublend)
#define USBHUB_PTR MAKE_SYMBOL(usbhub, hublptr)
#define USBD_LNAME "USBD"
#define USBD_START MAKE_SYMBOL(usbd, lstart)
#define USBD_END MAKE_SYMBOL(usbd, lend)
#define USBD_PTR MAKE_SYMBOL(usbd, lptr)
#define OHCI_LNAME "OpenHCI"
#define OHCI_START MAKE_SYMBOL(openhci, ohcilstart)
#define OHCI_END MAKE_SYMBOL(openhci, ohcilend)
#define OHCI_PTR MAKE_SYMBOL(openhci, ohcilptr)
#define UHCD_LNAME "UHCD"
#define UHCD_START MAKE_SYMBOL(uhcd, HCDLStart)
#define UHCD_END MAKE_SYMBOL(uhcd, HCDLEnd)
#define UHCD_PTR MAKE_SYMBOL(uhcd, HCDLPtr)
//
// USBLOG typedefs
//
typedef union {
ULONG TagValue;
CHAR TagChars[TAG_CHAR_LENGTH];
} USBLOG_TAG_READ, *PUSBLOG_TAG_READ;
typedef struct _usb_log_entry {
USBLOG_TAG_READ Tag;
DWORD Param1;
DWORD Param2;
DWORD Param3;
} USB_LOG_ENTRY, *PUSB_LOG_ENTRY;
typedef PCHAR (DESC_ROUTINE) (
ULONG64 Entry
);
typedef DESC_ROUTINE *PDESC_ROUTINE;
typedef struct _USB_LOG {
PCHAR LogName;
PCHAR LogStartName;
PCHAR LogEndName;
PCHAR LogPtrName;
ULONG64 LogStart;
ULONG64 LogEnd;
ULONG64 LogPtr;
ULONG64 LastSearchResult;
ULONG64 LogCurrViewTop;
ULONG64 LogCurrViewBottom;
LONG LinesToDump;
PDESC_ROUTINE DescRoutine;
} USB_LOG, *PUSB_LOG;
typedef struct _USBLOG_ARGS {
ULONG64 Address;
LONG NumberLines;
PCHAR SearchString;
BOOLEAN ResetLog;
BOOLEAN SearchLog;
} USBLOG_ARGS, *PUSBLOG_ARGS;
//
// Add logging function declarations
//
VOID USBLOG_DoLog(PUSB_LOG LogToDump, PCSTR Args);
VOID USBLOG_Usage(void);
VOID USBLOG_GetParams(PCSTR Args, PUSBLOG_ARGS ParsedArgs);
ULONG64 USBLOG_SearchLog(PUSB_LOG Log, ULONG64 SearchBegin, PCHAR SearchString);
VOID DumpDefaultLogHeader(VOID);
BOOLEAN DumpLog(PUSB_LOG, BOOLEAN, BOOLEAN);
BOOLEAN ResetLog(PUSB_LOG);
#define GetMostRecentEntry(l) ((l) -> LogPtr)
ULONG64 GetEntryBefore(PUSB_LOG, ULONG64);
ULONG64 GetEntryAfter(PUSB_LOG, ULONG64);
#define IsMostRecentEntry(l, e) ((e) == (l) -> LogPtr)
#define IsLeastRecentEntry(l, e) (((LogHasRolledOver((l))) \
? ( (e) == ((l) -> LogPtr - 1)) \
: ( (e) == ((l) -> LogEnd))) )
VOID GetLogEntryTag(ULONG64, PUSBLOG_TAG_READ);
VOID GetLogEntryParams(ULONG64, ULONG64 *, ULONG64 *, ULONG64 *);
ULONG64 SearchLogForTag(PUSB_LOG, ULONG64, ULONG, USBLOG_TAG_READ[]);
#define GetLastSearchResult(l) ((l) -> LastSearchResult)
#define SetLastSearchResult(l, a) ((l) -> LastSearchResult = (a))
#define SetLinesToDump(l, n) ((l) -> LinesToDump = (n))
#define GetLinesToDump(l) ((l) -> LinesToDump)
VOID ConvertStringToTag(PCHAR, PUSBLOG_TAG_READ);
VOID ConvertTagToString(PUSBLOG_TAG_READ, PCHAR, ULONG);
BOOLEAN IsValidEntry(ULONG64);
VOID GetCurrentView(PUSB_LOG, ULONG64 *, ULONG64 *);
VOID SetCurrentView(PUSB_LOG, ULONG64, ULONG64);
VOID LogViewScrollUp(PUSB_LOG);
VOID LogViewScrollDown(PUSB_LOG);
VOID DisplayCurrentView(PUSB_LOG);
VOID DisplayHeader();
//
// Global log structure declarations
//
DECLARE_LOG(USBHUB_LOG_NAME, USBHUB_LNAME, USBHUB_START, USBHUB_END, USBHUB_PTR,
DEFAULT_LINES_TO_DUMP, NULL);
DECLARE_LOG(USBD_LOG_NAME, USBD_LNAME, USBD_START, USBD_END, USBD_PTR,
DEFAULT_LINES_TO_DUMP, NULL);
DECLARE_LOG(OHCI_LOG_NAME, OHCI_LNAME, OHCI_START, OHCI_END, OHCI_PTR,
DEFAULT_LINES_TO_DUMP, NULL);
DECLARE_LOG(UHCD_LOG_NAME, UHCD_LNAME, UHCD_START, UHCD_END, UHCD_PTR,
DEFAULT_LINES_TO_DUMP, NULL);
//
// Define each of these, which is relatively simple
//
DECLARE_API( usblog )
/*++
Routine Description:
Dumps a HID Preparsed Data blob
Arguments:
args - Address flags
Return Value:
None
--*/
{
ULONG index;
UCHAR logName[32];
UCHAR buffer[256];
logName[0] = '\0';
memset(buffer, '\0', sizeof(buffer));
if (!*args)
{
USBLOG_Usage();
}
else
{
if (!sscanf(args, "%s %256c", logName, buffer)) {
USBLOG_Usage();
}
}
index = 0;
while ('\0' != logName[index])
{
logName[index] = (UCHAR) toupper(logName[index]);
index++;
}
if (!strcmp(logName, "USBHUB"))
{
USBLOG_DoLog(&(USBHUB_LOG_NAME), buffer);
}
else if (!strcmp(logName, "USBD"))
{
USBLOG_DoLog(&(USBD_LOG_NAME), buffer);
}
else if (!strcmp(logName, "OPENHCI"))
{
USBLOG_DoLog(&(OHCI_LOG_NAME), buffer);
}
else if (!strcmp(logName, "UHCD"))
{
USBLOG_DoLog(&(UHCD_LOG_NAME), buffer);
}
else
{
dprintf("Unknown USB log type!\n");
USBLOG_Usage();
}
return S_OK;
}
VOID
USBLOG_DoLog(
PUSB_LOG LogToDump,
PCSTR Args
)
{
BOOLEAN atEnd;
BOOLEAN dumpSuccess;
USBLOG_ARGS logArgs;
BOOLEAN doDump;
BOOLEAN doScroll;
ULONG64 searchAddress;
TRACE_SPEW("Entering USBLOG_DoLog with args %s\n", Args);
doDump = TRUE;
doScroll = TRUE;
//
// Parse the arguments to the logging function
//
USBLOG_GetParams(Args, &logArgs);
//
// Analyze the params and modify the log structure if need be
//
if (0 != logArgs.NumberLines)
{
SetLinesToDump(LogToDump, logArgs.NumberLines);
}
if (0 != logArgs.Address)
{
if (!logArgs.SearchLog)
{
if (GetLinesToDump(LogToDump) > 0)
{
SetCurrentView(LogToDump, logArgs.Address, 0);
}
else
{
SetCurrentView(LogToDump, 0, logArgs.Address);
}
doScroll = FALSE;
}
}
if (logArgs.ResetLog)
{
ResetLog(LogToDump);
doScroll = FALSE;
}
if (logArgs.SearchLog)
{
if (0 == logArgs.Address)
{
searchAddress = GetLastSearchResult(LogToDump);
if (0 == searchAddress)
{
searchAddress = GetMostRecentEntry(LogToDump);
}
}
else
{
searchAddress = logArgs.Address;
}
searchAddress = USBLOG_SearchLog(LogToDump,
searchAddress,
logArgs.SearchString);
if (0 != searchAddress)
{
SetLastSearchResult(LogToDump, searchAddress);
SetCurrentView(LogToDump, searchAddress, 0);
doScroll = FALSE;
}
else
{
dprintf("Couldn't find any such tag(s)\n");
doDump = FALSE;
}
}
if (doDump)
{
dumpSuccess = DumpLog(LogToDump, FALSE, doScroll);
if (!dumpSuccess)
{
dprintf("Error dumping log\n");
}
}
return;
}
VOID
USBLOG_GetParams(
IN PCSTR Args,
OUT PUSBLOG_ARGS ParsedArgs
)
{
PCHAR arg;
PCHAR args;
CHAR argDelims[] = " \t\n";
//
// Initialize the arguments structure first
//
memset(ParsedArgs, 0x00, sizeof(USBLOG_ARGS));
//
// Setup the argument string so that it's not a const anymore which
// eliminates compiler errors.
//
args = (PCHAR) Args;
//
// The command line for !log is the following:
// !log [address] [-r] [-s searchstring] [-l n]
//
// The argument parsing will assume these can be entered in any order,
// so we'll simply examine each argument until there are no more
// arguments. The main loop will look for either an address or an
// option. If it finds either, it processes as necessary. Otherwise,
// the argument is simply ignored.
//
arg = strtok(args, argDelims);
while (NULL != arg) {
TRACE_SPEW("Analyzing usblog arg: %s\n", arg);
//
// Check to see if this is an option or not
//
if ('-' != *arg)
{
//
// No, then it must be an address, call GetExpression
//
ParsedArgs -> Address = GetExpression(arg);
//
// Assume user competence and store the result...
// Add the value to the the ParsedArgs structure.
// Note that if > 1 address is given, this function
// simply uses the last one specified
//
}
else
{
//
// OK, it's an option...Process appropriately
//
switch (*(arg+1))
{
//
// Reset Log
//
case 'r':
ParsedArgs -> ResetLog = TRUE;
break;
//
// Set lines to display
//
case 'l':
arg = strtok(NULL, argDelims);
if (NULL != arg)
{
//
// Assume user competence and get the decimal string
//
if (!sscanf(arg, "%d", &(ParsedArgs -> NumberLines))) {
ParsedArgs -> NumberLines = 0;
}
TRACE_SPEW("Parsed -l command with %d lines\n",
ParsedArgs -> NumberLines);
}
break;
//
// Search the log
//
case 's':
ParsedArgs -> SearchLog = TRUE;
ParsedArgs -> SearchString = strtok(NULL, argDelims);
break;
default:
dprintf("Unknown option %c\n", *(arg+1));
break;
}
}
arg = strtok(NULL, argDelims);
}
return;
}
ULONG64
USBLOG_SearchLog(
PUSB_LOG Log,
ULONG64 SearchBegin,
PCHAR SearchString
)
{
ULONG64 firstFoundEntry;
USBLOG_TAG_READ tagArray[32];
ULONG index;
PCHAR searchToken;
TRACE_SPEW("Entering USBLOG_SearchLog looking for %s\n", SearchString);
index = 0;
firstFoundEntry = 0;
searchToken = strtok(SearchString, LOG_SEARCH_DELIMS);
while (index < 32 && NULL != searchToken)
{
TRACE_SPEW("Adding %s to tag array\n", searchToken);
ConvertStringToTag(searchToken, &(tagArray[index++]));
searchToken = strtok(NULL, LOG_SEARCH_DELIMS);
}
if (index > 0)
{
firstFoundEntry = SearchLogForTag(Log, SearchBegin, index, tagArray);
}
return (firstFoundEntry);
}
//
// Local logging function definitions.
//
BOOLEAN
DumpLog(
IN PUSB_LOG Log,
IN BOOLEAN StartFromTop,
IN BOOLEAN Scroll
)
{
ULONG lineCount;
BOOLEAN resetStatus;
ULONG64 currViewTop;
ULONG64 currViewBottom;
//
// Check if the log has been opened/reset yet
//
GetCurrentView(Log, &currViewTop, &currViewBottom);
if (0 == currViewTop || StartFromTop)
{
//
// Reset the log and return FALSE if the reset failed
//
resetStatus = ResetLog(Log);
if (!resetStatus)
{
return (FALSE);
}
Scroll = FALSE;
}
//
// Call the log's dump routine based on the direction
//
if (Scroll)
{
TRACE_SPEW("Checking lines to dump: %d\n", Log -> LinesToDump);
if (Log -> LinesToDump < 0)
{
LogViewScrollUp(Log);
}
else
{
LogViewScrollDown(Log);
}
}
DisplayCurrentView(Log);
return (TRUE);
}
BOOLEAN
ResetLog(
IN PUSB_LOG Log
)
{
ULONG bytesRead;
ULONG64 symbolAddress;
ULONG readStatus;
//
// Get the address of the start symbol, the end symbol, and the
// current pointer symbol
//
symbolAddress = GetExpression(Log -> LogStartName);
if (0 != symbolAddress)
{
if (!ReadPointer(symbolAddress, &(Log -> LogStart)))
{
dprintf("Unable to read %p\n", symbolAddress);
Log -> LogStart = 0;
}
}
symbolAddress = GetExpression(Log -> LogEndName);
if (0 != symbolAddress)
{
if (!ReadPointer(symbolAddress, &(Log -> LogEnd)))
{
dprintf("Unable to read %p\n", symbolAddress);
Log -> LogEnd = 0;
}
}
symbolAddress = GetExpression(Log -> LogPtrName);
if (0 != symbolAddress)
{
if (!ReadPointer(symbolAddress, &(Log -> LogPtr)))
{
dprintf("Unable to read %p\n", symbolAddress);
Log -> LogPtr= 0;
}
}
if ( (0 == Log -> LogStart) ||
(0 == Log -> LogEnd) ||
(0 == Log -> LogPtr) )
{
dprintf("Unable to reset log\n");
return (FALSE);
}
SetCurrentView(Log, Log -> LogPtr, 0);
return (TRUE);
}
VOID
GetCurrentView(
IN PUSB_LOG Log,
OUT ULONG64 *CurrTop,
OUT ULONG64 *CurrBottom
)
{
*CurrTop = Log -> LogCurrViewTop;
*CurrBottom = Log -> LogCurrViewBottom;
return;
}
VOID
SetCurrentView(
IN PUSB_LOG Log,
IN ULONG64 NewTop,
IN ULONG64 NewBottom
)
{
LONG lineCount;
if (0 == NewTop && 0 == NewBottom)
{
return;
}
lineCount = abs(Log -> LinesToDump);
if (0 == NewBottom)
{
//
// Calculate the new bottom based on NewTop and the number of lines to
// be displayed in the log.
//
NewBottom = NewTop + lineCount;
if (NewTop >= Log -> LogPtr)
{
lineCount -= (ULONG) CalcNumberLines(NewTop, Log -> LogEnd);
if (lineCount > 0)
{
if (LogHasRolledOver(Log))
{
NewBottom = Log -> LogStart + lineCount - 1;
if (NewBottom >= Log -> LogPtr)
{
NewBottom = Log -> LogPtr - 1;
}
}
else
{
NewBottom = Log -> LogEnd;
}
}
}
else
{
if (lineCount > CalcNumberLines(NewTop, Log -> LogPtr - 1))
{
NewBottom = Log -> LogPtr - 1;
}
}
}
else if (0 == NewTop)
{
//
// NULL == NewTop -- Need to calculate the NewTop of the view
//
NewTop = NewBottom - lineCount;
if (NewBottom <= Log -> LogPtr - 1)
{
lineCount -= (ULONG) CalcNumberLines(Log -> LogStart, NewBottom);
if (lineCount > 0)
{
NewTop = Log -> LogEnd - lineCount + 1;
if (NewTop < Log -> LogPtr)
{
NewTop = Log -> LogPtr;
}
}
}
else
{
if (NewTop < Log -> LogPtr)
{
NewTop = Log -> LogPtr;
}
}
}
TRACE_SPEW("Set CurrentView NewTop (0x%08x) NewBottom(0x%08x)\n",
NewTop, NewBottom);
Log -> LogCurrViewTop = NewTop;
Log -> LogCurrViewBottom = NewBottom;
return;
}
ULONG64
GetEntryBefore(
IN PUSB_LOG Log,
IN ULONG64 Entry
)
{
if (Entry == Log -> LogEnd)
{
if (LogHasRolledOver(Log))
{
return (Log -> LogStart);
}
return (0);
}
//
// Check to see if we hit the most recent entry in the log. If so we've
// hit the end of the log and will loop again. return NULL.
//
return ( ((Entry + 1) == Log -> LogPtr) ? 0 : Entry+1);
}
ULONG64
GetEntryAfter(
IN PUSB_LOG Log,
IN ULONG64 Entry
)
{
if (Entry == Log -> LogPtr)
{
return (0);
}
if (Entry == Log -> LogStart)
{
return (Log -> LogEnd);
}
return (Entry-1);
}
VOID
GetLogEntryTag(
IN ULONG64 Entry,
OUT PUSBLOG_TAG_READ Tag
)
{
ULONG bytesRead;
InitTypeRead(Entry, uhcd!USB_LOG_ENTRY);
// ReadMemory( Entry + (ULONG64) &(((PUSB_LOG_ENTRY) 0) -> Tag), Tag, sizeof(*Tag), &bytesRead);
Tag->TagValue = (ULONG) ReadField(Tag.TagValue);
return;
}
VOID
GetLogEntryParams(
IN ULONG64 Entry,
OUT ULONG64 *Param1,
OUT ULONG64 *Param2,
OUT ULONG64 *Param3
)
{
ULONG bytesRead;
InitTypeRead(Entry, uhcd!USB_LOG_ENTRY);
*Param1 = ReadField(Param1);
*Param2 = ReadField(Param2);
*Param3 = ReadField(Param3);
// ReadMemory(Entry + (ULONG64) &(((PUSB_LOG_ENTRY) 0)-> Param1), Param1, sizeof(*Param1), &bytesRead);
// ReadMemory(Entry + (ULONG64) &(((PUSB_LOG_ENTRY) 0)-> Param3), Param2, sizeof(*Param2), &bytesRead);
// ReadMemory(Entry + (ULONG64) &(((PUSB_LOG_ENTRY) 0)-> Param2), Param3, sizeof(*Param3), &bytesRead);
return;
}
ULONG64
SearchLogForTag(
IN PUSB_LOG Log,
IN ULONG64 SearchBegin,
IN ULONG TagCount,
IN USBLOG_TAG_READ TagArray[]
)
{
ULONG tagIndex;
ULONG64 currEntry;
USBLOG_TAG_READ currTag;
//
// Start the search at the most recent log entry
//
currEntry = SearchBegin;
while (currEntry != 0) {
GetLogEntryTag(currEntry, &currTag);
for (tagIndex = 0; tagIndex < TagCount; tagIndex++) {
if (TagArray[tagIndex].TagValue == currTag.TagValue) {
return (currEntry);
}
}
currEntry = GetEntryBefore(Log, currEntry);
}
return (0);
}
VOID
ConvertStringToTag(
IN PCHAR TagString,
OUT PUSBLOG_TAG_READ Tag
)
{
USBLOG_TAG_READ tag;
ULONG shiftAmount;
//
// Since a Tag is four characters long, this routine will convert only
// the first four characters even though the string might be longer
//
tag.TagValue = 0;
shiftAmount = 0;
while (tag.TagValue < 0x01000000 && *TagString) {
tag.TagValue += (*TagString) << shiftAmount;
TagString++;
shiftAmount += 8;
}
*Tag = tag;
return;
}
VOID
ConvertTagToString(
IN PUSBLOG_TAG_READ Tag,
IN PCHAR String,
IN ULONG StringLength
)
{
ULONG tagIndex;
for (tagIndex = 0; tagIndex < 4 && tagIndex < StringLength-1; tagIndex++) {
*String = Tag -> TagChars[tagIndex];
*String++;
}
*String = '\0';
return;
}
BOOLEAN
IsValidEntry(
ULONG64 Entry
)
{
USBLOG_TAG_READ tag;
GetLogEntryTag(Entry, &tag);
return (0 != tag.TagValue);
}
VOID
LogViewScrollUp(
IN PUSB_LOG Log
)
{
ULONG64 newBottom;
TRACE_SPEW("In ScrollUp routine\n");
newBottom = GetEntryAfter(Log, Log -> LogCurrViewTop);
if (newBottom)
{
SetCurrentView(Log, 0, newBottom);
}
return;
}
VOID
LogViewScrollDown(
IN PUSB_LOG Log
)
{
ULONG64 newTop;
TRACE_SPEW("In ScrollDown routine\n");
newTop = GetEntryBefore(Log, Log -> LogCurrViewBottom);
if (newTop)
{
SetCurrentView(Log, newTop, 0);
}
return;
}
VOID
DisplayCurrentView(
IN PUSB_LOG Log
)
{
ULONG64 viewTop;
ULONG64 viewBottom;
ULONG64 currEntry;
USBLOG_TAG_READ currTag;
CHAR TagString[TAG_STRING_LENGTH];
ULONG lineCount;
ULONG64 param1;
ULONG64 param2;
ULONG64 param3;
PCHAR desc;
//
// Display the header
//
DisplayHeader();
//
// Determine which line of the log to begin displaying
//
GetCurrentView(Log, &viewTop, &viewBottom);
//
// Start displaying lines and stop when we hit the end of the log
// or we have displayed the requested number of lines
//
//
// Check first to see if the top of the view is the most recent entry
//
if (IsMostRecentEntry(Log, viewTop))
{
dprintf("Top of log...\n");
}
currEntry = viewTop;
while (1)
{
//
// Display a line of the log
//
GetLogEntryTag(currEntry, &currTag);
ConvertTagToString(&currTag,
TagString,
TAG_STRING_LENGTH);
GetLogEntryParams(currEntry, &param1, &param2, &param3);
DUMP_ADDRESS(currEntry);
DUMP_STRING(" ");
DUMP_STRING(TagString);
DUMP_STRING(" ");
DUMP_DWORD(param1);
DUMP_STRING(" ");
DUMP_DWORD(param2);
DUMP_STRING(" ");
DUMP_DWORD(param3);
DUMP_STRING(" ");
if (0 != Log -> DescRoutine)
{
desc = Log -> DescRoutine(currEntry);
if (0 != desc)
{
DUMP_STRING(desc);
}
}
END_LINE();
if (currEntry == viewBottom)
{
break;
}
currEntry = GetEntryBefore(Log, currEntry);
}
if (IsLeastRecentEntry(Log, currEntry))
{
dprintf("Bottom of log...\n");
}
return;
}
//
// Local Function Definitions
//
VOID
DisplayHeader(
VOID
)
{
TRACE_SPEW("Entering dump default log header\n");
END_LINE();
PARAM("Entry");
TAG("Tag");
PARAM("Param1");
PARAM("Param2");
PARAM("Param3");
DESC("Description");
END_LINE();
DUMP_STRING("------------------------------------------------------------------");
END_LINE();
return;
}
VOID
USBLOG_Usage(
VOID
)
{
dprintf("!usblog <log> [addr] [-r] [-s str] [-l n]\n"
" <log> - {USBHUB | USBD | UHCD | OpenHCI}\n"
" [addr] - address to begin dumping from in <log>\n"
" [-r] - reset the log to dump from most recent entry\n"
" [-s str] - search for first instance of a particular tag\n"
" from the current position; str should be a list\n"
" of tags delimited by comma's with no whitespace\n"
" [-l n] - set the number of lines to display at a time to n\n");
dprintf("\n");
return;
}