// // kernel.cpp // // Piano LED Visualizer for Circle. // // The Pi is a USB MIDI *gadget*: the PC is the host, and this firmware appears // on the PC as an ordinary ALSA MIDI output port. Circle has no OTG support, so // the USB controller is gadget-only in this build and the piano can never be // plugged in here directly - all MIDI arrives from the PC. See section 2 of // PIANO-LED-CIRCLE-PLAN.md. // #include "kernel.h" #include #include #include static const char FromKernel[] = "kernel"; CKernel::CKernel (void) : m_Timer (&m_Interrupt), m_Logger (m_Options.GetLogLevel (), &m_Timer), m_pUSB (new CUSBMIDIGadget (&m_Interrupt)), m_pMIDIDevice (0) { m_ActLED.Blink (5); // show we are alive } CKernel::~CKernel (void) { } boolean CKernel::Initialize (void) { boolean bOK = TRUE; if (bOK) { bOK = m_Serial.Initialize (115200); } if (bOK) { // Headless appliance: there is no screen, so the log goes to the // serial port and nowhere else. bOK = m_Logger.Initialize (&m_Serial); } if (bOK) { bOK = m_Interrupt.Initialize (); } if (bOK) { bOK = m_Timer.Initialize (); } if (bOK) { // Bring the strip up before USB, so the LEDs are known-dark by the // time the host can start sending us notes. bOK = m_PianoLEDs.Initialize (); } if (bOK) { assert (m_pUSB != 0); bOK = m_pUSB->Initialize (); } return bOK; } void CKernel::UpdateMIDIDevice (void) { assert (m_pUSB != 0); if (!m_pUSB->UpdatePlugAndPlay ()) { return; } // The gadget deletes its CUSBMIDIDevice when the host suspends the bus // and builds a new one on the next enumeration, so the pointer we hold // is only valid until the next status change. Re-fetch it every time. CUSBMIDIDevice *pMIDIDevice = (CUSBMIDIDevice *) m_DeviceNameService.GetDevice ("umidi1", FALSE); if (pMIDIDevice == m_pMIDIDevice) { return; } m_pMIDIDevice = pMIDIDevice; if (m_pMIDIDevice != 0) { m_PianoLEDs.AttachMIDIDevice (m_pMIDIDevice); m_Logger.Write (FromKernel, LogNotice, "USB MIDI gadget connected"); } else { // Host went away mid-chord. Do not leave keys lit. m_PianoLEDs.AllOff (); m_Logger.Write (FromKernel, LogNotice, "USB MIDI gadget disconnected"); } } TShutdownMode CKernel::Run (void) { m_Logger.Write (FromKernel, LogNotice, "Compile time: " __DATE__ " " __TIME__); m_Logger.Write (FromKernel, LogNotice, "%u LEDs, %u keys, notes %u-%u, %s orientation", (unsigned) LED_COUNT, (unsigned) KEY_COUNT, (unsigned) MIDI_NOTE_MIN, (unsigned) MIDI_NOTE_MAX, STRIP_REVERSED ? "reversed" : "normal"); m_Logger.Write (FromKernel, LogNotice, "Brightness ceiling %u/255, at most %u keys lit at once", (unsigned) GLOBAL_BRIGHTNESS, (unsigned) MAX_LIT_KEYS); m_Logger.Write (FromKernel, LogNotice, "Waiting for USB host"); for (;;) { UpdateMIDIDevice (); // Rendering blocks for ~5.3ms of SPI traffic, which is why it runs // here and not in the MIDI packet handler's IRQ context. Update() // returns immediately when nothing has changed. m_PianoLEDs.Update (); } return ShutdownHalt; }