/*++ Copyright © 2000 Microsoft Corporation Module Name: usbglitch.c Abstract: This module implements the USB glitch detector consumer. It's intended for use with the Intel USB audio glitch detection hardware connected via a serial port. COM port should be specified using the USB_AUDIO_GLITCH_DETECTOR_PORT environment variable. The format is: COMn, where n = 1, 2, 3, ..., 9. See the big comment at the end. Author: Arthur Zwiegincew (arthurz) 14-Dec-00 Revision History: 14-Dec-00 - Created --*/ #pragma warning (disable:4201) #include #include #include #include #include #include #include #include #include "perflog.h" #include "usbglitch.h" // // Logging state. // USBAUDIOSTATE CurrentState; char* StateNames[5] = { "Disabled", "Enabled", "Stream", "Glitch", "Zero" }; int NumberOfFramesInCurrentState; BOOL StreamEncountered; // // Performance logging parameters. // // {28CF047A-2437-4b24-B653-B9446A419A69} DEFINE_GUID(GUID_DSHOW_CTL, 0x28cf047a, 0x2437, 0x4b24, 0xb6, 0x53, 0xb9, 0x44, 0x6a, 0x41, 0x9a, 0x69); struct { PERFLOG_LOGGING_PARAMS Params; TRACE_GUID_REGISTRATION TraceGuids[1]; } g_perflogParams; inline ULONGLONG _RDTSC( void ) { #ifdef _X86_ LARGE_INTEGER li; __asm { _emit 0x0F _emit 0x31 mov li.LowPart,eax mov li.HighPart,edx } return li.QuadPart; #if 0 // This isn't tested yet #elif defined (_IA64_) #define INL_REGID_APITC 3116 return __getReg( INL_REGID_APITC ); #endif // 0 #else // unsupported platform // not implemented on non x86/IA64 platforms return 0; #endif // _X86_/_IA64_ } #undef LogEvent void LogEvent (USBAUDIOSTATE State, int Frames) { _PERFINFO_WMI_USBAUDIOSTATE perfData; memset( &perfData, 0, sizeof (perfData)); perfData.header.Size = sizeof (perfData); perfData.header.Flags = WNODE_FLAG_TRACED_GUID; perfData.header.Guid = GUID_DSOUNDGLITCH; perfData.data.cycleCounter = _RDTSC(); perfData.data.usbAudioState = State; perfData.data.numberOfFrames = Frames; PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); printf ("%s: %d frames\n", StateNames[State], Frames); } void ProcessEvent (BYTE Data) { if (Data != CurrentState) { if (CurrentState == ZERO) { if (StreamEncountered && (Data == STREAM || Data == GLITCH)) { LogEvent (CurrentState, NumberOfFramesInCurrentState); } } else if (CurrentState == GLITCH) { if (Data == ZERO || Data == STREAM) { LogEvent (CurrentState, NumberOfFramesInCurrentState); } } else { LogEvent (CurrentState, NumberOfFramesInCurrentState); } CurrentState = (USBAUDIOSTATE)Data; NumberOfFramesInCurrentState = 1; if (CurrentState == STREAM) { StreamEncountered = TRUE; } } else { NumberOfFramesInCurrentState += 1; } } BOOL WINAPI ConsoleCtrlHandler (DWORD CtrlType) { PerflogShutdown(); return FALSE; } void __cdecl main (void) { char PortName[5] = {0}; DWORD PortNameLength; HANDLE Port; COMMCONFIG CommConfig; BOOL bstatus; DWORD EventMask; BYTE Data; DWORD BytesRead; // // Get the port name. // PortNameLength = GetEnvironmentVariable ("USB_AUDIO_GLITCH_DETECTOR_PORT", PortName, 5); if (PortNameLength != 4 || _strnicmp (PortName, "COM", 3) != 0 || PortName[3] < '1' || PortName[3] > '9') { printf ("Please set USB_AUDIO_GLITCH_DETECTOR_PORT environment " "variable to COM[1..9]."); return; } // // Open the port. // Port = CreateFile (PortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (Port == INVALID_HANDLE_VALUE) { printf ("Failed to open %s. GetLastError()=>%d\n", PortName, GetLastError()); return; } printf ("%s opened.\n", PortName); Sleep (10000); // // Set port configuration. // CommConfig.dwSize = sizeof (CommConfig); CommConfig.wVersion = 1; CommConfig.dcb.DCBlength = sizeof (DCB); CommConfig.dcb.BaudRate = CBR_115200; CommConfig.dcb.fBinary = 1; CommConfig.dcb.fParity = 0; CommConfig.dcb.fOutxCtsFlow = 0; CommConfig.dcb.fOutxDsrFlow = 0; CommConfig.dcb.fDtrControl = DTR_CONTROL_ENABLE; CommConfig.dcb.fDsrSensitivity = 0; CommConfig.dcb.fTXContinueOnXoff = 0; CommConfig.dcb.fOutX = 0; CommConfig.dcb.fInX = 0; CommConfig.dcb.fErrorChar = 1; CommConfig.dcb.fNull = 0; CommConfig.dcb.fRtsControl = RTS_CONTROL_ENABLE; CommConfig.dcb.fAbortOnError = 0; CommConfig.dcb.wReserved = 0; CommConfig.dcb.XonLim = 128; CommConfig.dcb.XoffLim = 512; CommConfig.dcb.ByteSize = 8; CommConfig.dcb.Parity = NOPARITY; CommConfig.dcb.StopBits = ONESTOPBIT; CommConfig.dcb.XonChar = 0; CommConfig.dcb.XoffChar = 0; CommConfig.dcb.ErrorChar = 0; CommConfig.dcb.EofChar = 127; CommConfig.dcb.EvtChar = 126; CommConfig.dcb.wReserved1 = 0; CommConfig.dwProviderSubType = PST_RS232; CommConfig.dwProviderOffset = 0; CommConfig.dwProviderSize = 0; bstatus = SetCommConfig (Port, &CommConfig, sizeof (CommConfig)); if (!bstatus) { printf ("Failed to configure %s. GetLastError()=>%d\n", PortName, GetLastError()); return; } bstatus = SetupComm (Port, 8, 8); if (!bstatus) { printf ("Failed to configure %s. GetLastError()=>%d\n", PortName, GetLastError()); return; } // // Initialize perf logging. // g_perflogParams.Params.ControlGuid = GUID_DSHOW_CTL; g_perflogParams.Params.OnStateChanged = NULL; g_perflogParams.Params.NumberOfTraceGuids = 1; g_perflogParams.Params.TraceGuids[0].Guid = &GUID_USBAUDIOSTATE; PerflogInitialize (&g_perflogParams.Params); SetConsoleCtrlHandler (ConsoleCtrlHandler, TRUE); // // Receive data. // CurrentState = DISABLED; NumberOfFramesInCurrentState = 0; StreamEncountered = FALSE; bstatus = SetCommMask (Port, EV_RXCHAR); if (!bstatus) { printf ("Internal error (1). GetLastError()=>%d\n", GetLastError()); return; } for (;;) { EventMask = 0; WaitCommEvent (Port, &EventMask, NULL); if (EventMask & EV_RXCHAR) { bstatus = ReadFile (Port, &Data, 1, &BytesRead, NULL); if (!bstatus || BytesRead != 1) { ClearCommError (Port, &BytesRead, NULL); continue; } ProcessEvent (Data); } } } /* John.keys@intel.com has provided the following information: Intel Hardware USB Audio Glitch Detector Theory of Operation ----------------------------------------- Serial Port Configuration: 115,200 baud 8 bit words 1 stop bit no parity The Glitch detector's port (outside port, labeled UART) is configured as DTE, so only straight-through cables should be used. A pc-pc debug cable will not work. Because of special reset circuitry on the board, both DTR and RTS should be enabled. The detector is stuck in reset if all LEDs are lit. When plugged into the USB port and receiving SOFs (Start Of Frame packets, one received every millisecond), the HW glitch detector will output the following values once per millisecond: 0 - Device is disabled 1 - Device is enabled - SetInterface Request received 2 - Frame with Streaming data 3 - Frame with no streaming data 4 - Frame with Zero-stuffed data To determine which value to send, the device contains a small state machine. The states it moves through are DISABLED, ENABLED, STREAM, GLITCH, and ZERO. Note that these states duplicate the values returned on the serial port. In fact, the detector sends back it's state at every frame. DISABLED: device starts in this state, returns to this state when unplugged. This is a special case, and the device can return to this state from any other state. The device can only move to ENABLED from this state. ENABLED: Device goes to this state with every SetConfiguration or SetInterface request. Since SetInterface requests are made to open and close Audio pipes, this allows the device to detect the end of audio streams. The will stay in this state until it is disconnected or it receives Isochronous data. It can then go to either STREAM or ZERO, depending on the data payload received. This prevents false glitch detection while waiting for the stream to start. STREAM: Valid data was received. The device can move to this state from any other state, with the exception of DISABLED. It can move to any other state. GLITCH: No data received. The device can only move to this state from STREAM or ZERO. It can change from this state to any other state. ZERO: Data was received, but all samples had zero value. The device can move to this state from any other state except DISABLED. It can change to any other state from ZERO. ---------------------------------------------------------------------------- Application Processing Considerations ---------------------------------------------------------------------------- The difference between ZERO and GLITCH: Glitch indicates that no data was received by the board. This indicates low-level ISR, DPC, IRQL, or Critical Work Items. ZERO: indicates high-level thread latencies that result in KMIXER data starvation. Accurate glitch detection requires looking at the stream over time. The board can only report it's state at any given frame. Therefore, the application must perform some additional processing of state to eliminate false glitches. Specifically, it must look at the transitions between states. ZERO: Zero state should be ignored until the first STREAM state is encountered. This prevents false detection of rate-lock packets and of initial attenuated streams. After the first STREAM state is encountered, ZERO should only be reported when a ZERO->STREAM or a ZERO->GLITCH transition is seen. ZERO->ENABLED transitions should not be reported as they can occur normally when shutting down the stream. Also, ZERO->GLITCH->ENABLED transitions must be ignored for the same reason. GLITCH: Only GLITCH->ZERO and GLITCH->STREAM transitions should be reported as glitches. GLITCH->ENA transitions always occur at the end of a stream and are normal. When enabled, the device will send one state byte every 1 millisecond. This means that the stream can also be used as a clock source for time-stamping glitches. */