zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    ERROR Function not available to this responsibility.Change responsibilities or contact your System Administrator.
    After Upgrade To Release 12.1.3 Users Receive "Function Not Available To This Responsibility" Error While Selecting Sub Menus Under Diagnostics (Doc ID 1200743.1)
    产品设计中先熟练使用铅笔 不要依赖Axure
    12.1.2: How to Modify and Enable The Configurable Home Page Delivered Via 12.1.2 (Doc ID 1061482.1)
    Reverting back to the R12.1.1 and R12.1.3 Homepage Layout
    常见Linux版本
    网口扫盲二:Mac与Phy组成原理的简单分析
    VMware 8安装苹果操作系统Mac OS X 10.7 Lion正式版
    VMware8安装MacOS 10.8
    回顾苹果操作系统Mac OS的发展历史
  • 原文地址:https://www.cnblogs.com/aspirs/p/11963873.html
Copyright © 2011-2022 走看看