總結下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] == '\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到的值為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