Skip to content

Commit

Permalink
Move USB logic to root complex abstraction
Browse files Browse the repository at this point in the history
This breaks the ability to plug in the USB cable in peripheral mode and
have everything automatically link up (connections saved in flash and
the active song), but this seems uncommon and should be handled via a
`onUSBDeviceAttach` event anyway as it would provide better support when
hosting devices.
  • Loading branch information
sapphire-arches committed Dec 26, 2024
1 parent a559514 commit d3d5a71
Show file tree
Hide file tree
Showing 20 changed files with 1,082 additions and 690 deletions.
1 change: 1 addition & 0 deletions src/RZA1/usb/r_usb_basic/src/driver/r_usb_plibusbip.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "deluge/drivers/usb/userdef/r_usb_pmidi_config.h"
#include "deluge/io/midi/midi_device_manager.h"
#include "deluge/io/midi/midi_engine.h"
#include "deluge/io/usb/usb_state.h"

#if ((USB_CFG_DTC == USB_CFG_ENABLE) || (USB_CFG_DMA == USB_CFG_ENABLE))
#include "RZA1/usb/r_usb_basic/src/hw/inc/r_usb_dmac.h"
Expand Down
26 changes: 13 additions & 13 deletions src/deluge/deluge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
#include "io/midi/midi_device_manager.h"
#include "io/midi/midi_engine.h"
#include "io/midi/midi_follow.h"
#include "io/midi/root_complex/usb_hosted.h"
#include "io/midi/root_complex/usb_peripheral.h"
#include "lib/printf.h" // IWYU pragma: keep this over rides printf with a non allocating version
#include "memory/general_memory_allocator.h"
#include "model/clip/instrument_clip.h"
Expand Down Expand Up @@ -243,14 +245,6 @@ bool readButtonsAndPads() {
usbInitializationPeriodComplete = 1;
}

/*
if (!inSDRoutine && !closedPeripheral && !anythingInitiallyAttachedAsUSBHost && AudioEngine::audioSampleTimer >=
(44100 << 1)) { D_PRINTLN("closing peripheral"); closeUSBPeripheral(); D_PRINTLN("switching back to host");
openUSBHost();
closedPeripheral = true;
}
*/

if (waitingForSDRoutineToEnd) {
if (sdRoutineLock) {
return false;
Expand Down Expand Up @@ -846,14 +840,20 @@ extern "C" int32_t deluge_main(void) {
deluge::io::usb::USBAutoLock lock;
openUSBHost();

// If nothing was plugged in to us as host, we'll go peripheral
// Ideally I'd like to repeatedly switch between host and peripheral mode anytime there's no USB connection.
// To do that, I'd really need to know at any point in time whether the user had just made a connection, just
// then, that hadn't fully initialized yet. I think I sorta have that for host, but not for peripheral yet.
if (!anythingInitiallyAttachedAsUSBHost) {
if (anythingInitiallyAttachedAsUSBHost) {
MIDIDeviceManager::setUSBRoot(new MIDIRootComplexUSBHosted());
}
else {
// If nothing was plugged in to us as host, we'll go peripheral
// Ideally I'd like to repeatedly switch between host and peripheral mode anytime there's no USB connection.
// To do that, I'd really need to know at any point in time whether the user had just made a connection,
// just then, that hadn't fully initialized yet. I think I sorta have that for host, but not for peripheral
// yet.
D_PRINTLN("switching from host to peripheral");
closeUSBHost();
openUSBPeripheral();

// configuredAsPeripheral will set the root complex.
}
}

Expand Down
56 changes: 37 additions & 19 deletions src/deluge/gui/menu_item/midi/devices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#include "io/midi/cable_types/usb_hosted.h"
#include "io/midi/midi_device.h"
#include "io/midi/midi_device_manager.h"
#include "io/midi/midi_root_complex.h"
#include "io/midi/root_complex/usb_hosted.h"
#include "io/midi/root_complex/usb_peripheral.h"
#include "util/container/static_vector.hpp"
#include <string_view>

Expand All @@ -37,7 +40,12 @@ static constexpr int32_t lowestDeviceNum = -3;
void Devices::beginSession(MenuItem* navigatedBackwardFrom) {
bool found = false;
if (navigatedBackwardFrom != nullptr) {
for (int32_t idx = lowestDeviceNum; idx < MIDIDeviceManager::hostedMIDIDevices.getNumElements(); idx++) {
// This will technically do the wrong thing when we're in peripheral mode (it'll set the max index to 2 instead
// of 0, which would be accurate) but it should be harmless -- `Devices::getCable` should just return nullptr in
// that case which we handle fine already anyway.
auto maxIndex =
(MIDIDeviceManager::rootUSB != nullptr) ? MIDIDeviceManager::rootUSB->getNumCables() : lowestDeviceNum + 1;
for (int32_t idx = lowestDeviceNum; idx < maxIndex; idx++) {
if (getCable(idx) == soundEditor.currentMIDICable) {
found = true;
this->setValue(idx);
Expand All @@ -62,10 +70,12 @@ void Devices::beginSession(MenuItem* navigatedBackwardFrom) {
void Devices::selectEncoderAction(int32_t offset) {
offset = std::clamp<int32_t>(offset, -1, 1);

auto maxIndex = (MIDIDeviceManager::rootUSB == nullptr) ? 0 : MIDIDeviceManager::rootUSB->getNumCables();

do {
int32_t newValue = this->getValue() + offset;

if (newValue >= MIDIDeviceManager::hostedMIDIDevices.getNumElements()) {
if (newValue >= maxIndex) {
if (display->haveOLED()) {
return;
}
Expand All @@ -75,14 +85,14 @@ void Devices::selectEncoderAction(int32_t offset) {
if (display->haveOLED()) {
return;
}
newValue = MIDIDeviceManager::hostedMIDIDevices.getNumElements() - 1;
newValue = maxIndex - 1;
}

this->setValue(newValue);

soundEditor.currentMIDICable = getCable(this->getValue());

} while (!soundEditor.currentMIDICable->connectionFlags);
} while (soundEditor.currentMIDICable == nullptr && soundEditor.currentMIDICable->connectionFlags == 0);
// Don't show devices which aren't connected. Sometimes we won't even have a name to display for them.

if (display->haveOLED()) {
Expand Down Expand Up @@ -115,24 +125,31 @@ void Devices::selectEncoderAction(int32_t offset) {
}

MIDICable* Devices::getCable(int32_t deviceIndex) {
if (deviceIndex < lowestDeviceNum || deviceIndex >= MIDIDeviceManager::hostedMIDIDevices.getNumElements()) {
if (deviceIndex < lowestDeviceNum) {
D_PRINTLN("impossible device request");
return nullptr;
}
switch (deviceIndex) {
case -3: {

if (deviceIndex == -3) {
return &MIDIDeviceManager::rootDin.cable;
}
case -2: {
return &MIDIDeviceManager::upstreamUSBMIDICable1;
}
case -1: {
return &MIDIDeviceManager::upstreamUSBMIDICable2;
}
default: {
return static_cast<MIDICable*>(MIDIDeviceManager::hostedMIDIDevices.getElement(deviceIndex));
}

if (MIDIDeviceManager::rootUSB != nullptr) {
auto& rootUSB = *MIDIDeviceManager::rootUSB;
if (deviceIndex < 0) {
if (rootUSB.getType() == RootComplexType::RC_USB_PERIPHERAL && deviceIndex >= -2) {
return rootUSB.getCable(deviceIndex + 2);
}
return nullptr;
}

if (rootUSB.getType() == RootComplexType::RC_USB_HOST) {
auto& usb = static_cast<MIDIRootComplexUSBHosted&>(rootUSB);
return usb.getCable(deviceIndex);
}
}

return nullptr;
}

void Devices::drawValue() {
Expand All @@ -154,11 +171,12 @@ void Devices::drawPixelsForOled() {

int32_t selectedRow = -1;

int32_t device_idx = currentScroll;
auto device_idx = currentScroll;
size_t row = 0;
while (row < kOLEDMenuNumOptionsVisible && device_idx < MIDIDeviceManager::hostedMIDIDevices.getNumElements()) {
auto max_index = (MIDIDeviceManager::rootUSB == nullptr) ? 0 : MIDIDeviceManager::rootUSB->getNumCables();
while (row < kOLEDMenuNumOptionsVisible && device_idx < static_cast<ptrdiff_t>(max_index)) {
MIDICable* cable = getCable(device_idx);
if (cable && cable->connectionFlags != 0u) {
if (cable != nullptr && cable->connectionFlags != 0u) {
itemNames.push_back(cable->getDisplayName());
if (device_idx == this->getValue()) {
selectedRow = static_cast<int32_t>(row);
Expand Down
2 changes: 1 addition & 1 deletion src/deluge/gui/menu_item/midi/devices.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Devices final : public Value<int32_t> {
void drawPixelsForOled();

private:
size_t currentScroll;
ptrdiff_t currentScroll;
};

extern Devices devicesMenu;
Expand Down
55 changes: 54 additions & 1 deletion src/deluge/io/midi/cable_types/usb_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,41 @@
#include "usb_common.h"
#include "io/midi/midi_engine.h"

void MIDICableUSB::checkIncomingSysex(uint8_t const* msg, int32_t ip, int32_t d) {
ConnectedUSBMIDIDevice* connected = &connectedUSBMIDIDevices[ip][d];

uint8_t statusType = msg[0] & 15;
int32_t to_read = 0;
bool will_end = false;
if (statusType == 0x4) {
// sysex start or continue
if (msg[1] == 0xf0) {
this->incomingSysexPos = 0;
}
to_read = 3;
}
else if (statusType >= 0x5 && statusType <= 0x7) {
to_read = statusType - 0x4; // read between 1-3 bytes
will_end = true;
}

for (int32_t i = 0; i < to_read; i++) {
if (this->incomingSysexPos >= sizeof(this->incomingSysexBuffer)) {
// TODO: allocate a GMA buffer to some bigger size
this->incomingSysexPos = 0;
return; // bail out
}
this->incomingSysexBuffer[this->incomingSysexPos++] = msg[i + 1];
}

if (will_end) {
if (this->incomingSysexBuffer[0] == 0xf0) {
midiEngine.midiSysexReceived(*this, this->incomingSysexBuffer, this->incomingSysexPos);
}
this->incomingSysexPos = 0;
}
}

void MIDICableUSB::connectedNow(int32_t midiDeviceNum) {
connectionFlags |= (1 << midiDeviceNum);
needsToSendMCMs = 2;
Expand All @@ -32,6 +67,24 @@ void MIDICableUSB::sendMCMsNowIfNeeded() {
}
}

static uint32_t setupUSBMessage(MIDIMessage message) {
// format message per USB midi spec on virtual cable 0
uint8_t cin;
uint8_t firstByte = (message.channel & 15) | (message.statusType << 4);

switch (firstByte) {
case 0xF2: // Position pointer
cin = 0x03;
break;

default:
cin = message.statusType; // Good for voice commands
break;
}

return ((uint32_t)message.data2 << 24) | ((uint32_t)message.data1 << 16) | ((uint32_t)firstByte << 8) | cin;
}

Error MIDICableUSB::sendMessage(MIDIMessage message) {
if (!connectionFlags) {
return Error::NONE;
Expand All @@ -46,7 +99,7 @@ Error MIDICableUSB::sendMessage(MIDIMessage message) {
ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][d];
if (connectedDevice->canHaveMIDISent) {
uint32_t channeledMessage = fullMessage | (portNumber << 4);
connectedDevice->bufferMessage(fullMessage);
connectedDevice->bufferMessage(channeledMessage);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/deluge/io/midi/cable_types/usb_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class MIDICableUSB : public MIDICable {
[[nodiscard]] Error sendSysex(const uint8_t* data, int32_t len) override;
[[nodiscard]] size_t sendBufferSpace() const override;

void checkIncomingSysex(uint8_t const* msg, int32_t ip, int32_t d);
void connectedNow(int32_t midiDeviceNum);
void sendMCMsNowIfNeeded();
uint8_t needsToSendMCMs;
Expand Down
9 changes: 6 additions & 3 deletions src/deluge/io/midi/device_specific/specific_midi_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ SpecificMidiDeviceType getSpecificMidiDeviceType(uint16_t vendorId, uint16_t pro
void iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook hook) {
using namespace MIDIDeviceManager;

for (int32_t i = 0; i < hostedMIDIDevices.getNumElements(); i++) {
auto* specificDevice = static_cast<MIDICableUSBHosted*>(hostedMIDIDevices.getElement(i));
if (rootUSB == nullptr || rootUSB->getType() != RootComplexType::RC_USB_HOST) {
return;
}

specificDevice->callHook(hook);
for (auto& c : rootUSB->getCables()) {
auto& cable = static_cast<MIDICableUSBHosted&>(c);
cable.callHook(hook);
}
}
Loading

0 comments on commit d3d5a71

Please sign in to comment.