Bp部分主要是注册中断事件,检测到中断后上报key_code给ap,通过rpc机制传递给ap端。
Ap部分驱动层注册rpc客户端,对事件进行处理,uevent上报相应的事件类型,应用层开机启动wiredaccessoryobserver服务,侦听到耳机事件后发送setdeviceconnectionstate到策略层,选择输出设备,驱动层根据设备切换到对应的音频通路,音频通路再通过acdb文件调用bp对应的通路的codec和snd,完成了通路的切换。
/*===========================bp部分=====================================*/
Hs_gpio.c
耳机检测分析,插入耳机检测通过中断机制判断的,
1.系统启动时mp侧先调用hsi_gpio_init函数中 hsi_gpio_process_isr_event(0, (timer_cb_data_type)gpio_id) 处理gpio_id中断事件。
在hsi_gpio_process_isr_event函数中设置中断
/* Set interrupt to fire when GPIO flips again */
Gpio状态翻转时设置中断激活
hs_tramp_gpio_register_isr((uint32)gpio_data->gpio_num, trigger,
hsi_gpio_detect_isr, (uint32)gpio_id);
2.gpio正常情况下不会发生变化,插入耳机后将会因电平变化有中断产生,因此开机后从his_gpio_detect_isr开始执行
void hsi_gpio_detect_isr(uint32 gpio_id)
{
boolean is_intrpt_enabled;
hsi_gpio_data_type *gpio_data;
gpio_data = (hsi_gpio_data + gpio_id);
/* Read the GPIO. If it is low set the interrupt to be level trigerred for
* high level. If it is high, set the interrupt for level trigerred for low
* level.
* Active value for the GPIO is determined from configuration data
*/
if (gpio_id < HS_GPIO_LAST)
{
is_intrpt_enabled = hs_tramp_gpio_is_interrupt_enabled(gpio_data->gpio_num);
if (TRUE == is_intrpt_enabled)
{
hs_tramp_gpio_deregister_isr((uint32)gpio_data->gpio_num,
hsi_gpio_detect_isr);
}
if(INVALID_GPIO_DEBOUNCE_PERIOD != gpio_data->timer_delay &&
TRUE == gpio_data->timer_initialzed)
{
HS_MSG_HIGH("if xx%s: hs_report_event %d", __FUNCTION__, gpio_id, 0);
/* Start the timer to debounce GPIO level change on GPIO */
/*设置定时器,消除GPIO电平变化的抖动*/
timer_reg( &(gpio_data->gpio_timer),
(timer_t2_cb_type)hsi_gpio_process_isr_event,
(timer_cb_data_type)gpio_id, gpio_data->timer_delay, 0
);
}
关于1调用情况分析结构体:
hs_poll_src_type hsi_gpio_handle =
{
hsi_gpio_init,
hsi_gpio_poll,
hsi_gpio_close,
};
hs_poll_table_type hsi_poll_list[HS_EVNT_LAST] =
{
{NULL, HS_GPIO_EXT_PWR, 0}, /* External Power */
{NULL, HS_GPIO_HSD, 0}, /* Headset */
{NULL, HS_GPIO_HSTD, 0}, /* Headset Type */
{NULL, HS_GPIO_NONE, 0}, /* Headset Switch */
{NULL, HS_GPIO_NONE, 0}, /* Keypad */
{&hsi_pwr_key_handle, HS_GPIO_NONE, 0}, /* PWR/END */
{NULL, HS_GPIO_FLIP, 0}, /* Flip state */
{NULL, HS_GPIO_CHARGER, 0}, /* Charger */
{NULL, HS_GPIO_NONE, 0}, /* Runtime env */
{NULL, HS_GPIO_NONE, 0}, /* Remote evts */
{NULL, HS_GPIO_NONE, 0}, /* Diag events */
{&hsi_gpio_handle, HS_GPIO_NONE, 0}, /* Accessory evts */
{NULL, HS_GPIO_NONE, 0}, /* headphone evts */
{NULL, HS_GPIO_NONE, 0}, /* mic detect evts*/
{&hsi_acc_type_drv_handle, HS_GPIO_ACCESSORY, 0}, /* acc type detect evts*/
上述结构体表明
hsi_poll_list[HS_EVNT_ACCESSORY_DETECT].handle触发hsi_gpio_handle,检测插入中断事件
hsi_poll_list[HS_EVNT_ACC_TYPE_DETECT].handle触发hsi_acc_type_drv_handle,区分耳机
void hs_task -》
static void hs_init( void ) -》
hsi_init_plugins();
hsi_init_plugins()函数中
for (i = (hs_event_type)0; i < HS_EVNT_LAST; ++i)
{
HSI_EVNT_DRIVER(i) = (hs_poll_src_type *)NULL;
HSI_EVNT_POLL_STATUS(i) = HS_POLL_SRC_INACTIVE;
HSI_EVNT_LAST_STATE(i) = HS_SRC_STATE_UNKWN;
if (NULL != hsi_poll_list[i].handle)
{
if (NULL != hsi_poll_list[i].handle->init)
{
result = (hsi_poll_list[i].handle->init)(&hsi_cfg_data, (void *)NULL);
}
if ((HS_SUCCESS == result) && (NULL != hsi_poll_list[i].handle->poll))
{
HSI_EVNT_DRIVER(i) = hsi_poll_list[i].handle;
/* Check if the poll source uses interrupts. If it does set it up as
* ACTIVE_INTERRUPT so that it does not get polled.
*/
HS_MSG_HIGH("%s: i=%d if(0==%d)", __FUNCTION__, i, hsi_poll_list[i].poll_interval);
if (0 == hsi_poll_list[i].poll_interval)
{
HSI_EVNT_POLL_STATUS(i) = HS_POLL_SRC_ACTIVE_INTERRUPT;
}
else
{
HSI_EVNT_POLL_STATUS(i) = HS_POLL_SRC_ACTIVE;
++hsi_poll_plugins_num;
}
}
}
else
{
HSI_EVNT_DRIVER(i) = (hs_poll_src_type *)NULL;
HSI_EVNT_POLL_STATUS(i) = HS_POLL_SRC_INACTIVE;
}
hsi_poll_data[i].gpio = hsi_poll_list[i].gpio_id;
hsi_set_poll_data_skip_cnt(i);
}
根据 @file hs_bsp_v.c设置结构体情况设置hsi_poll_list[i].handle,如果poll_interval为0的时候,就将poll状态设为中断,i为11时result = (hsi_poll_list[i].handle->init)(&hsi_cfg_data, (void *)NULL);会调用hsi_gpio_init函数
hs_return_type hsi_gpio_init(const hs_config_data_type *cfg, void *handle)
/*===========================ap部分=====================================*/
耳机驱动文件
1.Rpc_server_handset.c
从probe文件看看该文件的作用,定义了全局的变量
struct msm_handset {
struct input_dev *ipdev;
struct switch_dev sdev;开关设备
struct msm_handset_platform_data *hs_pdata;
bool mic_on, hs_on;
};
static struct msm_rpc_client *rpc_client;通过rpc机制获取bp事件
static struct msm_handset *hs;
1.1注册开关设备
hs->sdev.name = "h2w";
hs->sdev.print_name = msm_headset_print_name;
rc = switch_dev_register(&hs->sdev);
通过switch_dev_register就会生成目录:/sys/class/switch/h2w
及其下面的"/sys/class/switch/h2w/name";和"/sys/class/switch/h2w/state";文件。对了,接口就是这两个文件
由驱动通过update_stateà switch_set_state将rpc获取的耳机名称和状态,写入name和state
应用程序对耳机进行名称和状态的判断就靠读出这两个文件得到。
1.2注册input设备,
ipdev = input_allocate_device();
input_set_drvdata(ipdev, hs);
hs->ipdev = ipdev;
if (pdev->dev.platform_data)
hs->hs_pdata = pdev->dev.platform_data;
ipdev->id.vendor = 0x0001;
ipdev->id.product = 1;
ipdev->id.version = 1;
input_set_capability(ipdev, EV_KEY, KEY_MEDIA);
input_set_capability(ipdev, EV_KEY, KEY_VOLUMEUP);
input_set_capability(ipdev, EV_KEY, KEY_VOLUMEDOWN);
input_set_capability(ipdev, EV_SW, SW_HEADPHONE_INSERT);
input_set_capability(ipdev, EV_SW, SW_MICROPHONE_INSERT);
input_set_capability(ipdev, EV_KEY, KEY_POWER);
input_set_capability(ipdev, EV_KEY, KEY_END);
set_bit(INPUT_PROP_NO_FAKE_RELEASE, ipdev->propbit);
rc = input_register_device(ipdev);
1.3 初始化rpc客户端,接收bp上报的事件
rc = hs_rpc_init();
à rc = msm_rpc_create_server(&hs_rpc_server);
à static struct msm_rpc_server hs_rpc_server = {
.prog = HS_SERVER_PROG,
.vers = HS_SERVER_VERS,
.rpc_call = handle_hs_rpc_call,
};
à report_hs_key(args->key_code, args->key_parm);
1.4 report_hs_key函数上报key事件
//根据key_code是松开(0xff)还是其他确定hs_find_key()的参数是param还是code
if (key_code == HS_REL_K)
key = hs_find_key(key_parm);
else
key = hs_find_key(key_code);
static int hs_find_key(uint32_t hscode)
{
int i, key;
//hscode保存在高8位,0保存在低24位
key = KEY(hscode, 0);
//循环查hs_key_map,这个数组最后一个元素定义为0
for (i = 0; hs_key_map[i] != 0; i++) {
//高8位为当前key值的,取出其相应的低24位返回函数
if ((hs_key_map[i] & 0xff000000) == key)
return hs_key_map[i] & 0x00ffffff;
}
return -1;
}
Input_report_key
Input_report_switch
根据上报key事件和开关事件进行不同处理
这里有无mic的事件做了区分,update时会将h2w/state更新,但是input时没有区分出来。
2,应用层获取耳机状态文件WiredAccessoryObserver.java
调用关系
onuevent
->updatestate
->update
->handlemessage
->setdevicesState
->setdevicestate
Audiomanager.java
publicvoidsetWiredDeviceConnectionState(int device,int state, String name)
Audioservice.java
setDeviceconnectionState对应audiopolicymanager.cpp中的函数(JNI机制)
调用HAL层中的设备可以实现驱动通路的切换。
下面对应用分析比较透彻,对上层UI通过发送消息通知变更外设图标
http://my.eoe.cn/cnhua5/archive/1252.html