android_kernel_cmhtcleo/arch/arm/mach-msm/htc_headset_mgr.c

921 lines
22 KiB
C

/*
*
* /arch/arm/mach-msm/htc_headset_mgr.c
*
* HTC headset manager driver.
*
* Copyright (C) 2010 HTC, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sysdev.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/hrtimer.h>
#include <linux/debugfs.h>
#include <linux/jiffies.h>
#include <linux/gpio.h>
#include <asm/atomic.h>
#include <asm/mach-types.h>
#include <mach/board.h>
#include <mach/vreg.h>
#include <mach/atmega_microp.h>
#include <mach/htc_headset_mgr.h>
#define DRIVER_NAME "HS_MGR"
/* #define CONFIG_DEBUG_H2W */
/*Delay 200ms when 11pin device plug in*/
#define H2W_DETECT_DELAY msecs_to_jiffies(200)
#define BUTTON_H2W_DELAY msecs_to_jiffies(10)
#define H2WI(fmt, arg...) \
printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg)
#define H2WE(fmt, arg...) \
printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg)
#ifdef CONFIG_DEBUG_H2W
#define H2W_DBG(fmt, arg...) \
printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg)
#else
#define H2W_DBG(fmt, arg...) do {} while (0)
#endif
static struct workqueue_struct *detect_wq;
static void insert_35mm_do_work(struct work_struct *work);
static DECLARE_WORK(insert_35mm_work, insert_35mm_do_work);
static void remove_35mm_do_work(struct work_struct *work);
static DECLARE_WORK(remove_35mm_work, remove_35mm_do_work);
static struct workqueue_struct *button_wq;
static void button_35mm_do_work(struct work_struct *w);
static DECLARE_DELAYED_WORK(button_35mm_work, button_35mm_do_work);
static int hs_mgr_rpc_call(struct msm_rpc_server *server,
struct rpc_request_hdr *req, unsigned len);
static struct msm_rpc_server hs_rpc_server = {
.prog = HS_RPC_SERVER_PROG,
.vers = HS_RPC_SERVER_VERS,
.rpc_call = hs_mgr_rpc_call,
};
struct button_work {
struct delayed_work key_work;
int key_code;
};
static struct h2w_info *hi;
static struct hs_notifier_func hs_mgr_notifier;
void hs_notify_hpin_irq(void)
{
hi->hpin_jiffies = jiffies;
SYS_MSG("HPIN IRQ");
}
int hs_hpin_stable(void)
{
unsigned long last_hpin_jiffies = 0;
unsigned long unstable_jiffies = 1.2 * HZ;
last_hpin_jiffies = hi->hpin_jiffies;
if (time_before_eq(jiffies, last_hpin_jiffies + unstable_jiffies))
return 0;
return 1;
}
int headset_notifier_register(struct headset_notifier *notifier)
{
if (!notifier->func) {
SYS_MSG("NULL register function");
return 0;
}
switch (notifier->id) {
case HEADSET_REG_REMOTE_ADC:
SYS_MSG("Register REMOTE_ADC notifier");
hs_mgr_notifier.remote_adc = notifier->func;
break;
case HEADSET_REG_RPC_KEY:
SYS_MSG("Register RPC_KEY notifier");
hs_mgr_notifier.rpc_key = notifier->func;
break;
case HEADSET_REG_MIC_STATUS:
SYS_MSG("Register MIC_STATUS notifier");
hs_mgr_notifier.mic_status = notifier->func;
break;
case HEADSET_REG_MIC_BIAS:
SYS_MSG("Register MIC_BIAS notifier");
hs_mgr_notifier.mic_bias_enable = notifier->func;
break;
case HEADSET_REG_MIC_SELECT:
SYS_MSG("Register MIC_SELECT notifier");
hs_mgr_notifier.mic_select = notifier->func;
break;
case HEADSET_REG_KEY_INT_ENABLE:
SYS_MSG("Register KEY_INT_ENABLE notifier");
hs_mgr_notifier.key_int_enable = notifier->func;
break;
case HEADSET_REG_KEY_ENABLE:
SYS_MSG("Register KEY_ENABLE notifier");
hs_mgr_notifier.key_enable = notifier->func;
break;
default:
SYS_MSG("Unknown register ID");
return 0;
}
return 1;
}
static int hs_mgr_rpc_call(struct msm_rpc_server *server,
struct rpc_request_hdr *req, unsigned len)
{
struct hs_rpc_server_args_key *args_key;
DBG_MSG("");
switch (req->procedure) {
case HS_RPC_SERVER_PROC_NULL:
SYS_MSG("RPC_SERVER_NULL");
break;
case HS_RPC_SERVER_PROC_KEY:
args_key = (struct hs_rpc_server_args_key *)(req + 1);
args_key->adc = be32_to_cpu(args_key->adc);
SYS_MSG("RPC_SERVER_KEY ADC = %u (0x%X)",
args_key->adc, args_key->adc);
if (hs_mgr_notifier.rpc_key)
hs_mgr_notifier.rpc_key(args_key->adc);
else
SYS_MSG("RPC_KEY notify function doesn't exist");
break;
default:
SYS_MSG("Unknown RPC procedure");
return -EINVAL;
}
return 0;
}
static ssize_t h2w_print_name(struct switch_dev *sdev, char *buf)
{
return sprintf(buf, "Headset\n");
}
void button_pressed(int type)
{
printk(KERN_INFO "[H2W] button_pressed %d\n", type);
atomic_set(&hi->btn_state, type);
input_report_key(hi->input, type, 1);
input_sync(hi->input);
}
void button_released(int type)
{
printk(KERN_INFO "[H2W] button_released %d\n", type);
atomic_set(&hi->btn_state, 0);
input_report_key(hi->input, type, 0);
input_sync(hi->input);
}
void headset_button_event(int is_press, int type)
{
if (!hs_hpin_stable()) {
H2WI("The HPIN is unstable, SKIP THE BUTTON EVENT.");
return;
}
if (!is_press) {
if (hi->ignore_btn)
hi->ignore_btn = 0;
else
button_released(type);
} else {
if (!hi->ignore_btn && !atomic_read(&hi->btn_state))
button_pressed(type);
}
}
static void set_35mm_hw_state(int state)
{
if (hi->mic_bias_state != state && hs_mgr_notifier.mic_bias_enable) {
hs_mgr_notifier.mic_bias_enable(state);
hi->mic_bias_state = state;
if (state) /* Wait for MIC bias stable */
msleep(HS_DELAY_MIC_BIAS);
}
if (hs_mgr_notifier.mic_select)
hs_mgr_notifier.mic_select(state);
if (hs_mgr_notifier.key_enable)
hs_mgr_notifier.key_enable(state);
if (hs_mgr_notifier.key_int_enable)
hs_mgr_notifier.key_int_enable(state);
}
static void insert_11pin_35mm(int *state)
{
int mic = HEADSET_NO_MIC;
SYS_MSG("Insert USB 3.5mm headset");
set_35mm_hw_state(1);
if (hs_mgr_notifier.mic_status) {
mic = hs_mgr_notifier.mic_status();
}
if (mic == HEADSET_NO_MIC) {
/* without microphone */
*state |= BIT_HEADSET_NO_MIC;
hi->h2w_35mm_status = HTC_35MM_NO_MIC;
printk(KERN_INFO "11pin_3.5mm without microphone\n");
} else { /* with microphone */
*state |= BIT_HEADSET;
hi->h2w_35mm_status = HTC_35MM_MIC;
printk(KERN_INFO "11pin_3.5mm with microphone\n");
}
}
static void remove_11pin_35mm(void)
{
SYS_MSG("Remove USB 3.5mm headset");
set_35mm_hw_state(0);
if (atomic_read(&hi->btn_state))
button_released(atomic_read(&hi->btn_state));
hi->h2w_35mm_status = HTC_35MM_UNPLUG;
}
static void button_35mm_do_work(struct work_struct *w)
{
int key;
int pressed;
struct button_work *work;
work = container_of(w, struct button_work, key_work.work);
hi->key_level_flag = work->key_code;
if (!hi->is_ext_insert && !hi->h2w_35mm_status) {
kfree(work);
H2WI("3.5mm headset is plugged out, skip report key event");
return;
}
if (hi->key_level_flag) {
pressed = (hi->key_level_flag & 0x80) ? 0:1;
switch (hi->key_level_flag & 0x7f) {
case 1:
H2WI("3.5mm RC: Play(%d)", pressed);
key = HS_MGR_KEYCODE_MEDIA;
break;
case 2:
H2WI("3.5mm RC: BACKWARD(%d)", pressed);
key = HS_MGR_KEYCODE_BACKWARD;
break;
case 3:
H2WI("3.5mm RC: FORWARD(%d)", pressed);
key = HS_MGR_KEYCODE_FORWARD;
break;
default:
H2WI("3.5mm RC: WRONG Button Pressed (%d)", hi->key_level_flag);
return;
}
headset_button_event(pressed, key);
}
wake_lock_timeout(&hi->headset_wake_lock, 1.5 * HZ);
kfree(work);
}
static void enable_metrico_headset(int enable)
{
if (enable && !hi->metrico_status) {
#if 0
enable_mos_test(1);
#endif
hi->metrico_status = 1;
printk(KERN_INFO "Enable metrico headset\n");
}
if (!enable && hi->metrico_status) {
#if 0
enable_mos_test(0);
#endif
hi->metrico_status = 0;
printk(KERN_INFO "Disable metrico headset\n");
}
}
static void remove_35mm_do_work(struct work_struct *work)
{
int state;
wake_lock_timeout(&hi->headset_wake_lock, 2.5 * HZ);
H2W_DBG("");
/*To solve the insert, remove, insert headset problem*/
if (time_before_eq(jiffies, hi->insert_jiffies))
msleep(800);
if (hi->is_ext_insert) {
H2WI("Skip 3.5mm headset plug out!!!");
return;
}
SYS_MSG("Remove 3.5mm headset");
set_35mm_hw_state(0);
/* For HW Metrico lab test */
if (hi->metrico_status)
enable_metrico_headset(0);
if (atomic_read(&hi->btn_state))
button_released(atomic_read(&hi->btn_state));
hi->ext_35mm_status = HTC_35MM_UNPLUG;
mutex_lock(&hi->mutex_lock);
state = switch_get_state(&hi->sdev);
if (hi->usb_dev_type == USB_HEADSET) {
hi->usb_dev_status = STATUS_CONNECTED_ENABLED;
state &= ~BIT_HEADSET;
state |= BIT_HEADSET_NO_MIC;
switch_set_state(&hi->sdev, state);
mutex_unlock(&hi->mutex_lock);
} else if (hi->usb_dev_type == H2W_TVOUT) {
state &= ~BIT_HEADSET;
state |= BIT_HEADSET_NO_MIC;
switch_set_state(&hi->sdev, state);
#if 0
} else if (hi->cable_in1 && !gpio_get_value(hi->cable_in1)) {
state &= ~BIT_35MM_HEADSET;
switch_set_state(&hi->sdev, state);
queue_delayed_work(detect_wq, &detect_h2w_work,
HS_DELAY_ZERO_JIFFIES);
#endif
} else {
state &= ~(BIT_HEADSET | BIT_HEADSET_NO_MIC);
switch_set_state(&hi->sdev, state);
}
mutex_unlock(&hi->mutex_lock);
}
static void insert_35mm_do_work(struct work_struct *work)
{
int state;
int i, mic1, mic2;
H2W_DBG("");
hi->insert_jiffies = jiffies + HZ;
wake_lock_timeout(&hi->headset_wake_lock, 1.5 * HZ);
#if 0
if (hi->usb_dev_type && hi->is_ext_insert &&
hi->usb_dev_type != H2W_TVOUT && hi->usb_dev_type != USB_HEADSET)
remove_headset();
else if (hi->usb_dev_type == USB_HEADSET)
hi->usb_dev_status = STATUS_CONNECTED_DISABLED;
#endif
if (hi->usb_dev_type == USB_HEADSET)
hi->usb_dev_status = STATUS_CONNECTED_DISABLED;
if (hi->is_ext_insert) {
SYS_MSG("Insert 3.5mm headset");
set_35mm_hw_state(1);
hi->ignore_btn = 0;
mic1 = mic2 = HEADSET_NO_MIC;
if (hs_mgr_notifier.mic_status) {
if (hi->ext_35mm_status == HTC_35MM_NO_MIC ||
hi->h2w_35mm_status == HTC_35MM_NO_MIC)
for (i = 0; i < 10; i++) {
mic1 = hs_mgr_notifier.mic_status();
msleep(HS_DELAY_MIC_DETECT);
mic2 = hs_mgr_notifier.mic_status();
if (mic1 == mic2)
break;
}
else
mic1 = mic2 = hs_mgr_notifier.mic_status();
}
/* For HW Metrico lab test */
if (mic2 == HEADSET_METRICO && !hi->metrico_status)
enable_metrico_headset(1);
mutex_lock(&hi->mutex_lock);
state = switch_get_state(&hi->sdev);
state &= ~(BIT_HEADSET | BIT_HEADSET_NO_MIC);
if (mic2 == HEADSET_NO_MIC || mic1 != mic2) {
state |= BIT_HEADSET_NO_MIC;
printk(KERN_INFO "3.5mm_headset without microphone\n");
} else {
state |= BIT_HEADSET;
printk(KERN_INFO "3.5mm_headset with microphone\n");
}
switch_set_state(&hi->sdev, state);
if (state & BIT_HEADSET_NO_MIC)
hi->ext_35mm_status = HTC_35MM_NO_MIC;
else
hi->ext_35mm_status = HTC_35MM_MIC;
mutex_unlock(&hi->mutex_lock);
}
}
int htc_35mm_remote_notify_insert_ext_headset(int insert)
{
if (hi) {
mutex_lock(&hi->mutex_lock);
hi->is_ext_insert = insert;
mutex_unlock(&hi->mutex_lock);
H2WI(" %d", hi->is_ext_insert);
if (!hi->is_ext_insert)
queue_work(detect_wq, &remove_35mm_work);
else
queue_work(detect_wq, &insert_35mm_work);
}
return 1;
}
int htc_35mm_remote_notify_microp_ready(void)
{
if (hi) {
if (hi->is_ext_insert)
queue_work(detect_wq, &insert_35mm_work);
#if 0
if (hi->h2w_35mm_status)
insert_headset(NORMAL_HEARPHONE);
#endif
}
return 1;
}
int htc_35mm_remote_notify_button_status(int key_level)
{
struct button_work *work;
if (hi->ext_35mm_status == HTC_35MM_NO_MIC ||
hi->h2w_35mm_status == HTC_35MM_NO_MIC) {
SYS_MSG("MIC re-detection");
msleep(HS_DELAY_MIC_DETECT);
queue_work(detect_wq, &insert_35mm_work);
} else if (!hs_hpin_stable()) {
H2WI("The HPIN is unstable, SKIP THE BUTTON EVENT.");
return 1;
} else {
work = kzalloc(sizeof(struct button_work), GFP_KERNEL);
if (!work) {
printk(KERN_INFO "Failed to allocate button memory\n");
return 1;
}
work->key_code = key_level;
INIT_DELAYED_WORK(&work->key_work, button_35mm_do_work);
queue_delayed_work(button_wq, &work->key_work,
HS_JIFFIES_BUTTON);
}
return 1;
}
static void usb_headset_detect(int type)
{
int state;
mutex_lock(&hi->mutex_lock);
state = switch_get_state(&hi->sdev);
switch (type) {
case NO_DEVICE:
if (hi->usb_dev_type == USB_HEADSET) {
printk(KERN_INFO "Remove USB headset\n");
hi->usb_dev_type = NO_DEVICE;
hi->usb_dev_status = STATUS_DISCONNECTED;
state &= ~BIT_USB_HEADSET;
if (!hi->is_ext_insert)
state &= ~BIT_HEADSET_NO_MIC;
}
break;
case USB_HEADSET:
printk(KERN_INFO "Insert USB headset\n");
hi->usb_dev_type = USB_HEADSET;
if (hi->is_ext_insert) {
printk(KERN_INFO "Disable USB headset\n");
hi->usb_dev_status = STATUS_CONNECTED_DISABLED;
state |= BIT_USB_HEADSET;
} else {
printk(KERN_INFO "Enable USB headset\n");
hi->usb_dev_status = STATUS_CONNECTED_ENABLED;
state |= (BIT_USB_HEADSET | BIT_HEADSET_NO_MIC);
}
break;
default:
printk(KERN_INFO "Unknown headset type\n");
}
switch_set_state(&hi->sdev, state);
mutex_unlock(&hi->mutex_lock);
}
void headset_ext_detect(int type)
{
switch (type) {
case NO_DEVICE:
if (hi->usb_dev_type == USB_HEADSET)
usb_headset_detect(type);
break;
case USB_HEADSET:
usb_headset_detect(type);
break;
default:
printk(KERN_INFO "Unknown headset type\n");
}
}
int switch_send_event(unsigned int bit, int on)
{
unsigned long state;
mutex_lock(&hi->mutex_lock);
state = switch_get_state(&hi->sdev);
state &= ~(bit);
if (on)
state |= bit;
switch_set_state(&hi->sdev, state);
mutex_unlock(&hi->mutex_lock);
return 0;
}
static ssize_t tty_flag_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *s = buf;
mutex_lock(&hi->mutex_lock);
s += sprintf(s, "%d\n", hi->tty_enable_flag);
mutex_unlock(&hi->mutex_lock);
return (s - buf);
}
static ssize_t tty_flag_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int state;
mutex_lock(&hi->mutex_lock);
state = switch_get_state(&hi->sdev);
state &= ~(BIT_TTY_FULL | BIT_TTY_VCO | BIT_TTY_HCO);
if (count == (strlen("enable") + 1) &&
strncmp(buf, "enable", strlen("enable")) == 0) {
hi->tty_enable_flag = 1;
switch_set_state(&hi->sdev, state | BIT_TTY_FULL);
mutex_unlock(&hi->mutex_lock);
printk(KERN_INFO "Enable TTY FULL\n");
return count;
}
if (count == (strlen("vco_enable") + 1) &&
strncmp(buf, "vco_enable", strlen("vco_enable")) == 0) {
hi->tty_enable_flag = 2;
switch_set_state(&hi->sdev, state | BIT_TTY_VCO);
mutex_unlock(&hi->mutex_lock);
printk(KERN_INFO "Enable TTY VCO\n");
return count;
}
if (count == (strlen("hco_enable") + 1) &&
strncmp(buf, "hco_enable", strlen("hco_enable")) == 0) {
hi->tty_enable_flag = 3;
switch_set_state(&hi->sdev, state | BIT_TTY_HCO);
mutex_unlock(&hi->mutex_lock);
printk(KERN_INFO "Enable TTY HCO\n");
return count;
}
if (count == (strlen("disable") + 1) &&
strncmp(buf, "disable", strlen("disable")) == 0) {
hi->tty_enable_flag = 0;
switch_set_state(&hi->sdev, state);
mutex_unlock(&hi->mutex_lock);
printk(KERN_INFO "Disable TTY\n");
return count;
}
printk(KERN_ERR "tty_enable_flag_store: invalid argument\n");
return -EINVAL;
}
static DEVICE_ACCESSORY_ATTR(tty, 0666, tty_flag_show, tty_flag_store);
static ssize_t fm_flag_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int state;
mutex_lock(&hi->mutex_lock);
state = switch_get_state(&hi->sdev);
state &= ~(BIT_FM_HEADSET | BIT_FM_SPEAKER);
if (count == (strlen("fm_headset") + 1) &&
strncmp(buf, "fm_headset", strlen("fm_headset")) == 0) {
hi->fm_flag = 1;
state |= BIT_FM_HEADSET;
printk(KERN_INFO "Enable FM HEADSET\n");
} else if (count == (strlen("fm_speaker") + 1) &&
strncmp(buf, "fm_speaker", strlen("fm_speaker")) == 0) {
hi->fm_flag = 2;
state |= BIT_FM_SPEAKER;
printk(KERN_INFO "Enable FM SPEAKER\n");
} else if (count == (strlen("disable") + 1) &&
strncmp(buf, "disable", strlen("disable")) == 0) {
hi->fm_flag = 0 ;
printk(KERN_INFO "Disable FM\n");
} else {
mutex_unlock(&hi->mutex_lock);
printk(KERN_ERR "fm_enable_flag_store: invalid argument\n");
return -EINVAL;
}
switch_set_state(&hi->sdev, state);
mutex_unlock(&hi->mutex_lock);
return count;
}
static ssize_t fm_flag_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *s = buf;
char *show_str;
mutex_lock(&hi->mutex_lock);
if (hi->fm_flag == 0)
show_str = "disable";
if (hi->fm_flag == 1)
show_str = "fm_headset";
if (hi->fm_flag == 2)
show_str = "fm_speaker";
s += sprintf(s, "%s\n", show_str);
mutex_unlock(&hi->mutex_lock);
return (s - buf);
}
static DEVICE_ACCESSORY_ATTR(fm, 0666, fm_flag_show, fm_flag_store);
static int register_common_headset(struct h2w_info *h2w, int create_attr)
{
int ret = 0;
hi = h2w;
hi->htc_accessory_class = class_create(THIS_MODULE, "htc_accessory");
if (IS_ERR(hi->htc_accessory_class)) {
ret = PTR_ERR(hi->htc_accessory_class);
hi->htc_accessory_class = NULL;
goto err_create_class;
}
hi->tty_dev = device_create(hi->htc_accessory_class,
NULL, 0, "%s", "tty");
if (unlikely(IS_ERR(hi->tty_dev))) {
ret = PTR_ERR(hi->tty_dev);
hi->tty_dev = NULL;
goto err_create_tty_device;
}
/* register the attributes */
ret = device_create_file(hi->tty_dev, &dev_attr_tty);
if (ret)
goto err_create_tty_device_file;
hi->fm_dev = device_create(hi->htc_accessory_class,
NULL, 0, "%s", "fm");
if (unlikely(IS_ERR(hi->fm_dev))) {
ret = PTR_ERR(hi->fm_dev);
hi->fm_dev = NULL;
goto err_create_fm_device;
}
/* register the attributes */
ret = device_create_file(hi->fm_dev, &dev_attr_fm);
if (ret)
goto err_create_fm_device_file;
return 0;
err_create_fm_device_file:
device_unregister(hi->fm_dev);
err_create_fm_device:
device_remove_file(hi->tty_dev, &dev_attr_tty);
err_create_tty_device_file:
device_unregister(hi->tty_dev);
err_create_tty_device:
class_destroy(hi->htc_accessory_class);
err_create_class:
return ret;
}
static void unregister_common_headset(struct h2w_info *h2w)
{
hi = h2w;
device_remove_file(hi->tty_dev, &dev_attr_tty);
device_unregister(hi->tty_dev);
device_remove_file(hi->fm_dev, &dev_attr_fm);
device_unregister(hi->fm_dev);
class_destroy(hi->htc_accessory_class);
}
static int htc_35mm_probe(struct platform_device *pdev)
{
int ret;
struct htc_headset_mgr_platform_data *pdata = pdev->dev.platform_data;
SYS_MSG("++++++++++++++++++++");
hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL);
if (!hi)
return -ENOMEM;
hi->driver_flag = pdata->driver_flag;
hi->hpin_jiffies = jiffies;
hi->ext_35mm_status = 0;
hi->h2w_35mm_status = 0;
hi->is_ext_insert = 0;
hi->mic_bias_state = 0;
hi->key_level_flag = -1;
atomic_set(&hi->btn_state, 0);
hi->ignore_btn = 0;
hi->usb_dev_type = NO_DEVICE;
hi->tty_enable_flag = 0;
hi->fm_flag = 0;
hi->mic_switch_flag = 1;
hi->rc_flag = 0;
hi->insert_11pin_35mm = insert_11pin_35mm;
hi->remove_11pin_35mm = remove_11pin_35mm;
mutex_init(&hi->mutex_lock);
mutex_init(&hi->mutex_rc_lock);
wake_lock_init(&hi->headset_wake_lock, WAKE_LOCK_SUSPEND, "headset");
hi->sdev.name = "h2w";
hi->sdev.print_name = h2w_print_name;
ret = switch_dev_register(&hi->sdev);
if (ret < 0)
goto err_switch_dev_register;
detect_wq = create_workqueue("detection");
if (detect_wq == NULL) {
ret = -ENOMEM;
goto err_create_detect_work_queue;
}
button_wq = create_workqueue("button");
if (button_wq == NULL) {
ret = -ENOMEM;
goto err_create_button_work_queue;
}
hi->input = input_allocate_device();
if (!hi->input) {
ret = -ENOMEM;
goto err_request_input_dev;
}
hi->input->name = "h2w headset";
set_bit(EV_SYN, hi->input->evbit);
set_bit(EV_KEY, hi->input->evbit);
set_bit(KEY_END, hi->input->keybit);
set_bit(KEY_MUTE, hi->input->keybit);
set_bit(KEY_VOLUMEDOWN, hi->input->keybit);
set_bit(KEY_VOLUMEUP, hi->input->keybit);
set_bit(KEY_NEXTSONG, hi->input->keybit);
set_bit(KEY_PLAYPAUSE, hi->input->keybit);
set_bit(KEY_PREVIOUSSONG, hi->input->keybit);
set_bit(KEY_MEDIA, hi->input->keybit);
set_bit(KEY_SEND, hi->input->keybit);
ret = input_register_device(hi->input);
if (ret < 0)
goto err_register_input_dev;
ret = register_common_headset(hi, 0);
if (ret)
goto err_register_common_headset;
if (hi->driver_flag & DRIVER_HS_MGR_RPC_SERVER) {
/* Create RPC server */
ret = msm_rpc_create_server(&hs_rpc_server);
if (ret < 0) {
SYS_MSG("Failed to create RPC server");
goto err_create_rpc_server;
}
SYS_MSG("Create RPC server successfully");
}
SYS_MSG("--------------------");
return 0;
err_create_rpc_server:
err_register_common_headset:
input_unregister_device(hi->input);
err_register_input_dev:
input_free_device(hi->input);
err_request_input_dev:
destroy_workqueue(button_wq);
err_create_button_work_queue:
destroy_workqueue(detect_wq);
err_create_detect_work_queue:
switch_dev_unregister(&hi->sdev);
err_switch_dev_register:
printk(KERN_ERR "H2W: Failed to register driver\n");
return ret;
}
static int htc_35mm_remove(struct platform_device *pdev)
{
H2W_DBG("");
#if 0
if ((switch_get_state(&hi->sdev) &
(BIT_HEADSET | BIT_HEADSET_NO_MIC)) != 0)
remove_headset();
#endif
unregister_common_headset(hi);
input_unregister_device(hi->input);
destroy_workqueue(detect_wq);
destroy_workqueue(button_wq);
switch_dev_unregister(&hi->sdev);
return 0;
}
static struct platform_driver htc_35mm_driver = {
.probe = htc_35mm_probe,
.remove = htc_35mm_remove,
.driver = {
.name = "HTC_HEADSET_MGR",
.owner = THIS_MODULE,
},
};
static int __init htc_35mm_init(void)
{
H2W_DBG("");
return platform_driver_register(&htc_35mm_driver);
}
static void __exit htc_35mm_exit(void)
{
platform_driver_unregister(&htc_35mm_driver);
}
module_init(htc_35mm_init);
module_exit(htc_35mm_exit);
MODULE_DESCRIPTION("HTC headset manager driver");
MODULE_LICENSE("GPL");