irqreturn_t mt_bt_eirq_handler(int i, void *arg)
{
    struct hci_dev *hdev = NULL;

    BT_HWCTL_ALERT("mt_bt_eirq_handler\n");
    mt_bt_disable_irq();

#ifdef CONFIG_BT_HCIUART
    /* BlueZ stack, hci_uart driver */
    hdev = hci_dev_get(0);
    if(hdev == NULL){
        /* Avoid the early interrupt before hci0 registered */
        BT_HWCTL_ALERT("hdev is NULL\n");
    }else{
        BT_HWCTL_ALERT("EINT arrives! Notify host awake\n");
        BT_HWCTL_ALERT("Send host awake command\n");
        hci_send_cmd(hdev, 0xFCC1, 0, NULL);
        /* Enable irq after receiving host awake command's event */
    }
#else
    /* Maybe handle the interrupt in user space? */
    eint_gen = 1;
    wake_up_interruptible(&eint_wait);
    /* Send host awake command in user space, enable irq then */
#endif

    return IRQ_HANDLED;
}
static int __init bt_hwctl_init(void)
{
    int ret = -1, err = -1;
    
    BT_HWCTL_DEBUG("bt_hwctl_init\n");
    
    platform_driver_register(&mt6622_driver);
    
    if (!(bh = kzalloc(sizeof(struct bt_hwctl), GFP_KERNEL)))
    {
        BT_HWCTL_ALERT("bt_hwctl_init allocate dev struct failed\n");
        err = -ENOMEM;
        goto ERR_EXIT;
    }
    
    ret = alloc_chrdev_region(&bh->dev_t, 0, 1, BTHWCTL_NAME);
    if (ret) {
        BT_HWCTL_ALERT("alloc chrdev region failed\n");
        goto ERR_EXIT;
    }
    
    BT_HWCTL_DEBUG("alloc %s:%d:%d\n", BTHWCTL_NAME, MAJOR(bh->dev_t), MINOR(bh->dev_t));
    
    cdev_init(&bh->cdev, &bt_hwctl_fops);
    
    bh->cdev.owner = THIS_MODULE;
    bh->cdev.ops = &bt_hwctl_fops;
    
    err = cdev_add(&bh->cdev, bh->dev_t, 1);
    if (err) {
        BT_HWCTL_ALERT("add chrdev failed\n");
        goto ERR_EXIT;
    }
    
    bh->cls = class_create(THIS_MODULE, BTHWCTL_NAME);
    if (IS_ERR(bh->cls)) {
        err = PTR_ERR(bh->cls);
        BT_HWCTL_ALERT("class_create failed, errno:%d\n", err);
        goto ERR_EXIT;
    }
    
    bh->dev = device_create(bh->cls, NULL, bh->dev_t, NULL, BTHWCTL_NAME);
    mutex_init(&bh->sem);
    
    init_waitqueue_head(&eint_wait);
    
    /*INIT_WORK(&mtk_wcn_bt_event_work, mtk_wcn_bt_work_fun);
    mtk_wcn_bt_workqueue = create_singlethread_workqueue("mtk_wcn_bt");
    if (!mtk_wcn_bt_workqueue) {
        printk("create_singlethread_workqueue failed.\n");
        err = -ESRCH;
        goto ERR_EXIT;
    }*/    
    
    /* request gpio used by BT */
    //mt_bt_gpio_init();
    
    BT_HWCTL_DEBUG("bt_hwctl_init ok\n");
    
    return 0;
    
ERR_EXIT:
    if (err == 0)
        cdev_del(&bh->cdev);
    if (ret == 0)
        unregister_chrdev_region(bh->dev_t, 1);
        
    if (bh){
        kfree(bh);
        bh = NULL;
    }     
    return -1;
}
/*****************************************************************************
 *  bt_hwctl_ioctl
*****************************************************************************/
static long bt_hwctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int ret = 0;
     
    BT_HWCTL_DEBUG("bt_hwctl_ioctl\n");
    
    if(!bh) {
        BT_HWCTL_ALERT("bt_hwctl struct not initialized\n");
        return -EFAULT;
    }
    
    switch(cmd)
    {
        case BTHWCTL_IOCTL_SET_POWER:
        {
            unsigned long pwr = 0;
            if (copy_from_user(&pwr, (void*)arg, sizeof(unsigned long)))
                return -EFAULT;
                
            BT_HWCTL_DEBUG("BTHWCTL_IOCTL_SET_POWER: %d\n", (int)pwr);
            
            mutex_lock(&bh->sem);
            if (pwr){
                ret = mt_bt_power_on();
            }
            else{
                mt_bt_power_off();
            }
            mutex_unlock(&bh->sem);
            
            break;
        }
        case BTHWCTL_IOCTL_SET_EINT:
        {
            unsigned long eint = 0;
            if (copy_from_user(&eint, (void*)arg, sizeof(unsigned long)))
                return -EFAULT;
                
            BT_HWCTL_DEBUG("BTHWCTL_IOCTL_SET_EINT: %d\n", (int)eint);
            
            mutex_lock(&bh->sem);
            if (eint){
                /* Enable irq from user space */ 
                BT_HWCTL_DEBUG("Set BT EINT enable\n");
                mt_bt_enable_irq();
            }
            else{
                /* Disable irq from user space, maybe time to close driver */
                BT_HWCTL_DEBUG("Set BT EINT disable\n");
                mt_bt_disable_irq();
                eint_mask = 1;
                wake_up_interruptible(&eint_wait);
            }
            mutex_unlock(&bh->sem);
            
            break;
        }    
        default:
            BT_HWCTL_ALERT("BTHWCTL_IOCTL not support\n");
            return -EPERM;
    }
    
    return ret;
}