diff --git a/src/api/Drivers/USBMIDI2/Driver/Device.cpp b/src/api/Drivers/USBMIDI2/Driver/Device.cpp index 325344d8b..1b2b55320 100644 --- a/src/api/Drivers/USBMIDI2/Driver/Device.cpp +++ b/src/api/Drivers/USBMIDI2/Driver/Device.cpp @@ -157,6 +157,8 @@ Return Value: WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); pnpPowerCallbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware; pnpPowerCallbacks.EvtDeviceReleaseHardware = EvtDeviceReleaseHardware; + pnpPowerCallbacks.EvtDeviceD0Entry = EvtDeviceD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = EvtDeviceD0Exit; WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); // @@ -184,6 +186,18 @@ Return Value: devCtx->Midi = nullptr; devCtx->ExcludeD3Cold = WdfFalse; + devCtx->DeviceInGTBIDs = NULL; + devCtx->DeviceGTBMemory = NULL; + devCtx->DeviceOutGTBIDs = NULL; + devCtx->DeviceWriteMemory = NULL; + devCtx->DeviceWriteBuffer = NULL; + devCtx->DeviceWriteBufferIndex = NULL; + + for (int count = 0; count < MAX_NUM_GROUPS_CABLES; count++) + { + devCtx->midi1IsInSysex[count] = false; + devCtx->midi1OutSysex[count].inSysex = false; + } // // Allow ACX to add any post-requirement it needs on this device. @@ -414,7 +428,8 @@ SetPowerPolicy( // WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCannotWakeFromS0); - idleSettings.IdleTimeout = IDLE_POWER_TIMEOUT; + //idleSettings.IdleTimeout = IDLE_POWER_TIMEOUT; + idleSettings.IdleTimeout = 15*60*1000; idleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint; idleSettings.ExcludeD3Cold = devCtx->ExcludeD3Cold; @@ -431,6 +446,85 @@ SetPowerPolicy( return status; } +_Use_decl_annotations_ +PAGED_CODE_SEG +NTSTATUS +EvtDeviceD0Entry( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState +) +{ + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(PreviousState); + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit"); + + return STATUS_SUCCESS; +} + +_Use_decl_annotations_ +PAGED_CODE_SEG +NTSTATUS +EvtDeviceD0Exit( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE TargetState +) +{ + NTSTATUS status = STATUS_SUCCESS; + POWER_ACTION powerAction; + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); + + PAGED_CODE(); + + powerAction = WdfDeviceGetSystemPowerAction(Device); + + // + // Update the power policy D3-cold info for Connected Standby. + // + if (TargetState == WdfPowerDeviceD3 && powerAction == PowerActionNone) + { + PDEVICE_CONTEXT devCtx; + WDF_TRI_STATE excludeD3Cold = WdfTrue; + ACX_DX_EXIT_LATENCY latency; + + devCtx = GetDeviceContext(Device); + ASSERT(devCtx != nullptr); + + // + // Get the current exit latency. + // + latency = AcxDeviceGetCurrentDxExitLatency(Device, + WdfDeviceGetSystemPowerAction(Device), + TargetState); + + if (latency == AcxDxExitLatencyResponsive) + { + excludeD3Cold = WdfFalse; + } + + if (devCtx->ExcludeD3Cold != excludeD3Cold) + { + devCtx->ExcludeD3Cold = excludeD3Cold; + + status = SetPowerPolicy(Device); + if (!NT_SUCCESS(status)) + { + ASSERT(FALSE); + status = STATUS_SUCCESS; + } + } + } + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit"); + + return status; +} + _Use_decl_annotations_ PAGED_CODE_SEG VOID @@ -655,7 +749,7 @@ Return Value: deviceConfigAttrib.ParentObject = pDeviceContext->UsbDevice; status = WdfMemoryCreate( &deviceConfigAttrib, - PagedPool, + NonPagedPoolNx, USBMIDI_POOLTAG, (size_t)((numChars + 1) * sizeof(WCHAR)), &pDeviceContext->DeviceSNMemory, @@ -713,7 +807,7 @@ Return Value: deviceConfigAttrib.ParentObject = pDeviceContext->UsbDevice; status = WdfMemoryCreate( &deviceConfigAttrib, - PagedPool, + NonPagedPoolNx, USBMIDI_POOLTAG, (size_t)((numChars + 1) * sizeof(WCHAR)), &pDeviceContext->DeviceManfMemory, @@ -766,7 +860,7 @@ Return Value: deviceConfigAttrib.ParentObject = pDeviceContext->UsbDevice; status = WdfMemoryCreate( &deviceConfigAttrib, - PagedPool, + NonPagedPoolNx, USBMIDI_POOLTAG, (size_t)((numChars + 1) * sizeof(WCHAR)), &pDeviceContext->DeviceProductNameMemory, @@ -1091,7 +1185,6 @@ Return Value: // // Configure continuous reader // -#if 1 if (pDeviceContext->MidiInPipe) // only if there is an In endpoint { WDF_USB_CONTINUOUS_READER_CONFIG_INIT( @@ -1114,7 +1207,6 @@ Return Value: return status; } } -#endif TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit"); return STATUS_SUCCESS; @@ -1177,6 +1269,15 @@ Return Value: } goto exit; } + else + { + status = USBMIDI2DriverGetGTBIndexes(Device); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "Error determining GTB indexes for USB MIDI 2.0 device. %!STATUS!", status); + status = STATUS_SUCCESS; + } + } // First get the GTB Header to determine overall size needed // @@ -1223,7 +1324,7 @@ Return Value: goto exit; } - // First take a guess at size and declare memory + // Get size of full GTB memory from header gtbMemorySize = gtbHeader.wTotalLength; // Create temporary memory for holding the GTB data from device @@ -1288,12 +1389,51 @@ Return Value: midi2_cs_interface_desc_group_terminal_blocks* pUSBGTBs = (midi2_cs_interface_desc_group_terminal_blocks *)gtbMemoryPtr; + // Get GTB ID Indexes + size_t numInIDs = 0; + size_t numOutIDs = 0; + PUCHAR inIDs = NULL; + PUCHAR outIDs = NULL; + + if (devCtx->DeviceInGTBIDs) + { + inIDs = (PUCHAR)WdfMemoryGetBuffer(devCtx->DeviceInGTBIDs, &numInIDs); + } + if (devCtx->DeviceOutGTBIDs) + { + outIDs = (PUCHAR)WdfMemoryGetBuffer(devCtx->DeviceOutGTBIDs, &numOutIDs); + } + // Determine the size of GTB Structure UINT grpTermBlockStructureSize = sizeof(KSMULTIPLE_ITEM); USHORT grpTermBlockStringSizes[255]; + bool isInIndex; + UINT numCapturedGTBs = 0; for (UINT termBlockCount = 0; termBlockCount < numTerminalBlocks; termBlockCount++) { + isInIndex = false; + for (size_t count = 0; count < numInIDs; count++) + { + if (pUSBGTBs->aBlock[termBlockCount].bGrpTrmBlkID == inIDs[count]) + { + isInIndex = true; + break; + } + } + for (size_t count = 0; count < numOutIDs; count++) + { + if (pUSBGTBs->aBlock[termBlockCount].bGrpTrmBlkID == outIDs[count]) + { + isInIndex = true; + break; + } + } + + // If not in index then cycle to next GTB + if (!isInIndex) continue; + + numCapturedGTBs++; grpTermBlockStructureSize += sizeof(UMP_GROUP_TERMINAL_BLOCK_HEADER); // See if there is a string defined @@ -1341,7 +1481,7 @@ Return Value: gtbMemoryAttributes.ParentObject = devCtx->UsbDevice; status = WdfMemoryCreate( >bMemoryAttributes, - PagedPool, + NonPagedPoolNx, USBMIDI_POOLTAG, (size_t)grpTermBlockStructureSize, &devCtx->DeviceGTBMemory, @@ -1356,11 +1496,32 @@ Return Value: // Place KSMULTIPLE_ITEM Strucutre PKSMULTIPLE_ITEM pMultiHeader = (PKSMULTIPLE_ITEM)pGTBPropertyBuffer; pMultiHeader->Size = grpTermBlockStructureSize; - pMultiHeader->Count = numTerminalBlocks; + pMultiHeader->Count = numCapturedGTBs; pGTBPropertyBuffer += sizeof(KSMULTIPLE_ITEM); for (UINT termBlockCount = 0; termBlockCount < numTerminalBlocks; termBlockCount++) { + isInIndex = false; + for (size_t count = 0; count < numInIDs; count++) + { + if (pUSBGTBs->aBlock[termBlockCount].bGrpTrmBlkID == inIDs[count]) + { + isInIndex = true; + break; + } + } + for (size_t count = 0; count < numOutIDs; count++) + { + if (pUSBGTBs->aBlock[termBlockCount].bGrpTrmBlkID == outIDs[count]) + { + isInIndex = true; + break; + } + } + + // If not in index then cycle to next GTB + if (!isInIndex) continue; + PUMP_GROUP_TERMINAL_BLOCK_DEFINITION pThisGrpTrmBlk = (PUMP_GROUP_TERMINAL_BLOCK_DEFINITION)pGTBPropertyBuffer; // Populate data from GTB read from USB Device @@ -1539,8 +1700,6 @@ Return Value: // Check the subtype and process accordingly midi_desc_header_common_t* pCommonHdr = (midi_desc_header_common_t*)pNextDescriptor; - //PUINT8 pThisIndex = NULL; - //PUSHORT pThisGTBStringSize = NULL; midi_desc_in_jack_t* pInJack = NULL; midi_desc_out_jack_t* pOutJack = NULL; @@ -1654,7 +1813,7 @@ Return Value: gtbMemoryAttributes.ParentObject = pDevCtx->UsbDevice; status = WdfMemoryCreate( >bMemoryAttributes, - PagedPool, + NonPagedPoolNx, USBMIDI_POOLTAG, (size_t)grpTermBlockStructureSize, &pDevCtx->DeviceGTBMemory, @@ -1722,6 +1881,163 @@ Return Value: return status; } +_Use_decl_annotations_ +PAGED_CODE_SEG +NTSTATUS +USBMIDI2DriverGetGTBIndexes( + _In_ WDFDEVICE Device +) +/*++ +Routine Description: + + This is a helper routine to fetch the GTB IDs indexed by Endpoints for this + device interface. + +Arguments: + Device - the device context from USB Driver + Indexes - pointer to array of indexes to be populated with indexes found. + +Return Value: + + NONE + +--*/ +{ + PDEVICE_CONTEXT pDevCtx = NULL; + PUSB_CONFIGURATION_DESCRIPTOR pConfigurationDescriptor = NULL; + PUCHAR pCurrent = NULL; + PUCHAR pEnd = NULL; + size_t configDescSize; + NTSTATUS status = 0; + WDF_OBJECT_ATTRIBUTES deviceConfigAttrib; + bool isInEndpoint = false; + PUSB_ENDPOINT_DESCRIPTOR pEndpointDescriptor = NULL; + + ASSERT(Device); + pDevCtx = GetDeviceContext(Device); + ASSERT(pDevCtx); + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); + + if (pDevCtx->DeviceGTBMemory) + { + TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE, "GTB response memory already created."); + goto exit; + } + + pConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)WdfMemoryGetBuffer(pDevCtx->DeviceConfigDescriptorMemory, &configDescSize); + // Confirm this is a USB MIDI 2.0 device interface + if (pDevCtx->UsbMIDIbcdMSC != 0x200) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "Unexpected bcdMSC version."); + goto exit; + } + + // Find the relevant interface + PUSB_INTERFACE_DESCRIPTOR pInterfaceDescriptor = USBD_ParseConfigurationDescriptor( + pConfigurationDescriptor, // Descriptor Buffer + pDevCtx->UsbMIDIInterfaceNumber, // Interface Number + pDevCtx->UsbMIDIStreamingAlt // Alternate Setting + ); + if (!pInterfaceDescriptor) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "Could not find interface descriptor for selected device."); + goto exit; + } + // Set current search possition + pCurrent = (PUCHAR)pInterfaceDescriptor + sizeof(USB_INTERFACE_DESCRIPTOR); + + // Find the end of this interface by looking for end or next interface + PUSB_INTERFACE_DESCRIPTOR pInterfaceDescriptorNext = USBD_ParseConfigurationDescriptorEx( + pConfigurationDescriptor, + (PVOID)pCurrent, + -1, // looking for the next interface whatever it is + -1, + -1, + -1, + -1 + ); + if (!pInterfaceDescriptorNext) + { + // means likely last interface descriptor, thus we search to end + pEnd = (PUCHAR)pConfigurationDescriptor + configDescSize; + } + else + { + // Next interface marks end of interface descriptor of interest + pEnd = (PUCHAR)pInterfaceDescriptorNext; + } + + // Now we know the bounds of the descriptor, look for endpoints to determine GTB ID Indexes. + // Walk through finding Class-Specific MIDI Streaming Data Endpoint Descriptors + // Walk through looking for Jack Descriptors + PUSB_COMMON_DESCRIPTOR pNextDescriptor; + while (nullptr != (pNextDescriptor = USBD_ParseDescriptors( + (PVOID)pConfigurationDescriptor, + (ULONG)configDescSize, + (PVOID)pCurrent, + (LONG)USB_ENDPOINT_DESCRIPTOR_TYPE))) + { + // pNextDescriptor is valid, make sure in search area for this interface + if ((PVOID)pNextDescriptor >= pEnd) + { + // Hit end of this interface descriptor + break; + } + + // Get info from Endpoint Descriptor + pEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)pNextDescriptor; + ASSERT(pEndpointDescriptor); + isInEndpoint = (pEndpointDescriptor->bEndpointAddress & 0x80) ? true : false; + + // Advance to next descriptor which should be Class Specific Endpoint Descriptor + pCurrent = (PUCHAR)pNextDescriptor + pNextDescriptor->bLength; + + // Check the subtype and process accordingly + midi_desc_header_common_t* pCommonHdr = (midi_desc_header_common_t*)pCurrent; + + // Is it correct descriptor + if (pCommonHdr->bDescriptorType != MIDI_CS_ENDPOINT || pCommonHdr->bDescriptorSubType != MIDI20_CS_ENDPOINT_GENERAL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "Expected MIDI2.0 CS Endpoint not found."); + goto exit; + } + + midi2_desc_streaming_data_endpoint_t* pCSEndpointHdr = (midi2_desc_streaming_data_endpoint_t*)pCommonHdr; + + // If there are defined GTB IDs and the device does not already have defined GTB IDs for endpoint + // then create and load + if (pCSEndpointHdr->bNumGrpTrmBlock && (isInEndpoint ? !pDevCtx->DeviceInGTBIDs : !pDevCtx->DeviceOutGTBIDs)) + { + // Create temporary memory for holding the GTB data from device + WDF_OBJECT_ATTRIBUTES_INIT(&deviceConfigAttrib); + deviceConfigAttrib.ParentObject = pDevCtx->UsbDevice; + PUCHAR pGTBIDs; + status = WdfMemoryCreate( + &deviceConfigAttrib, + NonPagedPoolNx, + USBMIDI_POOLTAG, + (size_t)pCSEndpointHdr->bNumGrpTrmBlock, + (isInEndpoint) ? &pDevCtx->DeviceInGTBIDs : &pDevCtx->DeviceOutGTBIDs, + (PVOID*)&pGTBIDs + ); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "Error allocating memory for GTB IDs. %!STATUS!", status); + goto exit; + } + RtlCopyMemory(pGTBIDs, (PUCHAR)&pCSEndpointHdr->bAssoGrpTrmBlkID, (size_t)pCSEndpointHdr->bNumGrpTrmBlock); + } + } + +exit: + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit"); + return status; +} + _Use_decl_annotations_ NONPAGED_CODE_SEG VOID USBMIDI2DriverEvtReadComplete( @@ -1753,14 +2069,15 @@ Return Value: NTSTATUS status; PUINT8 pReceivedBuffer; PUINT32 pReceivedWords; - UMP_PACKET umpPacket; + static UMP_PACKET umpPacket; // Keep to fill over two USB packets UINT32 receivedIndex = 0; UINT32 receivedNumWords; - struct + static struct { UMPDATAFORMAT umpHeader; UINT32 umpData[4]; } UMP_Packet_Struct; + static UINT32 wordsRemain = 0; // To know how many words remaining to process Pipe; @@ -1793,7 +2110,7 @@ Return Value: continue; } - // If not alternate indicating USB MIDI 1.0 + // If indicating USB MIDI 1.0 if (pDeviceContext->UsbMIDIbcdMSC == MIDI_CS_BCD_MIDI1) { // Need Cable Number @@ -1819,47 +2136,85 @@ Return Value: } else if (pDeviceContext->UsbMIDIbcdMSC == MIDI_CS_BCD_MIDI2) { - // Put data into buffer to pass - UMP_Packet_Struct.umpHeader.Position = NULL; // For now allow service to tag time - // TODO: Processing for JR Timestamps - umpPacket.wordCount = 0; // use to store information - - PUINT8 pWord = (PUINT8)&pReceivedWords[receivedIndex]; + // Determine if there remains data to transfer meaning UMP broken over two USB packets + if (wordsRemain) + { + UINT32 bytesToCopy; + if (wordsRemain <= (receivedNumWords - receivedIndex)) + { + bytesToCopy = wordsRemain * sizeof(UINT32); + wordsRemain = 0; + } + else + { + bytesToCopy = (receivedNumWords - receivedIndex) * sizeof(UINT32); + wordsRemain -= bytesToCopy / sizeof(UINT32); + } - // Determine amount of data to copy based on message type - switch (pWord[3] & 0xf0) + // Copy more data from bridging UMP over USB packets + RtlCopyMemory( + (PVOID)&UMP_Packet_Struct.umpData[UMP_Packet_Struct.umpHeader.ByteCount / sizeof(UINT32)], + (PVOID)&pReceivedWords[receivedIndex], + bytesToCopy + ); + UMP_Packet_Struct.umpHeader.ByteCount += bytesToCopy; + receivedIndex += bytesToCopy / sizeof(UINT32); + } + else { - case UMP_MT_UTILITY: - case UMP_MT_SYSTEM: - case UMP_MT_MIDI1_CV: - umpPacket.wordCount = 1; - break; + // Put data into buffer to pass + UMP_Packet_Struct.umpHeader.Position = NULL; // For now allow service to tag time + // TODO: Processing for JR Timestamps + umpPacket.wordCount = 0; // use to store information - case UMP_MT_DATA_64: - case UMP_MT_MIDI2_CV: - umpPacket.wordCount = 2; - break; + PUINT8 pWord = (PUINT8)&pReceivedWords[receivedIndex]; - case UMP_MT_DATA_128: - case UMP_MT_FLEX_128: - case UMP_MT_STREAM_128: - umpPacket.wordCount = 4; + // Determine amount of data to copy based on message type + switch (pWord[3] & 0xf0) + { + case UMP_MT_UTILITY: + case UMP_MT_SYSTEM: + case UMP_MT_MIDI1_CV: + umpPacket.wordCount = 1; break; - default: - // Could not process packet so skip current word and try with next one - receivedIndex++; - continue; - } - UMP_Packet_Struct.umpHeader.ByteCount = umpPacket.wordCount * sizeof(UINT32); + case UMP_MT_DATA_64: + case UMP_MT_MIDI2_CV: + umpPacket.wordCount = 2; + break; - // Copy appropriate data - RtlCopyMemory( - (PVOID)&UMP_Packet_Struct.umpData, - (PVOID)&pReceivedWords[receivedIndex], - UMP_Packet_Struct.umpHeader.ByteCount - ); - receivedIndex += umpPacket.wordCount; + case UMP_MT_DATA_128: + case UMP_MT_FLEX_128: + case UMP_MT_STREAM_128: + umpPacket.wordCount = 4; + break; + + default: + // Could not process packet so skip current word and try with next one + receivedIndex++; + continue; + } + + // Determine if all data available to complete UMP packet + if (umpPacket.wordCount <= (receivedNumWords - receivedIndex)) + { + UMP_Packet_Struct.umpHeader.ByteCount = umpPacket.wordCount * sizeof(UINT32); + wordsRemain = 0; // no more to fill data + } + else + { + UMP_Packet_Struct.umpHeader.ByteCount = (receivedNumWords - receivedIndex) * sizeof(UINT32); + wordsRemain = umpPacket.wordCount - (receivedNumWords - receivedIndex); + } + + // Copy appropriate data + RtlCopyMemory( + (PVOID)&UMP_Packet_Struct.umpData, + (PVOID)&pReceivedWords[receivedIndex], + UMP_Packet_Struct.umpHeader.ByteCount + ); + receivedIndex += UMP_Packet_Struct.umpHeader.ByteCount / sizeof(UINT32); + } } else { @@ -1869,24 +2224,27 @@ Return Value: } // Send to circuit - if (pDeviceContext->pStreamEngine) + if (!wordsRemain) { - if (!pDeviceContext->pStreamEngine->FillReadStream( - (PUINT8)&UMP_Packet_Struct, - (size_t)(UMP_Packet_Struct.umpHeader.ByteCount) + sizeof(UMPDATAFORMAT) - )) + if (pDeviceContext->pStreamEngine) { - TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, - "Error submitting to read queue prep buffer.\n"); + if (!pDeviceContext->pStreamEngine->FillReadStream( + (PUINT8)&UMP_Packet_Struct, + (size_t)(UMP_Packet_Struct.umpHeader.ByteCount) + sizeof(UMPDATAFORMAT) + )) + { + TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, + "Error submitting to read queue prep buffer.\n"); + goto ReadCompleteExit; + } + } + else + { + TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE, + "No StreamEngine found in Device Context for filling read stream.\n"); goto ReadCompleteExit; } } - else - { - TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE, - "No StreamEngine found in Device Context for filling read stream.\n"); - goto ReadCompleteExit; - } } } @@ -1938,7 +2296,8 @@ VOID USBMIDI2DriverIoWrite( WDFDEVICE Device, PVOID BufferStart, - size_t numBytes + size_t numBytes, + BOOLEAN isMoreData ) /*++ @@ -1961,13 +2320,12 @@ Return Value:Amy PDEVICE_CONTEXT pDeviceContext = NULL; WDFUSBPIPE pipe = NULL; PUCHAR pBuffer; - PUCHAR pWriteBuffer = NULL; - size_t writeBufferIndex = 0; - WDFREQUEST usbRequest = NULL; - WDFMEMORY writeMemory = NULL; PUINT32 pWriteWords = NULL; WDF_OBJECT_ATTRIBUTES writeMemoryAttributes; - + bool bEnterSysex; + bool bEndSysex; + UINT8 numberBytes; + UINT8 sysexStatus; TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); pDeviceContext = GetDeviceContext(Device); @@ -2076,6 +2434,9 @@ Return Value:Amy case UMP_SYSTEM_STOP: case UMP_SYSTEM_ACTIVE_SENSE: case UMP_SYSTEM_RESET: + case UMP_SYSTEM_UNDEFINED_F4: + case UMP_SYSTEM_UNDEFINED_F5: + case UMP_SYSTEM_UNDEFINED_F9: umpWritePacket.umpData.umpBytes[0] = (cbl_num << 4) | MIDI_CIN_SYSEX_END_1BYTE; break; @@ -2110,102 +2471,140 @@ Return Value:Amy break; case UMP_MT_DATA_64: - { - bool bEndSysex = false; + bEnterSysex = false; + bEndSysex = false; + + umpWritePacket.wordCount = 0; // Determine if sysex will end after this message switch (umpPacket.umpData.umpBytes[1] & UMP_SYSEX7_STATUS_MASK) { case UMP_SYSEX7_COMPLETE: - pDeviceContext->midi1OutSysex[cbl_num].index = 1; + bEnterSysex = true; case UMP_SYSEX7_END: bEndSysex = true; break; case UMP_SYSEX7_START: - pDeviceContext->midi1OutSysex[cbl_num].index = 1; + bEnterSysex = true; default: bEndSysex = false; break; } - // determine the size - UINT8 sysexSize = umpPacket.umpData.umpBytes[1] & UMP_SYSEX7_SIZE_MASK; + if (bEnterSysex) + { + // Determine if believed already in Sysex and if so, reset converter + if (pDeviceContext->midi1OutSysex[cbl_num].inSysex) + { + pDeviceContext->midi1OutSysex[cbl_num].inSysex = false; + } + } - // determine the number of USB MIDI 1 packets to be created - UINT8 numWordsNeeded = (sysexSize + pDeviceContext->midi1OutSysex[cbl_num].index - 1) / 3; - UINT8 numBytesRemain = (sysexSize + pDeviceContext->midi1OutSysex[cbl_num].index - 1) % 3; - if (bEndSysex && numBytesRemain) + if (bEnterSysex && !pDeviceContext->midi1OutSysex[cbl_num].inSysex) { - numWordsNeeded++; // add word for any remaining sysex data + pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Head = 0; + pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Tail = 0; + pDeviceContext->midi1OutSysex[cbl_num].inSysex = true; } - umpWritePacket.wordCount = numWordsNeeded; - UINT8 sysexCount = 0; - UINT8 sysexWordCount = 0; - while (sysexCount < sysexSize && sysexWordCount < numWordsNeeded) + UINT8 byteStream[UMPTOBS_BUFFER]; + sysexStatus = (umpPacket.umpData.umpBytes[1] >> 4); + numberBytes = 0; + + if (sysexStatus <= 1 && numberBytes < UMPTOBS_BUFFER) + { + byteStream[numberBytes++] = SYSEX_START; + } + for (UINT8 count = 0; count < (umpPacket.umpData.umpBytes[1] & 0xf); count++) + { + if (numberBytes < UMPTOBS_BUFFER) + { + byteStream[numberBytes++] = umpPacket.umpData.umpBytes[2 + count]; + } + } + if ((sysexStatus == 0 || sysexStatus == 3) && numberBytes < UMPTOBS_BUFFER) + { + byteStream[numberBytes++] = SYSEX_STOP; + } + + // Move into sysex circular buffer queue + for (UINT8 count = 0; count < numberBytes; count++) { - pDeviceContext->midi1OutSysex[cbl_num].buffer[pDeviceContext->midi1OutSysex[cbl_num].index++] = umpPacket.umpData.umpBytes[2 + sysexCount++]; + pDeviceContext->midi1OutSysex[cbl_num].sysexBS[pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Head++] + = byteStream[count]; + pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Head %= SYSEX_BS_RB_SIZE; + } + + // How many bytes available in BS + numberBytes = (pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Head > pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Tail) + ? pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Head - pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Tail + : (SYSEX_BS_RB_SIZE - pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Head) + pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Tail; - // is current packet build full - if (pDeviceContext->midi1OutSysex[cbl_num].index == 4) + while (numberBytes && umpWritePacket.wordCount < 4) + { + umpWritePacket.umpData.umpWords[umpWritePacket.wordCount] = 0; + + if (numberBytes > 2) + { + PUINT8 pumpBytes = (PUINT8) & umpWritePacket.umpData.umpWords[umpWritePacket.wordCount]; + for (UINT8 count = 0; count < 3; count++) + { + pumpBytes[count + 1] = + pDeviceContext->midi1OutSysex[cbl_num].sysexBS[pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Tail++]; + pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Tail %= SYSEX_BS_RB_SIZE; + numberBytes--; + } + // Mark cable number and CIN for start / continue SYSEX in USB MIDI 1.0 format + if (bEndSysex && !numberBytes) + { + pumpBytes[0] = (UINT8)(cbl_num << 4) | MIDI_CIN_SYSEX_END_3BYTE; + } + else + { + pumpBytes[0] = (UINT8)(cbl_num << 4) | MIDI_CIN_SYSEX_START; + } + } + else { - pDeviceContext->midi1OutSysex[cbl_num].buffer[0] = (cbl_num << 4); + // If less than two and we have a word to populate, check if the end of sysex if (bEndSysex) { - if (sysexCount == sysexSize) + // Process bytes + PUINT8 pumpBytes = (PUINT8)&umpWritePacket.umpData.umpWords[umpWritePacket.wordCount]; + UINT8 count; + for (count = 0; numberBytes; count++) { - pDeviceContext->midi1OutSysex[cbl_num].buffer[0] |= MIDI_CIN_SYSEX_END_3BYTE; + pumpBytes[count + 1] = + pDeviceContext->midi1OutSysex[cbl_num].sysexBS[pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Tail++]; + pDeviceContext->midi1OutSysex[cbl_num].usbMIDI1Tail %= SYSEX_BS_RB_SIZE; + numberBytes--; } - else + // Mark cable number and CIN for start / continue SYSEX in USB MIDI 1.0 format + switch (count) { - pDeviceContext->midi1OutSysex[cbl_num].buffer[0] |= MIDI_CIN_SYSEX_START; + case 1: + pumpBytes[0] = (UINT8)(cbl_num << 4) | MIDI_CIN_SYSEX_END_1BYTE; + break; + + case 2: + default: + pumpBytes[0] = (UINT8)(cbl_num << 4) | MIDI_CIN_SYSEX_END_2BYTE; + break; } } else { - pDeviceContext->midi1OutSysex[cbl_num].buffer[0] |= MIDI_CIN_SYSEX_START; + break; } - - // Fill Word into write packet and reset index - umpWritePacket.umpData.umpWords[sysexWordCount++] = *(PUINT32)&pDeviceContext->midi1OutSysex[cbl_num].buffer; - pDeviceContext->midi1OutSysex[cbl_num].index = 1; - } - } - - // Determine if need to terminate current word - if (bEndSysex && numBytesRemain) - { - // Fill rest of buffer - while (pDeviceContext->midi1OutSysex[cbl_num].index < 4) // WRONG LOGIC HERE - { - pDeviceContext->midi1OutSysex[cbl_num].buffer[pDeviceContext->midi1OutSysex[cbl_num].index++] = 0x00; - } - switch (numBytesRemain) - { - case 1: - pDeviceContext->midi1OutSysex[cbl_num].buffer[0] = (cbl_num << 4) | MIDI_CIN_SYSEX_END_1BYTE; - break; - - case 2: - pDeviceContext->midi1OutSysex[cbl_num].buffer[0] = (cbl_num << 4) | MIDI_CIN_SYSEX_END_2BYTE; - break; - - default: - // should never get here, but use full packet in error - pDeviceContext->midi1OutSysex[cbl_num].buffer[0] = (cbl_num << 4) | MIDI_CIN_SYSEX_END_3BYTE; } - - // Fill Word into write packet - umpWritePacket.umpData.umpWords[sysexWordCount++] = *(PUINT32)&pDeviceContext->midi1OutSysex[cbl_num].buffer; - pDeviceContext->midi1OutSysex[cbl_num].index = 1; + umpWritePacket.wordCount++; } break; - } default: // Not handled so ignore - numProcessed += umpPacket.wordCount; + numProcessed += umpPacket.wordCount; // ignore this UMP packet as corrupted umpWritePacket.wordCount = 0; } @@ -2214,13 +2613,13 @@ Return Value:Amy numProcessed += umpPacket.wordCount; // If currently no write buffer, create one - if (!pWriteBuffer) + if (!pDeviceContext->DeviceWriteBuffer) { // Create Request status = WdfRequestCreate( NULL, // attributes WdfUsbTargetPipeGetIoTarget(pipe), - &usbRequest // retuest object + &pDeviceContext->DeviceUSBWriteRequest // retuest object ); if (!NT_SUCCESS(status)) { @@ -2230,7 +2629,7 @@ Return Value:Amy } WDF_OBJECT_ATTRIBUTES_INIT(&writeMemoryAttributes); - writeMemoryAttributes.ParentObject = usbRequest; + writeMemoryAttributes.ParentObject = pDeviceContext->DeviceUSBWriteRequest; // Create Memory Object status = WdfMemoryCreate( @@ -2238,7 +2637,7 @@ Return Value:Amy NonPagedPoolNx, USBMIDI_POOLTAG, pDeviceContext->MidiOutMaxSize, - &writeMemory, + &pDeviceContext->DeviceWriteMemory, NULL ); if (!NT_SUCCESS(status)) @@ -2247,24 +2646,24 @@ Return Value:Amy "%!FUNC! could not create WdfMemory with status: %!STATUS!", status); goto DriverIoWriteExit; } - pWriteBuffer = (PUCHAR)WdfMemoryGetBuffer(writeMemory, NULL); - pWriteWords = (PUINT32)pWriteBuffer; + pDeviceContext->DeviceWriteBuffer = (PUCHAR)WdfMemoryGetBuffer(pDeviceContext->DeviceWriteMemory, NULL); } + pWriteWords = (PUINT32)pDeviceContext->DeviceWriteBuffer; // Write into buffer for (int count = 0; count < umpWritePacket.wordCount; count++) { - pWriteWords[writeBufferIndex++] = umpWritePacket.umpData.umpWords[count]; + pWriteWords[pDeviceContext->DeviceWriteBufferIndex++] = umpWritePacket.umpData.umpWords[count]; } - if ((writeBufferIndex*sizeof(UINT32)) == pDeviceContext->MidiOutMaxSize) + if ((pDeviceContext->DeviceWriteBufferIndex*sizeof(UINT32)) == pDeviceContext->MidiOutMaxSize) { // Write to buffer if (!USBMIDI2DriverSendToUSB( - usbRequest, - writeMemory, + pDeviceContext->DeviceUSBWriteRequest, + pDeviceContext->DeviceWriteMemory, pipe, - writeBufferIndex*sizeof(UINT32), + pDeviceContext->DeviceWriteBufferIndex*sizeof(UINT32), pDeviceContext, true // delete this request when complete )) @@ -2273,23 +2672,22 @@ Return Value:Amy } // Indicate new buffer needed - pWriteBuffer = NULL; - writeBufferIndex = 0; + pDeviceContext->DeviceWriteBuffer = NULL; + pDeviceContext->DeviceWriteBufferIndex = 0; } } } // Check if anything leftover to write to USB - if (writeBufferIndex) + if (pDeviceContext->DeviceWriteBufferIndex && !isMoreData) // and there is not more data coming { - // NEED TO FILL REST OF BUFFER WITH NULL // // Write to buffer if (!USBMIDI2DriverSendToUSB( - usbRequest, - writeMemory, + pDeviceContext->DeviceUSBWriteRequest, + pDeviceContext->DeviceWriteMemory, pipe, - writeBufferIndex*sizeof(UINT32), + pDeviceContext->DeviceWriteBufferIndex*sizeof(UINT32), pDeviceContext, true // delete this request when complete )) @@ -2298,8 +2696,8 @@ Return Value:Amy } // Indicate new buffer needed - pWriteBuffer = NULL; - writeBufferIndex = 0; + pDeviceContext->DeviceWriteBuffer = NULL; + pDeviceContext->DeviceWriteBufferIndex = 0; } } else if (pDeviceContext->UsbMIDIbcdMSC == MIDI_CS_BCD_MIDI2) @@ -2312,70 +2710,90 @@ Return Value:Amy while (transferPos < numBytes) { - thisTransferSize = numBytes - transferPos; - - // Enusre not bigger than max - if (thisTransferSize > pDeviceContext->MidiOutMaxSize) + // If there is currently no USB packet being worked on, create a working space + if (!pDeviceContext->DeviceWriteBuffer) { - thisTransferSize = pDeviceContext->MidiOutMaxSize; - } + pDeviceContext->DeviceWriteBufferIndex = 0; - // Create request, buffer and copy data into buffer - // Create Request - status = WdfRequestCreate( - NULL, // attributes - WdfUsbTargetPipeGetIoTarget(pipe), - &usbRequest // retuest object - ); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, - "%!FUNC! Error creating request for USB Write with status: %!STATUS!", status); - goto DriverIoWriteExit; + // Create Request + status = WdfRequestCreate( + NULL, // attributes + WdfUsbTargetPipeGetIoTarget(pipe), + &pDeviceContext->DeviceUSBWriteRequest // retuest object + ); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, + "%!FUNC! Error creating request for USB Write with status: %!STATUS!", status); + goto DriverIoWriteExit; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&writeMemoryAttributes); + writeMemoryAttributes.ParentObject = pDeviceContext->DeviceUSBWriteRequest; + + // Create Memory Object + status = WdfMemoryCreate( + &writeMemoryAttributes, + NonPagedPoolNx, + USBMIDI_POOLTAG, + pDeviceContext->MidiOutMaxSize, + &pDeviceContext->DeviceWriteMemory, + NULL + ); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, + "%!FUNC! could not create WdfMemory with status: %!STATUS!", status); + goto DriverIoWriteExit; + } + pDeviceContext->DeviceWriteBuffer = (PUCHAR)WdfMemoryGetBuffer(pDeviceContext->DeviceWriteMemory, NULL); } - WDF_OBJECT_ATTRIBUTES_INIT(&writeMemoryAttributes); - writeMemoryAttributes.ParentObject = usbRequest; + thisTransferSize = numBytes - transferPos; - // Create Memory Object - status = WdfMemoryCreate( - &writeMemoryAttributes, - NonPagedPoolNx, - USBMIDI_POOLTAG, - pDeviceContext->MidiOutMaxSize, - &writeMemory, - NULL - ); - if (!NT_SUCCESS(status)) + bool pushUSBTransfer = false; + PUINT32 pWriteMem = (PUINT32)pDeviceContext->DeviceWriteBuffer; + + // Can we transfer current into buffer? + if ((thisTransferSize + pDeviceContext->DeviceWriteBufferIndex*sizeof(UINT32)) <= pDeviceContext->MidiOutMaxSize) { - TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, - "%!FUNC! could not create WdfMemory with status: %!STATUS!", status); - goto DriverIoWriteExit; + // Copy necessary data into the buffer performing byte swap + PUINT32 pReadMem = (PUINT32)&pBuffer[transferPos]; + for (size_t count = 0; count < (thisTransferSize / sizeof(UINT32)); count++) + { + pWriteMem[pDeviceContext->DeviceWriteBufferIndex++] = pReadMem[count]; // USB is Little Endian format + } + transferPos += thisTransferSize; } - - // Copy necessary data into the buffer performing byte swap - PUINT32 pWriteMem = (PUINT32)WdfMemoryGetBuffer(writeMemory, NULL); - PUINT32 pReadMem = (PUINT32)&pBuffer[transferPos]; - for (size_t count = 0; count < (thisTransferSize / sizeof(UINT32)); count++) + else { - pWriteMem[count] = pReadMem[count]; // USB is Little Endian format + pushUSBTransfer = true; } - // Transfer to USB - if (!USBMIDI2DriverSendToUSB( - usbRequest, - writeMemory, - pipe, - thisTransferSize, - pDeviceContext, - true // delete this request when complete - )) + if (pushUSBTransfer || !isMoreData) { - goto DriverIoWriteExit; - } + // Fill rest of buffer + while (pDeviceContext->DeviceWriteBufferIndex * sizeof(UINT32) < pDeviceContext->MidiOutMaxSize) + { + pWriteMem[pDeviceContext->DeviceWriteBufferIndex++] = 0; + } + + // Write to buffer + if (!USBMIDI2DriverSendToUSB( + pDeviceContext->DeviceUSBWriteRequest, + pDeviceContext->DeviceWriteMemory, + pipe, + pDeviceContext->DeviceWriteBufferIndex * sizeof(UINT32), + pDeviceContext, + true // delete this request when complete + )) + { + goto DriverIoWriteExit; + } - // Increment position - transferPos += thisTransferSize; + pDeviceContext->DeviceWriteBuffer = NULL; + pDeviceContext->DeviceWriteBufferIndex = 0; + } } } else @@ -2652,6 +3070,9 @@ Return Value: case UMP_SYSTEM_STOP: case UMP_SYSTEM_ACTIVE_SENSE: case UMP_SYSTEM_RESET: + case UMP_SYSTEM_UNDEFINED_F4: + case UMP_SYSTEM_UNDEFINED_F5: + case UMP_SYSTEM_UNDEFINED_F9: code_index = MIDI_CIN_SYSEX_END_1BYTE; break; @@ -2660,41 +3081,60 @@ Return Value: } } + UINT8 firstByte = 1; + UINT8 lastByte = 4; + UINT8 copyPos; + switch (code_index) { - case MIDI_CIN_SYSEX_START: - umpPkt->umpData.umpBytes[0] = UMP_MT_DATA_64 | cbl_num; // Message Type and group - - // As this is start of SYSEX, need to set status to indicate so and copy 3 bytes of data - umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_START | 3; + case MIDI_CIN_SYSEX_START: // or continue + + if (!*pbIsInSysex) + { + // SYSEX Start means first byte should be SYSEX start + if (pBuffer[1] != SYSEX_START) return false; + firstByte = 2; + lastByte = 4; - // Set that in SYSEX - *pbIsInSysex = true; + // As this is start of SYSEX, need to set status to indicate so and copy 2 bytes of data + // as first byte of SYSEX_START + umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_START | 2; - // Copy in rest of data - for (int count = 1; count < 4; count++) + // Set that in SYSEX + *pbIsInSysex = true; + } + else { - umpPkt->umpData.umpBytes[count + 1] = pBuffer[count]; + firstByte = 1; + lastByte = 4; + + // As this is in SYSEX, then need to indicate continue + umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_CONTINUE | 3; } - // Pad rest of data - umpPkt->umpData.umpBytes[5] = 0x00; - umpPkt->umpData.umpBytes[6] = 0x00; - umpPkt->umpData.umpBytes[7] = 0x00; + + // Capture Cable number + umpPkt->umpData.umpBytes[0] = UMP_MT_DATA_64 | cbl_num; // Message Type and group umpPkt->wordCount = 2; + // Transfer in bytes + copyPos = firstByte; + for (UINT8 count = 2; count < 8; count++) + { + umpPkt->umpData.umpBytes[count] = (copyPos < lastByte) + ? pBuffer[copyPos++] : 0x00; + } break; case MIDI_CIN_SYSEX_END_1BYTE: // or single byte System Common // Determine if a system common - if ((pBuffer[1] & 0x80) // most significant bit set and not sysex - && (pBuffer[1] != MIDI_STATUS_SYSEX_END)) + if ( (pBuffer[1] & 0x80) // most significant bit set and not sysex ending + && (pBuffer[1] != SYSEX_STOP)) { umpPkt->umpData.umpBytes[0] = UMP_MT_SYSTEM | cbl_num; umpPkt->umpData.umpBytes[1] = pBuffer[1]; - umpPkt->umpData.umpBytes[2] = 0x00; - umpPkt->umpData.umpBytes[3] = 0x00; - umpPkt->wordCount = 1; - break; + firstByte = 1; + lastByte = 1; + goto COMPLETE_1BYTE; } umpPkt->umpData.umpBytes[0] = UMP_MT_DATA_64 | cbl_num; @@ -2702,21 +3142,27 @@ Return Value: // Determine if complete based on if currently in SYSEX if (*pbIsInSysex) { - umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_END | 1; + if (pBuffer[1] != SYSEX_STOP) return false; + umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_END | 0; *pbIsInSysex = false; // we are done with SYSEX + firstByte = 1; + lastByte = 1; } else { - umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_COMPLETE | 1; + // should not get here + return false; } - // Copy in the data, assumes the original USB MIDI 1.0 data has padded data - for (int count = 1; count < 4; count++) +COMPLETE_1BYTE: + umpPkt->wordCount = 2; + // Transfer in bytes + copyPos = firstByte; + for (UINT8 count = 2; count < 8; count++) { - umpPkt->umpData.umpBytes[count + 1] = pBuffer[count]; + umpPkt->umpData.umpBytes[count] = (copyPos < lastByte) + ? pBuffer[copyPos++] : 0x00; } - - umpPkt->wordCount = 2; break; case MIDI_CIN_SYSEX_END_2BYTE: @@ -2725,24 +3171,28 @@ Return Value: // Determine if complete based on if currently in SYSEX if (*pbIsInSysex) { - umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_END | 2; + if (pBuffer[2] != SYSEX_STOP) return false; + umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_END | 1; *pbIsInSysex = false; // we are done with SYSEX + firstByte = 1; + lastByte = 2; } else { - umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_COMPLETE | 2; + umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_COMPLETE | 0; + *pbIsInSysex = false; // we are done with SYSEX + firstByte = 1; + lastByte = 1; } - // Copy in the data, assumes the original USB MIDI 1.0 data has padded data - for (int count = 1; count < 4; count++) + umpPkt->wordCount = 2; + // Transfer in bytes + copyPos = firstByte; + for (UINT8 count = 2; count < 8; count++) { - umpPkt->umpData.umpBytes[count + 1] = pBuffer[count]; + umpPkt->umpData.umpBytes[count] = (copyPos < lastByte) + ? pBuffer[copyPos++] : 0x00; } - // Pad rest of data - umpPkt->umpData.umpBytes[6] = 0x00; - umpPkt->umpData.umpBytes[7] = 0x00; - - umpPkt->wordCount = 2; break; case MIDI_CIN_SYSEX_END_3BYTE: @@ -2751,24 +3201,29 @@ Return Value: // Determine if complete based on if currently in SYSEX if (*pbIsInSysex) { - umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_END | 3; + if (pBuffer[3] != SYSEX_STOP) return false; + umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_END | 2; *pbIsInSysex = false; // we are done with SYSEX + firstByte = 1; + lastByte = 3; } else { - umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_COMPLETE | 3; + if (pBuffer[1] != SYSEX_START || pBuffer[3] != SYSEX_STOP) return false; + umpPkt->umpData.umpBytes[1] = UMP_SYSEX7_COMPLETE | 1; + *pbIsInSysex = false; // we are done with SYSEX + firstByte = 2; + lastByte = 3; } - // Copy in the data, assumes the original USB MIDI 1.0 data has padded data - for (int count = 1; count < 4; count++) + umpPkt->wordCount = 2; + // Transfer in bytes + copyPos = firstByte; + for (UINT8 count = 2; count < 8; count++) { - umpPkt->umpData.umpBytes[count + 1] = pBuffer[count]; + umpPkt->umpData.umpBytes[count] = (copyPos < lastByte) + ? pBuffer[copyPos++] : 0x00; } - // Pad rest of data - umpPkt->umpData.umpBytes[6] = 0x00; - umpPkt->umpData.umpBytes[7] = 0x00; - - umpPkt->wordCount = 2; break; // MIDI1 Channel Voice Messages diff --git a/src/api/Drivers/USBMIDI2/Driver/Device.h b/src/api/Drivers/USBMIDI2/Driver/Device.h index add5b5ac6..647f2c423 100644 --- a/src/api/Drivers/USBMIDI2/Driver/Device.h +++ b/src/api/Drivers/USBMIDI2/Driver/Device.h @@ -50,6 +50,11 @@ Module Name: #include "Common.h" #include "StreamEngine.h" +// Used for handling exchange of SYSEX between USB MIDI 1.0 and UMP +#define UMPTOBS_BUFFER 12 +#define SYSEX_START 0xF0 +#define SYSEX_STOP 0xF7 + /* make prototypes usable from C++ */ #ifdef __cplusplus extern "C" { @@ -58,25 +63,29 @@ extern "C" { // Maximum number of groups per UMP endpoint or virual cables for USB MIDI 1.0 #define MAX_NUM_GROUPS_CABLES 16 +// Size of ring buffers for sysex bytestream +#define SYSEX_BS_RB_SIZE UMPTOBS_BUFFER + 4 // size coming in plus at least one USB MIDI1.0 SYSEX Packet + // -// Structures to aid in conversion between USB MIDI 1.0 and UMP +// Structure to aid in UMP SYSEX to USB MIDI 1.0 // - typedef struct MIDI_STREAM_t - { - UINT8 buffer[4]; - UINT8 index; - UINT8 total; - } MIDI_STREAM_TYPE; - - typedef struct +typedef struct UMP_TO_MIDI1_SYSEX_t +{ + bool inSysex; + UINT8 sysexBS[SYSEX_BS_RB_SIZE]; + UINT8 usbMIDI1Tail; + UINT8 usbMIDI1Head; +} UMP_TO_MIDI1_SYSEX; + +typedef struct +{ + UINT8 wordCount; + union ump_device { - UINT8 wordCount; - union ump_device - { - UINT32 umpWords[4]; - UINT8 umpBytes[sizeof(UINT32) * 4]; - } umpData; - } UMP_PACKET, *PUMP_PACKET; + UINT32 umpWords[4]; + UINT8 umpBytes[sizeof(UINT32) * 4]; + } umpData; +} UMP_PACKET, *PUMP_PACKET; // // Define device context. @@ -96,7 +105,7 @@ typedef struct _DEVICE_CONTEXT { // Buffers and information for USB MIDI 1.0 and UMP translations bool midi1IsInSysex[MAX_NUM_GROUPS_CABLES]; - MIDI_STREAM_TYPE midi1OutSysex[MAX_NUM_GROUPS_CABLES]; + UMP_TO_MIDI1_SYSEX midi1OutSysex[MAX_NUM_GROUPS_CABLES]; // Pipes // Currently setup for single endpoint pair - need to consider for multiple endpoint pairs @@ -119,6 +128,12 @@ typedef struct _DEVICE_CONTEXT { WDFMEMORY DeviceNameMemory; WDFMEMORY DeviceSNMemory; WDFMEMORY DeviceGTBMemory; + WDFMEMORY DeviceInGTBIDs; + WDFMEMORY DeviceOutGTBIDs; + WDFMEMORY DeviceWriteMemory; + PUCHAR DeviceWriteBuffer; + size_t DeviceWriteBufferIndex; + WDFREQUEST DeviceUSBWriteRequest; // // Streaming Engine @@ -134,6 +149,8 @@ EVT_WDF_DRIVER_DEVICE_ADD EvtBusDeviceAdd; EVT_WDF_DEVICE_PREPARE_HARDWARE EvtDevicePrepareHardware; EVT_WDF_DEVICE_RELEASE_HARDWARE EvtDeviceReleaseHardware; EVT_WDF_DEVICE_CONTEXT_CLEANUP EvtDeviceContextCleanup; +EVT_WDF_DEVICE_D0_ENTRY EvtDeviceD0Entry; +EVT_WDF_DEVICE_D0_EXIT EvtDeviceD0Exit; /* make internal prototypes usable from C++ */ #ifdef __cplusplus @@ -198,6 +215,18 @@ USBMIDI2DriverCreateGTB( _In_ WDFDEVICE Device ); +// +// Function to get flags indicating in and Out GTB IDs active +// from Endpoint information in USB descriptors. +// +_Must_inspect_result_ +__drv_requiresIRQL(PASSIVE_LEVEL) +PAGED_CODE_SEG +NTSTATUS +USBMIDI2DriverGetGTBIndexes( + _In_ WDFDEVICE Device +); + // // Function to connect and prepare pipes for use // @@ -242,7 +271,8 @@ VOID USBMIDI2DriverIoWrite( _In_ WDFDEVICE Device, _In_ PVOID BufferStart, - _In_ size_t numBytes + _In_ size_t numBytes, + _In_ BOOLEAN isMoreData ); _Must_inspect_result_ diff --git a/src/api/Drivers/USBMIDI2/Driver/StreamEngine.cpp b/src/api/Drivers/USBMIDI2/Driver/StreamEngine.cpp index f4078a5a6..8d2fcf568 100644 --- a/src/api/Drivers/USBMIDI2/Driver/StreamEngine.cpp +++ b/src/api/Drivers/USBMIDI2/Driver/StreamEngine.cpp @@ -238,7 +238,8 @@ StreamEngine::HandleIo() USBMIDI2DriverIoWrite( AcxCircuitGetWdfDevice(AcxPinGetCircuit(m_Pin)), (PUCHAR)startingReadAddress + sizeof(UMPDATAFORMAT), - thisData->ByteCount + thisData->ByteCount, + (finalReadPosition != midiOutWritePosition) ? TRUE : FALSE // indicates there is more data in this pass or not ); } @@ -297,7 +298,7 @@ Return Value: // Current mechanism to determine if currently processing data is that // the StreamEngine is not null. TBD this mechanism needs to be fixed. - auto lock = m_MidiInLock.acquire(); + //auto lock = m_MidiInLock.acquire(); if (m_IsRunning) { @@ -578,7 +579,7 @@ StreamEngine::Run() { // m_IsRunning is used to indicate running state. If m_IsRunning true, the out worker // thread will output data to the connected device, otherwise data will be thrown away. - auto lock = m_MidiInLock.acquire(); + //auto lock = m_MidiInLock.acquire(); WDFDEVICE devCtx = AcxCircuitGetWdfDevice(AcxPinGetCircuit(m_Pin)); PDEVICE_CONTEXT pDevCtx = GetDeviceContext(devCtx); diff --git a/src/api/Drivers/USBMIDI2/Driver/StreamEngine.h b/src/api/Drivers/USBMIDI2/Driver/StreamEngine.h index b16d71658..c4f80dc94 100644 --- a/src/api/Drivers/USBMIDI2/Driver/StreamEngine.h +++ b/src/api/Drivers/USBMIDI2/Driver/StreamEngine.h @@ -235,9 +235,7 @@ class StreamEngine wil::kernel_event_auto_reset m_ThreadExitEvent; wil::kernel_event_manual_reset m_ThreadExitedEvent {true}; - // m_StandardStreamingLock m_LoopbackMessageQueue are only used // for standard streaming of loopback messages - wil::fast_mutex_with_critical_region m_StandardStreamingLock; wil::fast_mutex_with_critical_region m_MidiInLock; LIST_ENTRY m_LoopbackMessageQueue {nullptr}; }; diff --git a/src/api/Drivers/USBMIDI2/Driver/USBMidi2.inf b/src/api/Drivers/USBMIDI2/Driver/USBMidi2.inf index 73819f029..f53749a11 100644 Binary files a/src/api/Drivers/USBMIDI2/Driver/USBMidi2.inf and b/src/api/Drivers/USBMIDI2/Driver/USBMidi2.inf differ diff --git a/src/api/Drivers/USBMIDI2/Driver/USBMidi2.vcxproj b/src/api/Drivers/USBMIDI2/Driver/USBMidi2.vcxproj index 6ec706132..01a5a8695 100644 --- a/src/api/Drivers/USBMIDI2/Driver/USBMidi2.vcxproj +++ b/src/api/Drivers/USBMIDI2/Driver/USBMidi2.vcxproj @@ -98,19 +98,24 @@ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\ + + false + false + true true trace.h true - false + true sha256 %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib;acx\km\$(ACX_VERSION_MAJOR).$(ACX_VERSION_MINOR)\acxstub.lib;$(DDK_LIB_PATH)\libcntpr.lib;$(DDK_LIB_PATH)\wpprecorder.lib;$(DDK_LIB_PATH)\Usbd.lib + true %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)\acx\km\$(ACX_VERSION_MAJOR).$(ACX_VERSION_MINOR);. @@ -172,10 +177,11 @@ true trace.h true - false + true %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib;acx\km\$(ACX_VERSION_MAJOR).$(ACX_VERSION_MINOR)\acxstub.lib;$(DDK_LIB_PATH)\libcntpr.lib;$(DDK_LIB_PATH)\wpprecorder.lib;$(DDK_LIB_PATH)\Usbd.lib + true stdcpp17 @@ -215,6 +221,7 @@ + diff --git a/src/api/Drivers/USBMIDI2/Driver/ump.h b/src/api/Drivers/USBMIDI2/Driver/ump.h index 1a5e1373c..73bb20056 100644 --- a/src/api/Drivers/USBMIDI2/Driver/ump.h +++ b/src/api/Drivers/USBMIDI2/Driver/ump.h @@ -108,8 +108,11 @@ Module Name: #define UMP_SYSTEM_MTC 0xf1 // 2 bytes incl status #define UMP_SYSTEM_SONG_POS_PTR 0xf2 // 3 bytes incl status #define UMP_SYSTEM_SONG_SELECT 0xf3 // 2 bytes incl status +#define UMP_SYSTEM_UNDEFINED_F4 0xf4 // undefined +#define UMP_SYSTEM_UNDEFINED_F5 0xf5 // undefined #define UMP_SYSTEM_TUNE_REQ 0xf6 // status byte only #define UMP_SYSTEM_TIMING_CLK 0xf8 // status byte only +#define UMP_SYSTEM_UNDEFINED_F9 0xf9 // undefined #define UMP_SYSTEM_START 0xfa // status byte only #define UMP_SYSTEM_CONTINUE 0xfb // status byte only #define UMP_SYSTEM_STOP 0xfc // status byte only @@ -123,6 +126,7 @@ Module Name: typedef enum { MIDI_CS_INTERFACE = 0x24, + MIDI_CS_ENDPOINT = 0x25, MIDI_CS_INTERFACE_GR_TRM_BLOCK = 0x26, } midi_cs_interface_types;