linux kernel switch driver(android headset device detection)

 

 

Summarize linux kernel switch driver.

The switch driver here is to listen CPU gpio port state changes, switch can be understood as gpio port state changes.

switch driver is due to the introduction of android.

 

Case below to access said next process headset earphone detection androd access system

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

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

 

The above is a switch-gpio CPU gpio index want to listen, to tell the switch driver to listen on port gpio

 

Analysis of said dts switch_gpio section is in the 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);

Switch class is created in the kernel switch driver, the corresponding path for the / sys / class / switch. The dts also creates the specified file name device, such as the above file dts h2w, so that the corresponding path is / sys / class / switch / h2w.

In the switch driver, the kernel thread is created to periodically detect the state gpio specified, and then determine whether gpio port state changes, if gpio port state changes found, will be issued 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);

 

 

In the switch driver can also choose to register kernel irq gpio to achieve changes in the specified port state monitor gpio

 

After the switch driver made a uevent, the upper layer will be reading this event, it is to be read by the android in the 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);
        }  

After the above getEvents, the next step is to deal with this event, call processEventsLocked () This function call to SwitchInputMapper of procesSwitch / sync function, sync function is inserted into the event queue of this event

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;
    }
}

 

The next event is a process event in the Queue, following flush () function:

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();
}

 

For the switch, the call is to notify the following functions:

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

 

Above the listener is InputDispatcher, it will call to 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);
}

 

The above notifySwitch () is 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");
}

 

 

The above is the function calls native layer CallVoidMethod Call java layer, call the function corresponding to the layer java class method 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);
        }

 

The problem for the headset, is to take the SW_JACK_BITS case, it is a call mWiredAccessoryCallbacks.notifyWiredAccessoryChanged, notifyWiredAccessoryChanged the corresponding method WiredAccessoryManager in:

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);

 

So, if the device detects that the headphone access, will yield the following log. If headsetState is 2, it represents an earphone without access to the 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);

 

 

Type earphone device may perform the following command to view current system access:

 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

 

Guess you like

Origin www.cnblogs.com/aspirs/p/11963873.html