~aleteoryx/muditaos

183617d3e29d42488ae73625a1ef3ef004bf6780 — Marek Niepieklo 4 years ago b3a44f0
[EGD-6416] Add handling of headset keys

Change the volume level using headset keys
M module-bsp/board/linux/headset/headset.cpp => module-bsp/board/linux/headset/headset.cpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "headset.hpp"


@@ 13,9 13,9 @@ namespace bsp
            return 1;
        }

        bool Handler(uint8_t notification)
        HeadsetState headset_get_data(bool &headsetState, uint8_t &keyEvent, uint8_t &keyCode)
        {
            return false;
            return HeadsetState::NoChange;
        }

        bool IsInserted()

M module-bsp/board/rt1051/bsp/headset/headset.cpp => module-bsp/board/rt1051/bsp/headset/headset.cpp +182 -70
@@ 13,35 13,74 @@ namespace bsp
{
    namespace headset
    {
        using namespace drivers;

        static constexpr uint8_t HEADSET_I2C_ADDR     = 0x3B;
        static constexpr uint8_t HEADSET_INT_REG_ADDR = 0x01;
        static constexpr uint8_t HEADSET_INT_DIS_ADDR = 0x03;
        static constexpr uint8_t HEADSET_DEV_SET_ADDR = 0x04;
        static constexpr uint8_t HEADSET_DET_RES_ADDR = 0x0B;

        static constexpr uint8_t HEADSET_DET_THRESH1_ADDR = 0x0D;
        static constexpr uint8_t HEADSET_DET_THRESH2_ADDR = 0x0E;
        static constexpr uint8_t HEADSET_DET_THRESH3_ADDR = 0x0F;

        static constexpr uint8_t HEADSET_INT_DIS_INT_DIS = 1 << 3;
        static constexpr uint8_t HEADSET_INT_DIS_INT_ENA = 0 << 3;
        static constexpr uint8_t HEADSET_INT_DIS_ADC_DIS = 1 << 2;
        static constexpr uint8_t HEADSET_INT_DIS_ADC_ENA = 0 << 2;
        static constexpr uint8_t HEADSET_INT_DIS_DC_DIS  = 1 << 1;
        static constexpr uint8_t HEADSET_INT_DIS_DC_ENA  = 0 << 1;
        static constexpr uint8_t HEADSET_INT_DIS_INS_DIS = 1 << 0;
        static constexpr uint8_t HEADSET_INT_DIS_INS_ENA = 0 << 0;

        static constexpr uint8_t HEADSET_DET_THRESH1_VAL = 0x22;
        static constexpr uint8_t HEADSET_DET_THRESH2_VAL = 0x23;
        static constexpr uint8_t HEADSET_DET_THRESH3_VAL = 0x6C;

        static constexpr uint8_t HEADSET_DEV_SET_DET_EN = 1 << 5;
        static constexpr uint8_t HEADSET_DEV_SET_DEB_1S = 0x06;

        static constexpr uint16_t HEADSET_POLL_INTERVAL_MS = 500;
        namespace
        {
            using namespace drivers;

            constexpr uint8_t HEADSET_I2C_ADDR = 0x3B;

            constexpr uint8_t HEADSET_INTERRUPT_REG      = 0x01;
            constexpr uint8_t HEADSET_KEY_PRESS_INT_REG  = 0x02;
            constexpr uint8_t HEADSET_INTERRUPT_DIS_REG  = 0x03;
            constexpr uint8_t HEADSET_DEV_SETTINGS_REG   = 0x04;
            constexpr uint8_t HEADSET_DEV_SETTING_1_REG  = 0x05;
            constexpr uint8_t HEADSET_ACCESSORY_STAT_REG = 0x0B;

            constexpr uint8_t HEADSET_DET_THRESH1_ADDR = 0x0D;
            constexpr uint8_t HEADSET_DET_THRESH2_ADDR = 0x0E;
            constexpr uint8_t HEADSET_DET_THRESH3_ADDR = 0x0F;

            constexpr uint8_t HEADSET_INT_DIS_INT_DIS = 1 << 3;
            constexpr uint8_t HEADSET_INT_DIS_INT_ENA = 0 << 3;
            constexpr uint8_t HEADSET_INT_DIS_ADC_DIS = 1 << 2;
            constexpr uint8_t HEADSET_INT_DIS_ADC_ENA = 0 << 2;
            constexpr uint8_t HEADSET_INT_DIS_DC_DIS  = 1 << 1;
            constexpr uint8_t HEADSET_INT_DIS_DC_ENA  = 0 << 1;
            constexpr uint8_t HEADSET_INT_DIS_INS_DIS = 1 << 0;
            constexpr uint8_t HEADSET_INT_DIS_INS_ENA = 0 << 0;

            constexpr uint8_t HEADSET_DET_THRESH1_VAL = 0x22;
            constexpr uint8_t HEADSET_DET_THRESH2_VAL = 0x23;
            constexpr uint8_t HEADSET_DET_THRESH3_VAL = 0x6C;

            constexpr uint8_t HEADSET_DEV_SET_DET_TRIG = 1 << 4;
            constexpr uint8_t HEADSET_DEV_SET_DET_EN   = 1 << 5;
            constexpr uint8_t HEADSET_DEV_SET_RESET    = 1 << 7;
            constexpr uint8_t HEADSET_DEV_SET_DEB_1S   = 0x06;

            constexpr uint8_t HEADSET_DEV_SET1_KP_EN = 1 << 2;

            constexpr uint16_t HEADSET_POLL_INTERVAL_MS = 500;

            enum class HeadsetIQRDetectionResult : uint8_t
            {
                DETRES_3POLE            = 1 << 0,
                DETRES_OMTP             = 1 << 1,
                DETRES_CTIA             = 1 << 2,
                DETRES_INSERTION_STATUS = 1 << 3
            };

            enum class HeadsetIQRKeyPressStatus : uint8_t
            {
                KEY4_RELEASE = 1 << 7,
                KEY4_PRESS   = 1 << 6,
                KEY3_RELEASE = 1 << 5,
                KEY3_PRESS   = 1 << 4,
                KEY2_RELEASE = 1 << 3,
                KEY2_PRESS   = 1 << 2,
                KEY1_RELEASE = 1 << 1,
                KEY1_PRESS   = 1 << 0,
            };

            enum class HeadsetType : uint8_t
            {
                Type3Pole,
                TypeOMTP,
                TypeCTIA,
                TypeDontCare
            };

        } // anonymous namespace

        static std::shared_ptr<drivers::DriverI2C> i2c;
        static std::shared_ptr<drivers::DriverGPIO> gpio;


@@ 53,53 92,127 @@ namespace bsp
        static bool HeadsetInserted    = false;
        static bool MicrophoneInserted = false;

        static bool ReadInsertionStatus()
        static HeadsetType insertedHeadsetType = HeadsetType::TypeDontCare;

        static void readKeyCodeAndEvent(uint8_t &keyEvent, uint8_t &keyCode)
        {
            uint8_t keypress_int = 0;

            i2cAddr.subAddress = HEADSET_KEY_PRESS_INT_REG;
            i2c->Read(i2cAddr, static_cast<uint8_t *>(&keypress_int), 1);

            switch (static_cast<HeadsetIQRKeyPressStatus>(keypress_int)) {
            case HeadsetIQRKeyPressStatus::KEY4_RELEASE:
                keyCode  = static_cast<uint8_t>(KeyCode::Key4);
                keyEvent = static_cast<uint8_t>(KeyEvent::KeyReleased);
                break;
            case HeadsetIQRKeyPressStatus::KEY4_PRESS:
                keyCode  = static_cast<uint8_t>(KeyCode::Key4);
                keyEvent = static_cast<uint8_t>(KeyEvent::KeyPressed);
                break;
            case HeadsetIQRKeyPressStatus::KEY3_RELEASE:
                keyCode  = static_cast<uint8_t>(KeyCode::Key3);
                keyEvent = static_cast<uint8_t>(KeyEvent::KeyReleased);
                break;
            case HeadsetIQRKeyPressStatus::KEY3_PRESS:
                keyCode  = static_cast<uint8_t>(KeyCode::Key3);
                keyEvent = static_cast<uint8_t>(KeyEvent::KeyPressed);
                break;
            case HeadsetIQRKeyPressStatus::KEY2_RELEASE:
                keyCode  = static_cast<uint8_t>(KeyCode::Key2);
                keyEvent = static_cast<uint8_t>(KeyEvent::KeyReleased);
                break;
            case HeadsetIQRKeyPressStatus::KEY2_PRESS:
                keyCode  = static_cast<uint8_t>(KeyCode::Key2);
                keyEvent = static_cast<uint8_t>(KeyEvent::KeyPressed);
                break;
            case HeadsetIQRKeyPressStatus::KEY1_RELEASE:
                keyCode  = static_cast<uint8_t>(KeyCode::Key1);
                keyEvent = static_cast<uint8_t>(KeyEvent::KeyReleased);
                break;
            case HeadsetIQRKeyPressStatus::KEY1_PRESS:
                keyCode  = static_cast<uint8_t>(KeyCode::Key1);
                keyEvent = static_cast<uint8_t>(KeyEvent::KeyPressed);
                break;
            default:
                keyCode  = static_cast<uint8_t>(KeyCode::Error);
                keyEvent = static_cast<uint8_t>(KeyEvent::Error);
                break;
            }
        }

        HeadsetState headset_get_data(bool &headsetState, uint8_t &keyEvent, uint8_t &keyCode)
        {
            uint8_t reg;
            bool ret = false;
            uint8_t acc_status              = 0;
            HeadsetState headsetStateChange = HeadsetState::NoChange;

            i2cAddr.subAddress = HEADSET_INT_REG_ADDR;
            i2c->Read(i2cAddr, (uint8_t *)&reg, 1);
            // Clear event flags
            i2cAddr.subAddress = HEADSET_INTERRUPT_REG;
            i2c->Read(i2cAddr, static_cast<uint8_t *>(&reg), 1);

            i2cAddr.subAddress = HEADSET_DET_RES_ADDR;
            i2c->Read(i2cAddr, (uint8_t *)&reg, 1);
            i2cAddr.subAddress = HEADSET_ACCESSORY_STAT_REG;
            i2c->Read(i2cAddr, static_cast<uint8_t *>(&acc_status), 1);

            if (((reg & 0x08) == 0) && (HeadsetInserted == true)) {
            // Check if jack removed event
            if (((acc_status & static_cast<uint8_t>(HeadsetIQRDetectionResult::DETRES_INSERTION_STATUS)) == 0) &&
                (HeadsetInserted == true)) {
                HeadsetInserted = false;
                MicrophoneInserted = false;

                LOG_INFO("Headset removed");
                gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::MIC_BIAS_DRIVER_EN), 0);

                ret = true;
            }
                // Turn off key press/release detection
                reg                = HEADSET_DEV_SET1_KP_EN;
                i2cAddr.subAddress = HEADSET_DEV_SETTING_1_REG;
                i2c->Modify(i2cAddr, reg, MicrophoneInserted, 1);

            if (((reg & 0x08) != 0) && (HeadsetInserted == false)) {
                headsetState       = static_cast<uint8_t>(HeadsetInserted);
                headsetStateChange = HeadsetState::Changed;
            }
            else if (((acc_status & static_cast<uint8_t>(HeadsetIQRDetectionResult::DETRES_INSERTION_STATUS)) != 0) &&
                     (HeadsetInserted == false)) {
                LOG_INFO("Headset inserted");
                HeadsetInserted = true;

                if ((reg & 0x01) != 0) {
                acc_status &= ~static_cast<uint8_t>(HeadsetIQRDetectionResult::DETRES_INSERTION_STATUS);

                switch (static_cast<HeadsetIQRDetectionResult>(acc_status)) {
                case HeadsetIQRDetectionResult::DETRES_3POLE:
                    LOG_INFO("Headset 3-pole detected");
                    MicrophoneInserted = false;
                }
                if ((reg & 0x02) != 0) {
                    insertedHeadsetType = HeadsetType::Type3Pole;
                    break;
                case HeadsetIQRDetectionResult::DETRES_OMTP:
                    LOG_INFO("Headset 4-pole OMTP detected");
                    MicrophoneInserted = true;
                }
                if ((reg & 0x04) != 0) {
                    insertedHeadsetType = HeadsetType::TypeOMTP;
                    break;
                case HeadsetIQRDetectionResult::DETRES_CTIA:
                    LOG_INFO("Headset 4-pole Standard detected");
                    MicrophoneInserted = true;
                    insertedHeadsetType = HeadsetType::TypeCTIA;
                    break;
                default:
                    LOG_INFO("uknown Headset type detected");
                    MicrophoneInserted  = false;
                    insertedHeadsetType = HeadsetType::TypeDontCare;
                }
                ret = true;
            }

            if (MicrophoneInserted == true) {
                gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::MIC_BIAS_DRIVER_EN), 1);
                // Turn on/off key press/release detection
                reg                = HEADSET_DEV_SET1_KP_EN;
                i2cAddr.subAddress = HEADSET_DEV_SETTING_1_REG;
                i2c->Modify(i2cAddr, reg, MicrophoneInserted, 1);

                headsetState       = static_cast<uint8_t>(HeadsetInserted);
                headsetStateChange = HeadsetState::Changed;
            }
            // Key pressed/released event
            else {
                gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::MIC_BIAS_DRIVER_EN), 0);
                readKeyCodeAndEvent(keyEvent, keyCode);
            }

            return ret;
            return headsetStateChange;
        }

        static void TimerHandler(TimerHandle_t xTimer)


@@ 123,7 236,7 @@ namespace bsp

        status_t Init(xQueueHandle qHandle)
        {
            // Microphone jack external GPIO pin configuration
            // Headset jack external GPIO pin configuration
            gpio = DriverGPIO::Create(static_cast<GPIOInstances>(BoardDefinitions::MIC_BIAS_DRIVER_GPIO),
                                      DriverGPIOParams{});



@@ 132,7 245,7 @@ namespace bsp
                                              .defLogic = 0,
                                              .pin      = static_cast<uint32_t>(BoardDefinitions::MIC_BIAS_DRIVER_EN)});

            gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::MIC_BIAS_DRIVER_EN), 0);
            gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::MIC_BIAS_DRIVER_EN), 1);

            // Jack detection I2C and GPIO configuration



@@ 152,21 265,25 @@ namespace bsp
                static_cast<I2CInstances>(BoardDefinitions::HEADSET_I2C),
                DriverI2CParams{.baudrate = static_cast<uint32_t>(BoardDefinitions::HEADSET_I2C_BAUDRATE)});

            // Reset headset controller
            uint8_t reg        = HEADSET_DEV_SET_RESET;
            i2cAddr.subAddress = HEADSET_DEV_SETTINGS_REG;
            i2c->Write(i2cAddr, static_cast<uint8_t *>(&reg), 1);

            qHandleIrq = qHandle;

            HeadsetInserted = false;
            MicrophoneInserted = false;

            // Turn off DC and ADC conversion interrupt
            uint8_t reg =
                HEADSET_INT_DIS_INT_ENA | HEADSET_INT_DIS_ADC_ENA | HEADSET_INT_DIS_DC_ENA | HEADSET_INT_DIS_INS_ENA;

            i2cAddr.subAddress = HEADSET_INT_DIS_ADDR;
            // Set Insertion de-bounce time to 1s, enable auto-detection and manually trigger detection
            reg                = HEADSET_DEV_SET_DEB_1S | HEADSET_DEV_SET_DET_EN | HEADSET_DEV_SET_DET_TRIG;
            i2cAddr.subAddress = HEADSET_DEV_SETTINGS_REG;
            i2c->Write(i2cAddr, static_cast<uint8_t *>(&reg), 1);

            // Set Insertion de-bounce time to 1s, enable auto-detection
            reg                = HEADSET_DEV_SET_DET_EN | HEADSET_DEV_SET_DEB_1S;
            i2cAddr.subAddress = HEADSET_DEV_SET_ADDR;
            // Turn off DC and ADC conversion interrupt
            reg = HEADSET_INT_DIS_INT_ENA | HEADSET_INT_DIS_ADC_DIS | HEADSET_INT_DIS_DC_DIS | HEADSET_INT_DIS_INS_ENA;

            i2cAddr.subAddress = HEADSET_INTERRUPT_DIS_REG;
            i2c->Write(i2cAddr, static_cast<uint8_t *>(&reg), 1);

            // Set detection thresholds


@@ 196,15 313,6 @@ namespace bsp
            return kStatus_Success;
        }

        bool Handler(uint8_t notification)
        {
            if (notification == 0x01) {
                return ReadInsertionStatus();
            }

            return false;
        }

        bool IsInserted()
        {
            return HeadsetInserted;


@@ 216,9 324,13 @@ namespace bsp
            HeadsetInserted = false;
            MicrophoneInserted = false;

            uint8_t reg        = HEADSET_DEV_SET_RESET;
            i2cAddr.subAddress = HEADSET_DEV_SETTINGS_REG;
            i2c->Write(i2cAddr, static_cast<uint8_t *>(&reg), 1);
            i2c.reset();

            gpio->WritePin(static_cast<uint32_t>(BoardDefinitions::MIC_BIAS_DRIVER_EN), 0);
            gpio->DisableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::HEADSET_IRQ_PIN));

            return kStatus_Success;
        }

M module-bsp/bsp/headset/headset.hpp => module-bsp/bsp/headset/headset.hpp +20 -2
@@ 1,18 1,36 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "FreeRTOS.h"
#include "queue.h"

#include "headset_key_codes.hpp"

namespace bsp {
 namespace headset {

    /**
     * @brief This method is responsible for initializing the headset controller.
     * @param qHandle key code
     */
    int32_t Init(xQueueHandle qHandle);

    bool Handler(uint8_t notification);
    /**
     * @brief This method is responsible for reading state of headset and its keys.
     * @param headsetState headset event (1 - connected, 0 - removed)
     * @param keyEvent key event (1 - pressed, 0 - released)
     * @param keyCode key code
     * @note Method returns `HeadsetState::Changed` if state of headset changed (connected -> removed)
     */
    HeadsetState headset_get_data(bool &headsetState, uint8_t &keyEvent, uint8_t &keyCode);

    bool IsInserted();

    /**
     * @brief This method is responsible for deinitializing the headset controller.
     * @param qHandle key code
     */
    int32_t Deinit();

    BaseType_t headset_IRQHandler();

A module-bsp/bsp/headset/headset_key_codes.hpp => module-bsp/bsp/headset/headset_key_codes.hpp +31 -0
@@ 0,0 1,31 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <cstdint>

namespace bsp {
 namespace headset {

    enum class HeadsetState : bool
    {
        NoChange = false,
        Changed
    };

    enum class KeyCode : uint8_t
    {
        Error = 0,
        Key1,
        Key2,
        Key3,
        Key4
    };

    enum class KeyEvent : uint8_t
    {
        KeyReleased = 0,
        KeyPressed,
        Error
    };
 }
}

M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +28 -4
@@ 76,14 76,20 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
        if (!queue->Dequeue(&notification, 0)) {
            return false;
        }
        bool headsetState = false;
        uint8_t headsetKeyEvent, headsetKeyCode;

        if (bsp::headset::headset_get_data(headsetState, headsetKeyEvent, headsetKeyCode) ==
            bsp::headset::HeadsetState::Changed) {

        if (bsp::headset::Handler(notification) == true) {
            bool state   = bsp::headset::IsInserted();
            auto message = std::make_shared<AudioEventRequest>(audio::EventType::JackState,
                                                               state ? audio::Event::DeviceState::Connected
                                                                     : audio::Event::DeviceState::Disconnected);
                                                               headsetState ? audio::Event::DeviceState::Connected
                                                                            : audio::Event::DeviceState::Disconnected);
            service->bus.sendUnicast(message, service::name::evt_manager);
        }
        else if (auto keyCode = headsetKeyToKeyboardKey(headsetKeyCode); keyCode != bsp::KeyCodes::Undefined) {
            processKeyEvent(static_cast<bsp::KeyEvents>(headsetKeyEvent), keyCode);
        }
    }

    if (queueID == static_cast<std::uint32_t>(WorkerEventQueues::queueBattery)) {


@@ 300,3 306,21 @@ void WorkerEvent::processKeyEvent(bsp::KeyEvents event, bsp::KeyCodes code)
    }
    service->bus.sendUnicast(message, service::name::evt_manager);
}

bsp::KeyCodes WorkerEvent::headsetKeyToKeyboardKey(uint8_t headsetKeyCode)
{
    switch (headsetKeyCode) {
    case static_cast<uint8_t>(bsp::headset::KeyCode::Key1):
        return bsp::KeyCodes::JoystickEnter;

    case static_cast<uint8_t>(bsp::headset::KeyCode::Key2):
        return bsp::KeyCodes::Undefined;

    case static_cast<uint8_t>(bsp::headset::KeyCode::Key3):
        return bsp::KeyCodes::VolUp;

    case static_cast<uint8_t>(bsp::headset::KeyCode::Key4):
        return bsp::KeyCodes::VolDown;
    }
    return bsp::KeyCodes::Undefined;
}

M module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp => module-services/service-evtmgr/service-evtmgr/WorkerEvent.hpp +6 -0
@@ 56,6 56,12 @@ class WorkerEvent : public sys::Worker
     */
    void processKeyEvent(bsp::KeyEvents event, bsp::KeyCodes code);

    /**
     * @brief This method is responsible for translating a headset key to keyboard key code.
     * @param code key code
     */
    bsp::KeyCodes headsetKeyToKeyboardKey(uint8_t code);

    void updateResourcesAfterCpuFrequencyChange(bsp::CpuFrequencyHz newFrequency);
    /**
     * @brief list of keys with long press enabled. First item is key code, second is long press time.