linux kernel switch driver(android headset device detection)

总结下linux kernel switch driver。

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

switch driver是因android引入的。

下面以耳机接入为例说下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] == '\n')
                    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] == '\n')
                    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\n");
            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 /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

猜你喜欢

转载自www.cnblogs.com/aspirs/p/11963873.html