• linux kernel switch driver(android headset device detection)


    总结下linux kernel switch driver。

    这里的switch driver是为监听CPU gpio口状态变化的,switch可以理解为gpio口状态变化。

    switch driver是因android引入的。

    总结地说,就是在switch driver中创建kernel thread周期性地检测指定的gpio口的状态,如果检测到gpio口状态变化了,就发uevent;android native层的input flinger会去读这个event,读到后往android java层notify,notify给InputManagerService/WiredAccessoryManager,WiredAccessoryManager在处理这个msg,比如当收到的msg表示有耳机接入时,会将外放mute掉。

    下面以耳机接入为例具体说下androd系统中耳机接入检测的过程

    kernel/arch/arm64/boot/dts/xxx.dts

    switch_gpio {
            compatible = "mstar,switch-gpio";
            switch-name = "h2w";
            switch-gpio = <66>;
            switch-inverse = <0>;
        };

    上面的switch-gpio是想要监听的CPU gpio index,用来告诉switch driver要监听的gpio口

    解析上述dts switch_gpio section是在kernel switch driver中:

    drivers/switch/switch_gpio.c

    static int gpio_switch_probe(struct platform_device *pdev)
    {
        struct gpio_switch_platform_data *pdata = pdev->dev.platform_data;
        struct gpio_switch_data *switch_data;
        int ret = 0;
    
        if (!pdata)
            return -EBUSY;
    
        switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL);
        if (!switch_data)
            return -ENOMEM;
    
        switch_data->sdev.name = pdata->name;
        switch_data->gpio = pdata->gpio;
        switch_data->name_on = pdata->name_on;
        switch_data->name_off = pdata->name_off;
        switch_data->state_on = pdata->state_on;
        switch_data->state_off = pdata->state_off;
        switch_data->sdev.print_state = switch_gpio_print_state;
    
        ret = switch_dev_register(&switch_data->sdev);

    在kernel switch driver中会创建switch class,对应路径为/sys/class/switch。还会根据dts文件中指定的name创建device,例如上面dts文件中的h2w,所以对应路径是/sys/class/switch/h2w。

    在switch driver中,会创建kernel thread来定期检测指定的gpio的状态,然后判断gpio口状态是否改变,如果发现gpio口状态变化,会发uevent:

    drivers/switch/switch_class.c

    void switch_set_state(struct switch_dev *sdev, int state)
    {
        char name_buf[120];
        char state_buf[120];
        char *prop_buf;
        char *envp[3];
        int env_offset = 0;
        int length;
    
        if (sdev->state != state) {
            sdev->state = state;
    
            prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
            if (prop_buf) {
                length = name_show(sdev->dev, NULL, prop_buf);
                if (length > 0) {
                    if (prop_buf[length - 1] == '
    ')
                        prop_buf[length - 1] = 0;
                    snprintf(name_buf, sizeof(name_buf),
                        "SWITCH_NAME=%s", prop_buf);
                    envp[env_offset++] = name_buf;
                }
                length = state_show(sdev->dev, NULL, prop_buf);
                if (length > 0) {
                    if (prop_buf[length - 1] == '
    ')
                        prop_buf[length - 1] = 0;
                    snprintf(state_buf, sizeof(state_buf),
                        "SWITCH_STATE=%s", prop_buf);
                    envp[env_offset++] = state_buf;
                }
                envp[env_offset] = NULL;
                kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
                free_page((unsigned long)prop_buf);
            } else {
                printk(KERN_ERR "out of memory in switch_set_state
    ");
                kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
            }
        }
    }
    EXPORT_SYMBOL_GPL(switch_set_state);

    在switch driver中也可以选择注册gpio的kernel irq来实现对指定gpio口状态变化的监听

    在switch driver中发了uevent后,上层会来读这个event,就是由android中的input flinger来读:

    frameworks/native/services/inputflinger/InputReader.cpp

    void InputReader::loopOnce() {
        int32_t oldGeneration;
        int32_t timeoutMillis;
        bool inputDevicesChanged = false;
        Vector<InputDeviceInfo> inputDevices;
        { // acquire lock
            AutoMutex _l(mLock);
    
            oldGeneration = mGeneration;
            timeoutMillis = -1;
    
            uint32_t changes = mConfigurationChangesToRefresh;
            if (changes) {
                mConfigurationChangesToRefresh = 0;
                timeoutMillis = 0;
                refreshConfigurationLocked(changes);
            } else if (mNextTimeout != LLONG_MAX) {
                nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
                timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
            }
        } // release lock
    
        size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    
        { // acquire lock
            AutoMutex _l(mLock);
            mReaderIsAliveCondition.broadcast();
    
            if (count) {
                processEventsLocked(mEventBuffer, count);
            }  

    上面的getEvents后,接下来就是处理这个event了,调用processEventsLocked()这个函数会call到SwitchInputMapper的procesSwitch/sync函数,sync函数是将这个event插入event queue

    void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
        if (switchCode >= 0 && switchCode < 32) {
            if (switchValue) {
                mSwitchValues |= 1 << switchCode;
            } else {
                mSwitchValues &= ~(1 << switchCode);
            }
            mUpdatedSwitchMask |= 1 << switchCode;
        }
    }
    
    void SwitchInputMapper::sync(nsecs_t when) {
        if (mUpdatedSwitchMask) {
            uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
            NotifySwitchArgs args(when, 0, updatedSwitchValues, mUpdatedSwitchMask);
            getListener()->notifySwitch(&args);
    
            mUpdatedSwitchMask = 0;
        }
    }

    接下来是处理event queue中的event了,如下的flush()函数:

    frameworks/native/services/inputflinger/InputListener.cpp

    void QueuedInputListener::flush() {
        size_t count = mArgsQueue.size();
        for (size_t i = 0; i < count; i++) {
            NotifyArgs* args = mArgsQueue[i];
            args->notify(mInnerListener);
            delete args;
        }
        mArgsQueue.clear();
    }

    对于switch而言,就是call如下的notify函数:

    void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
        listener->notifySwitch(this);
    }

    上述的listener是InputDispatcher,所以会call到InputDispatcher::notifySwitch()

    frameworks/native/services/inputflinger/InputDispatcher.cpp

    void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
    #if DEBUG_INBOUND_EVENT_DETAILS
        ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
                "switchMask=0x%08x",
                args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
    #endif
    
        uint32_t policyFlags = args->policyFlags;
        policyFlags |= POLICY_FLAG_TRUSTED;
        mPolicy->notifySwitch(args->eventTime,
                args->switchValues, args->switchMask, policyFlags);
    }

    上面的notifySwitch()是NativeInputManager::notifySwitch()

    frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

    void NativeInputManager::notifySwitch(nsecs_t when,
            uint32_t switchValues, uint32_t switchMask, uint32_t /* policyFlags */) {
    #if DEBUG_INPUT_DISPATCHER_POLICY
        ALOGD("notifySwitch - when=%lld, switchValues=0x%08x, switchMask=0x%08x, policyFlags=0x%x",
                when, switchValues, switchMask, policyFlags);
    #endif
        ATRACE_CALL();
    
        JNIEnv* env = jniEnv();
    
        env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifySwitch,
                when, switchValues, switchMask);
        checkAndClearExceptionFromCallback(env, "notifySwitch");
    }

    上面的CallVoidMethod调用就是native层call java层的函数,call的对应的java层的函数是InputManagerService类中的方法

    frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

        // Native callback.
        private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
            if (DEBUG) {
                Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
                        + ", mask=" + Integer.toHexString(switchMask));
            }
    
            if ((switchMask & SW_LID_BIT) != 0) {
                final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
                mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
            }
    
            if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
                final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
                mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
            }
    
            if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
                mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
                        switchMask);
            }

    对于此次的headset问题,就是走的SW_JACK_BITS case,所以是call mWiredAccessoryCallbacks.notifyWiredAccessoryChanged,notifyWiredAccessoryChanged对应是WiredAccessoryManager中的方法:

    frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java

        public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) {
            if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos
                    + " bits=" + switchCodeToString(switchValues, switchMask)
                    + " mask=" + Integer.toHexString(switchMask));
    
            synchronized (mLock) {
                int headset;
                mSwitchValues = (mSwitchValues & ~switchMask) | switchValues;
                switch (mSwitchValues &
                    (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) {
                    case 0:
                        headset = 0;
                        break;
    
                    case SW_HEADPHONE_INSERT_BIT:
                        headset = BIT_HEADSET_NO_MIC;
                        break;
    
                    case SW_LINEOUT_INSERT_BIT:
                        headset = BIT_LINEOUT;
                        break;
    
                    case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT:
                        headset = BIT_HEADSET;
                        break;
    
                    case SW_MICROPHONE_INSERT_BIT:
                        headset = BIT_HEADSET;
                        break;
    
                    default:
                        headset = 0;
                        break;
                }
    
                updateLocked(NAME_H2W,
                    (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
            }
        }
        private void updateLocked(String newName, int newState) {
            // Retain only relevant bits
            int headsetState = newState & SUPPORTED_HEADSETS;
            int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
            int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
            int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT);
            boolean h2wStateChange = true;
            boolean usbStateChange = true;
            if (LOG) Slog.v(TAG, "newName=" + newName
                    + " newState=" + newState
                    + " headsetState=" + headsetState
                    + " prev headsetState=" + mHeadsetState);

    所以,如果是检测到有耳机设备接入,会有如下的log。如果headsetState为2,表示有不带mic的耳机接入了:

    ??[15-51-04.505]11-30 02:14:21.985  3262  3262 V WiredAccessoryManager: newName=h2w newState=2 headsetState=2 prev headsetState=0
        private static final int BIT_HEADSET = (1 << 0);
        private static final int BIT_HEADSET_NO_MIC = (1 << 1);
        private static final int BIT_USB_HEADSET_ANLG = (1 << 2);
        private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
        private static final int BIT_HDMI_AUDIO = (1 << 4);
        private static final int BIT_LINEOUT = (1 << 5);
        private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
                                                       BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL|
                                                       BIT_HDMI_AUDIO|BIT_LINEOUT);

    可以执行如下的命令查看系统中目前接入的耳机类设备,例如cat到的值为2表示当前系统有接上不带mic的耳机:

     cat /sys/class/switch/h2w/state

    ??[15-51-04.505]11-30 02:14:21.985  3262  3262 V WiredAccessoryManager: newName=h2w newState=2 headsetState=2 prev headsetState=0

  • 相关阅读:
    PyQt5--基础篇:用eric6工具实现三级联动效果
    Django--基本篇:项目结构与设计模式(MVC)
    python文件最基础的读写删除
    Begin using git (Part1)
    Begin using git
    通用刷新数据源代码
    Combox程序中自定义数据源
    表内父子关系的递归查询
    C++ 获取数组长度
    Linux在终端里面切换root用户
  • 原文地址:https://www.cnblogs.com/aspirs/p/11963873.html
Copyright © 2020-2023  润新知