+++ /dev/null
-\r
-/*\r
- * Copyright (C) 2010-2011 RDA Micro <anli@rdamicro.com>\r
- * This file belong to RDA micro\r
- * File: drivers/char/tcc_bt_dev.c\r
- */\r
-\r
-#include <linux/kernel.h>\r
-#include <linux/module.h>\r
-#include <linux/fs.h>\r
-#include <asm/uaccess.h>\r
-#include <linux/ioctl.h>\r
-#include <linux/device.h>\r
-#include <asm/io.h>\r
-#include <linux/delay.h>\r
-#include <linux/gpio.h>\r
-#include <mach/gpio.h>\r
-#include <asm/mach-types.h>\r
-#include <mach/iomux.h>\r
-#include <linux/interrupt.h>\r
-#include <asm/irq.h>\r
-#include <linux/wakelock.h>\r
-#include <linux/rfkill-rk.h>\r
-#include <linux/platform_device.h>\r
-\r
-#include <net/bluetooth/bluetooth.h>\r
-#include <net/bluetooth/hci_core.h>\r
-\r
-\r
-#ifndef HOST_WAKE_DELAY\r
-#define HOST_WAKE_DELAY\r
-#endif\r
-\r
-#define DEV_NAME "tcc_bt_dev"\r
-\r
-#define BT_DEV_MAJOR_NUM 234\r
-#define BT_DEV_MINOR_NUM 0\r
-\r
-#define IOCTL_BT_DEV_POWER _IO(BT_DEV_MAJOR_NUM, 100)\r
-#define IOCTL_BT_SET_EINT _IO(BT_DEV_MAJOR_NUM, 101)\r
-\r
-static wait_queue_head_t eint_wait;\r
-static struct work_struct rda_bt_event_work;\r
-//static struct workqueue_struct *rda_bt_workqueue;
-\r
-static int irq_num = -1;
-static int eint_gen;\r
-static int eint_mask;\r
-static struct class *bt_dev_class;\r
-static struct mutex sem;\r
-static struct tcc_bt_platform_data *tcc_bt_pdata = NULL;\r
-static int irq_flag = 0;
-
-
-static void rda_bt_enable_irq(void)\r
-{
- if((irq_num != -1) && (irq_flag == 0))
- {
- enable_irq(irq_num);
- irq_flag = 1;
- }
-}
-
-static void rda_bt_disable_irq(void)\r
-{
- if((irq_num != -1) && (irq_flag == 1))
- {
- disable_irq_nosync(irq_num);
- irq_flag = 0;
- }
-}
-
-#if 0
-static void rda_bt_work_fun(struct work_struct *work)\r
-{\r
- struct hci_dev *hdev = NULL;\r
-\r
- /* BlueZ stack, hci_uart driver */\r
- hdev = hci_dev_get(0);\r
- \r
- if(hdev == NULL)\r
- {\r
- /* Avoid the early interrupt before hci0 registered */\r
- //printk(KERN_ALERT "hdev is NULL\n ");\r
- }\r
- else\r
- {\r
- printk(KERN_ALERT "Send host wakeup command.\n");\r
- hci_send_cmd(hdev, 0xC0FC, 0, NULL);\r
- }\r
- \r
- rda_bt_enable_irq();\r
-}
-#endif
-
-static int tcc_bt_dev_open(struct inode *inode, struct file *file)\r
-{\r
- printk("[## BT ##] tcc_bt_dev_open.\n");\r
- eint_gen = 0;\r
- eint_mask = 0;\r
- return 0;\r
-}\r
-\r
-static int tcc_bt_dev_release(struct inode *inode, struct file *file)\r
-{\r
- printk("[## BT ##] tcc_bt_dev_release.\n");\r
- eint_gen = 0;\r
- eint_mask = 0;\r
- return 0;\r
-}\r
-\r
-/*****************************************************************************\r
- * tcc_bt_dev_poll\r
-*****************************************************************************/\r
-\r
-static unsigned int tcc_bt_dev_poll(struct file *file, poll_table *wait)\r
-{\r
- uint32_t mask = 0;\r
- \r
- printk("[## BT ##] tcc_bt_poll eint_gen %d, eint_mask %d ++\n", eint_gen, eint_mask);\r
-\r
- wait_event_interruptible(eint_wait, (eint_gen == 1 || eint_mask == 1));\r
- \r
- printk("[## BT ##] tcc_bt_poll eint_gen %d, eint_mask %d --\n", eint_gen, eint_mask);\r
- \r
- if(eint_gen == 1)\r
- {\r
- mask = POLLIN|POLLRDNORM;\r
- eint_gen = 0;\r
- }\r
- else if (eint_mask == 1)\r
- {\r
- mask = POLLERR;\r
- eint_mask = 0;\r
- }\r
- \r
- return mask;\r
-}\r
-\r
-static int tcc_bt_power_control(int on_off)\r
-{ \r
- printk("[## BT ##] tcc_bt_power_control input[%d].\n", on_off);\r
- \r
- if(on_off)\r
- { \r
- gpio_direction_output(tcc_bt_pdata->power_gpio.io, tcc_bt_pdata->power_gpio.enable);\r
- msleep(500);
- rda_bt_enable_irq();
- }\r
- else\r
- {\r
- gpio_direction_output(tcc_bt_pdata->power_gpio.io, !tcc_bt_pdata->power_gpio.enable);\r
- rda_bt_disable_irq();
- msleep(500);\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-\r
-static long tcc_bt_dev_ioctl(struct file *file, unsigned int cmd,unsigned long arg)\r
-{\r
- void __user *argp = (void __user *)arg;\r
- int ret = -1;\r
- int rate = 0;\r
-\r
- switch(cmd)\r
- {\r
- case IOCTL_BT_DEV_POWER:\r
- {\r
- printk("[## BT ##] IOCTL_BT_DEV_POWER cmd[%d] parm1[%d].\n", cmd, rate);\r
- if (copy_from_user(&rate, argp, sizeof(rate)))\r
- return -EFAULT;\r
-\r
- mutex_lock(&sem); \r
- ret = tcc_bt_power_control(rate);\r
- mutex_unlock(&sem); \r
- break;\r
- } \r
- \r
- case IOCTL_BT_SET_EINT:\r
- {\r
- if (copy_from_user(&rate, argp, sizeof(rate)))\r
- return -EFAULT;\r
- printk("[## BT ##] IOCTL_BT_SET_EINT cmd[%d].\n", cmd);\r
- mutex_lock(&sem); \r
-\r
- if(rate)\r
- {\r
- rda_bt_enable_irq();\r
- }\r
- else\r
- {\r
- rda_bt_disable_irq();\r
- eint_mask = 1;\r
- wake_up_interruptible(&eint_wait);\r
- } \r
- mutex_unlock(&sem); \r
- ret = 0;\r
- break;\r
- }\r
- \r
- default :\r
- {\r
- printk("[## BT ##] tcc_bt_dev_ioctl cmd[%d].\n", cmd);\r
- break;\r
- }\r
- }\r
-\r
- return ret;\r
-}\r
-\r
-\r
-struct file_operations tcc_bt_dev_ops = \r
-{\r
- .owner = THIS_MODULE,\r
- .unlocked_ioctl = tcc_bt_dev_ioctl,\r
- .open = tcc_bt_dev_open,\r
- .release = tcc_bt_dev_release,\r
- .poll = tcc_bt_dev_poll,\r
-};\r
-\r
-#ifdef HOST_WAKE_DELAY\r
-struct wake_lock rda_bt_wakelock;\r
-#endif\r
-\r
-static irqreturn_t rda_bt_host_wake_irq(int irq, void *dev)\r
-{\r
- printk("rda_bt_host_wake_irq.\n");\r
- rda_bt_disable_irq();
-
-#ifdef HOST_WAKE_DELAY\r
- wake_lock_timeout(&rda_bt_wakelock, 3 * HZ); \r
-#endif
-
-#if 0 //CONFIG_BT_HCIUART
- if(rda_bt_workqueue)
- queue_work(rda_bt_workqueue, &rda_bt_event_work);\r
-#else\r
- /* Maybe handle the interrupt in user space? */\r
- eint_gen = 1;\r
- wake_up_interruptible(&eint_wait);\r
- /* Send host wakeup command in user space, enable irq then */\r
-#endif
-
- return IRQ_HANDLED;
-}
-
-static int tcc_bt_probe(struct platform_device *pdev)\r
-{\r
- int ret;\r
-\r
- struct tcc_bt_platform_data *pdata = pdev->dev.platform_data;\r
- \r
- printk("tcc_bt_probe.\n");\r
- if(pdata == NULL) {\r
- printk("tcc_bt_probe failed.\n");\r
- return -1;\r
- }\r
- tcc_bt_pdata = pdata;\r
- \r
- if(pdata->power_gpio.io != INVALID_GPIO) {\r
- if (gpio_request(pdata->power_gpio.io, "ldoonpin")){\r
- printk("tcc bt ldoonpin is busy!\n");\r
- return -1;\r
- }\r
- }\r
- gpio_direction_output(pdata->power_gpio.io, !pdata->power_gpio.enable);//GPIO_LOW\r
- \r
-#ifdef HOST_WAKE_DELAY\r
- wake_lock_init(&rda_bt_wakelock, WAKE_LOCK_SUSPEND, "rda_bt_wake");\r
-#endif\r
-\r
- if(pdata->wake_host_gpio.io != INVALID_GPIO) {\r
- if (gpio_request(pdata->wake_host_gpio.io, "tcc_bt_wake")){\r
- printk("tcc bt wakeis busy!\n");\r
- gpio_free(pdata->wake_host_gpio.io);\r
- return -1;\r
- }\r
- } \r
-\r
- gpio_direction_input(pdata->wake_host_gpio.io);\r
- irq_num = gpio_to_irq(pdata->wake_host_gpio.io);\r
- ret = request_irq(irq_num, rda_bt_host_wake_irq, pdata->wake_host_gpio.enable, "tcc_bt_host_wake",NULL); \r
- if(ret < 0)\r
- {\r
- printk("bt_host_wake irq request fail.\n");\r
- irq_num = -1;\r
- goto error;\r
- }\r
-
- enable_irq_wake(irq_num);
- irq_flag = 1;
- rda_bt_disable_irq();
-
- mutex_init(&sem); \r
- printk("[## BT ##] init_module\n");\r
- \r
- ret = register_chrdev(BT_DEV_MAJOR_NUM, DEV_NAME, &tcc_bt_dev_ops);\r
- if(ret < 0)\r
- {\r
- printk("[## BT ##] [%d]fail to register the character device.\n", ret);\r
- goto error;\r
- }\r
- \r
- bt_dev_class = class_create(THIS_MODULE, DEV_NAME);\r
- if (IS_ERR(bt_dev_class)) \r
- {\r
- printk("BT RDA class_create failed\n");\r
- goto error;\r
- }\r
- \r
- device_create(bt_dev_class, NULL, MKDEV(BT_DEV_MAJOR_NUM, BT_DEV_MINOR_NUM), NULL, DEV_NAME);\r
-
- init_waitqueue_head(&eint_wait);\r
-
- /*
- INIT_WORK(&rda_bt_event_work, rda_bt_work_fun);\r
- rda_bt_workqueue = create_singlethread_workqueue("rda_bt");
- if (!rda_bt_workqueue)
- {
- printk("create_singlethread_workqueue failed.\n");\r
- ret = -ESRCH;\r
- goto error;\r
- }
- */
-
- return 0;\r
- \r
-error:\r
- gpio_free(pdata->power_gpio.io); \r
- gpio_free(pdata->wake_host_gpio.io);\r
- return ret; \r
-}\r
-\r
-static int tcc_bt_remove(struct platform_device *pdev)\r
-{\r
- printk("[## BT ##] cleanup_module.\n");
- free_irq(irq_num, NULL);
- unregister_chrdev(BT_DEV_MAJOR_NUM, DEV_NAME);\r
- if(tcc_bt_pdata)\r
- tcc_bt_pdata = NULL;
-
- cancel_work_sync(&rda_bt_event_work);\r
- //destroy_workqueue(rda_bt_workqueue); \r
- \r
-#ifdef HOST_WAKE_DELAY \r
- wake_lock_destroy(&rda_bt_wakelock);\r
-#endif
-\r
- return 0;\r
-}\r
-\r
-static struct platform_driver tcc_bt_driver = {\r
- .probe = tcc_bt_probe,\r
- .remove = tcc_bt_remove,\r
- .driver = {\r
- .name = "tcc_bt_dev",\r
- .owner = THIS_MODULE,\r
- },\r
-};\r
-\r
-static int __init tcc_bt_init_module(void)\r
-{\r
- printk("Enter %s\n", __func__);\r
- return platform_driver_register(&tcc_bt_driver);\r
-}\r
-\r
-static void __exit tcc_bt_cleanup_module(void)\r
-{\r
- printk("Enter %s\n", __func__);\r
- platform_driver_unregister(&tcc_bt_driver);\r
-}\r
-\r
-module_init(tcc_bt_init_module);\r
-module_exit(tcc_bt_cleanup_module);\r
-\r
-\r
-MODULE_AUTHOR("Telechips Inc. linux@telechips.com");\r
-MODULE_DESCRIPTION("TCC_BT_DEV");\r
-MODULE_LICENSE("GPL");\r
-\r
-\r