耳机系统综述

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

Android耳机系统综述本文为本人随笔所写,欢迎转载。由于个人的见识和能力有限,不能面面俱到,也可能存在谬误,敬请各位指出,本人的邮箱是hyouyan@126.com,博客是中其实并没有耳机系统这个称呼,只是我为了方便解释而加的。在整个android系统中,跟耳机相关的部分有:¾Linux驱动:主要完成耳机的插入的检测,hook键的检测,其中hook键包括长按和短按。¾在frameworks中的耳机的观察的文件(HeadsetObserver.java),这个文件主要是检测耳机是否插入和名字,并把相关的内容通过Intent广播出去。¾跟音频相关,改变音频输出的路径(这边涉及到播放音乐和电话部分)。¾跟事件的处理相关,这部分主要体现hook的功能,主要是接听电话,挂断电话等。事件的处理又分为linux的事件处理和android上的事件处理。我将分块叙述,由于各种原因,我在这不便把源代码公布,如果你需要的我的帮助,可以发邮件给我,也可以在我blog上留言,谢谢!Linux驱动首先要定义一个switch_dev(structswitch_devsdev;)并把它初始化,如(sdev.name=……);然后注册一个switchdevice:ret=switch_dev_register(&switch_data-sdev);if(ret0){gotoerr_switch_dev_register;}switch_dev_register这个函数在switch_class.c中实现intswitch_dev_register(structswitch_dev*sdev){intret;if(!switch_class){ret=create_switch_class();if(ret0)returnret;}sdev-index=atomic_inc_return(&device_count);sdev-dev=device_create(switch_class,NULL,MKDEV(0,sdev-index),NULL,sdev-name);if(IS_ERR(sdev-dev))returnPTR_ERR(sdev-dev);ret=device_create_file(sdev-dev,&dev_attr_state);if(ret0)gotoerr_create_file_1;ret=device_create_file(sdev-dev,&dev_attr_name);if(ret0)gotoerr_create_file_2;dev_set_drvdata(sdev-dev,sdev);sdev-state=0;return0;err_create_file_2:device_remove_file(sdev-dev,&dev_attr_state);err_create_file_1:device_destroy(switch_class,MKDEV(0,sdev-index));printk(KERN_ERRswitch:Failedtoregisterdriver%s\n,sdev-name);returnret;}这个函数中主要是以下几个函数zcreate_switch_class()zdevice_create(switch_class,NULL,MKDEV(0,sdev-index),NULL,sdev-name);zdevice_create_file(sdev-dev,&dev_attr_state);zdevice_create_file(sdev-dev,&dev_attr_name);经过以上函数后将会生成路径和被用户空间访问的节点/sys/class/switch/h2w/name;/sys/class/switch/h2w/state;这两个供用户空间访问在这个函数中要注意到staticDEVICE_ATTR(state,S_IRUGO|S_IWUSR,state_show,NULL);staticDEVICE_ATTR(name,S_IRUGO|S_IWUSR,name_show,NULL);这两项中用于设置节点state和name的属性DEVICE_ATTR有四个参数,分别为名称、权限位、读函数、写函数有此可以知道state和name,虽然有读写权限,但都只有读函数,没有写函数。其中state对headsetobserver.java区分有无mic和耳机是否插入起作用staticssize_tstate_show(structdevice*dev,structdevice_attribute*attr,char*buf){structswitch_dev*sdev=(structswitch_dev*)dev_get_drvdata(dev);if(sdev-print_state){//如果用户有定义print_state函数,将调用用户定义的intret=sdev-print_state(sdev,buf);if(ret=0)returnret;}returnsprintf(buf,%d\n,sdev-state);//把sdev-state以%d的格式装如buf中}在这个函数得注意:如果你想你的frameworks能区别出有没有mic,并且你用的是switch_gpio.c这个文件的话,你需要把switch_gpio.c中的sdev-print_state的定义去掉。我就在这卡了半天的时间。State原先出来一直是1,后来才发现原来是自己定义了sdev-print_state并只返回0和1,没有其他值。现重新回到driver,接下来时input子系统的内容input_allocate_device();分配内存给新的输入设备接下去初始化input_dev这个结构体,给输入设备命名dev-name,设置input支持的键值input_set_capability,如:input_set_capability(ipdev,EV_KEY,KEY_MEDIA);input_set_capability(ipdev,EV_SW,SW_HEADPHONE_INSERT);input_set_capability(ipdev,EV_KEY,KEY_END);注册input设备input_register_device(ipdev);在驱动中还涉及到工作队列等问题,就请各位自己去看一下吧。接下来是对于中断的处理,这个中断方式我是从HTC的驱动中学的,有点巧妙,想到了叶就不算巧妙了,呵呵。先申请为高电平中断,我的板子是插入耳机检测脚我高电平,在进入中断后再申请为低电平中断,这个相对于上升和下降有个好处——当设置为上升或下降沿触发中断时,开机之前插入耳机,当开机后,将识别不到耳机。而当设置为电平触发可以解决这个问题。我的观点是在耳机在插槽内时,检测引脚直接被拉倒插入耳机稳定后的电平,而不会产生上升和下降沿。中断申请的代码如下:request_irq(gpio_to_irq(18),gpio_irq_handler,IRQF_TRIGGER_HIGH,pdev-name,switch_data);中断处理的代码如下:set_irq_type(gpio_to_irq(18),gpio_get_value(18)?IRQF_TRIGGER_LOW:IRQF_TRIGGER_HIGH);由上可以看到C语言的问号表达式的好处了吧,呵呵。C语言博大精深!还有很多精髓的问题,以后用了,慢慢体会,如果你觉得你的C非常好了,呵呵,找一个C语言的笔试题来做做,哈哈,你真会发现又学到一堆的东西。呵呵。继续我们的驱动。接下来是有无mic的判断和设置state的值了,有HeadsetObserver.java这个文件中可以得出state的值:有mic:state等于1没有mic:state等于2扯点题外,我原先以为在/sys/class/switch/h2w/state;下的state只有0和1,我再问了我的一些同事,他们也跟我说是bool类型。但我看到headsetobserver.java中又有1和2,后面觉得有点可疑。再看源代码之前,真的不想看源代码,看了源代码后,发现源代码真好。哈哈。通过一步步跟,后面发现时可以大于1的,呵呵。这个将要用到switch_get_state(&data-sdev)这个函数,它也是在switch_class.c中实现的。voidswitch_set_state(structswitch_dev*sdev,intstate){charname_buf[120];charstate_buf[120];char*prop_buf;char*envp[3];intenv_offset=0;intlength;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);//给HeadsetObserver.java读取名字if(length0){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);//给HeadsetObserver.java读取读取状态,这个函数我们在前面分析过了,这个函数比较重要,关系到区分有无mic。if(length0){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((unsignedlong)prop_buf);}else{printk(KERN_ERRoutofmemoryinswitch_set_state\n);kobject_uevent(&sdev-dev-kobj,KOBJ_CHANGE);}}}EXPORT_SYMBOL_GPL(switch_set_state);//供外部所使用。由于hook键和检测mic的有关联,故如果有mic则要申请hook的中断。具体mic的检测可以参考我的blog中转载别人的的一篇文章,链接地址如下接下来是HOOK键功能的处理了,在google论坛里有些说实现hook键接听和挂断电话的问题。Hook键只有一个,要实现两个功能就得要用时间来区分了,短按:代表接听。长按:代表拒接。这样两种功能就实现了,呵呵。对于长短的检测最好用纳秒,用秒的准确性比较低。存在误判性比较高,可以利用把时间转换成纳秒来计算,我用如下实现检测时间的长短:do_gettimeofday(&time);timens=timeval_to_ns(&time);while(gpio_get_value(123)==0){};do_gettimeofday(&time);

1 / 23
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功