总结下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);
可以执行如下的命令查看系统中目前接入的耳机类设备:
<style></style>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