总结下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

上述的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

对于此次的headset问题,就是走的SW_JACK_BITS case,所以是call mWiredAccessoryCallbacks.notifyWiredAccessoryChanged,notifyWiredAccessoryChanged对应是WiredAccessoryManager中的方法:

frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java
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