Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92
Rockchip针对有线耳机的驱动单独建了目录, 在kernel/drivers/headset_observe/下
耳机插拔事件通过switch子系统上报,目录: kernel/drivers/switch/
通过其switch_dev_register()接口把headset注册到switch子系统中
headset->sdev.name = “h2w”;
headset->sdev.print_name = h2w_print_name;
ret = switch_dev_register(&headset->sdev);
这样就能在/sys下看到h2w的子目录
然后,用户空间就能收到uevent事件得知耳机设备状态有变化,从state文件读取状态得知插入还是拔出来做相应处理。
插拔状态的改变是通过switch_set_state()来实现,本质上就是通知上层并改变state文件值。
dts的配置如下
&adc {
status = "okay";
rockchip_headset {
compatible = "rockchip_headset";
headset_gpio = <&gpio7 GPIO_A7 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&gpio7_a7>;
io-channels = <&adc 2>;
/*
hook_gpio = ;
hook_down_type = ; //interrupt hook key down status
*/
};
};
耳机插拔中断检测在headset_interrupt()@rk_headset_irq_hook_adc.c
static irqreturn_t headset_interrupt(int irq, void *dev_id)
{
//休眠等待电平稳定
msleep(150);
if(headset_info->headset_status == HEADSET_IN)
{
if(pdata->chan != 0)
{
//detect Hook key
//驱动把耳机状态和hook key的检测都放同一个流程中处理了。
schedule_delayed_work(&headset_info->h_delayed_work[HOOK],msecs_to_jiffies(200));
}
}else if(headset_info->headset_status == HEADSET_OUT)
{
......
}
//触发方式从电平触发改成边沿触发,这样,hook key的中断不会影响耳机的插拔电平
//而且耳机插拔也可以被检测到
if(pdata->headset_insert_type == HEADSET_IN_HIGH)
irq_set_irq_type(headset_info->irq[HEADSET],IRQF_TRIGGER_RISING);
else
irq_set_irq_type(headset_info->irq[HEADSET],IRQF_TRIGGER_FALLING);
return IRQ_HANDLED;
}
下半部对应的work queue是hook_once_work()
static void hook_once_work(struct work_struct *work)
{
//通知audio codec预先处理mic
#if defined (CONFIG_SND_SOC_RT3261) || defined (CONFIG_SND_SOC_RT3224)
rt3261_headset_mic_detect(true);
#endif
//adc读取
ret = iio_read_channel_raw(headset_info->chan, &val);
if (ret < 0) {
pr_err("read hook_once_work adc channel() error: %d\n", ret);
}
//没有mic的情况
if(val >= 0 && val < HOOK_LEVEL_LOW)
{
headset_info->isMic= 0;//No microphone
#if defined (CONFIG_SND_SOC_RT3261) || defined (CONFIG_SND_SOC_RT3224)
rt3261_headset_mic_detect(false);
#endif
}
//有mic的情况
else if(val >= HOOK_LEVEL_HIGH)
{
headset_info->isMic = 1;//have mic
//不同按键的电阻值不同,那么读到的电压也不同,hook_work_callback()
//处理识别(adc 读取)。
schedule_delayed_work(&headset_info->hook_work,msecs_to_jiffies(100));
}
//根据是否有mic来得知是headset还是headphone.
headset_info->cur_headset_status = headset_info->isMic ? BIT_HEADSET:BIT_HEADSET_NO_MIC;
//通知上层设备状态变化并改变state文件值
switch_set_state(&headset_info->sdev, headset_info->cur_headset_status);
}
hook key 的上报通过input 子系统实现
注册:
int rk_headset_adc_probe(struct platform_device *pdev,struct rk_headset_pdata *pdata)
{
// Create and register the input driver.
headset->input_dev = input_allocate_device();
if (!headset->input_dev) {
dev_err(&pdev->dev, "failed to allocate input device\n");
ret = -ENOMEM;
goto failed_free;
}
headset->input_dev->name = pdev->name;
headset->input_dev->open = rk_hskey_open;
headset->input_dev->close = rk_hskey_close;
headset->input_dev->dev.parent = &pdev->dev;
//input_dev->phys = KEY_PHYS_NAME;
headset->input_dev->id.vendor = 0x0001;
headset->input_dev->id.product = 0x0001;
headset->input_dev->id.version = 0x0100;
// Register the input device
ret = input_register_device(headset->input_dev);
if (ret) {
dev_err(&pdev->dev, "failed to register input device\n");
goto failed_free_dev;
}
input_set_capability(headset->input_dev, EV_KEY, HOOK_KEY_CODE);
}
上报:
static void hook_work_callback(struct work_struct *work)
{
input_report_key(headset->input_dev,HOOK_KEY_CODE,headset->hook_status);
input_sync(headset->input_dev);
}
参考:
Android耳机监测以及耳机按键监测
Android下耳机HOOK键功能开发
Android 耳机驱动知识