diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 28c19d8dd579..1b12af4d009d 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -35,6 +35,7 @@ config RFKILL_GPIO config RFKILL_RK tristate "Rockchip RFKILL driver" + select INPUT depends on RFKILL depends on MMC depends on ARCH_ROCKCHIP diff --git a/net/rfkill/rfkill-bt.c b/net/rfkill/rfkill-bt.c index 73b802c7faa4..e76de540804c 100644 --- a/net/rfkill/rfkill-bt.c +++ b/net/rfkill/rfkill-bt.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -69,9 +70,11 @@ struct rfkill_rk_data { struct wake_lock bt_irq_wl; struct delayed_work bt_sleep_delay_work; int irq_req; + bool enable_power_key; }; static struct rfkill_rk_data *g_rfkill = NULL; +static struct input_dev *power_key_dev; static const char bt_name[] = #if defined(CONFIG_BCM4330) @@ -113,6 +116,20 @@ static const char bt_name[] = #endif ; +static int rfkill_rk_power_key_up(void) +{ + if (!power_key_dev) + return -ENODEV; + + input_report_key(power_key_dev, KEY_POWER, 1); + input_sync(power_key_dev); + msleep(20); + input_report_key(power_key_dev, KEY_POWER, 0); + input_sync(power_key_dev); + + return 0; +} + static irqreturn_t rfkill_rk_wake_host_irq(int irq, void *dev) { struct rfkill_rk_data *rfkill = dev; @@ -124,6 +141,16 @@ static irqreturn_t rfkill_rk_wake_host_irq(int irq, void *dev) wake_lock_timeout(&rfkill->bt_irq_wl, msecs_to_jiffies(BT_IRQ_WAKELOCK_TIMEOUT)); + if (rfkill->enable_power_key) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static irqreturn_t rfkill_rk_wake_host_irq_thread(int irq, void *dev) +{ + rfkill_rk_power_key_up(); + return IRQ_HANDLED; } @@ -165,11 +192,12 @@ static int rfkill_rk_setup_wake_irq(struct rfkill_rk_data *rfkill, int flag) LOG("Request irq for bt wakeup host\n"); irq->irq = gpio_to_irq(irq->gpio.io); sprintf(irq->name, "%s_irq", irq->gpio.name); - ret = request_irq(irq->irq, rfkill_rk_wake_host_irq, - (irq->gpio.enable == GPIO_ACTIVE_LOW) ? - IRQF_TRIGGER_FALLING : - IRQF_TRIGGER_RISING, - irq->name, rfkill); + ret = request_threaded_irq(irq->irq, rfkill_rk_wake_host_irq, + rfkill_rk_wake_host_irq_thread, + IRQF_ONESHOT | ((irq->gpio.enable == GPIO_ACTIVE_LOW) ? + IRQF_TRIGGER_FALLING : + IRQF_TRIGGER_RISING), + irq->name, rfkill); if (ret) goto fail2; rfkill->irq_req = 1; @@ -488,7 +516,6 @@ static ssize_t bluesleep_write_proc_btwrite(struct file *file, return -EFAULT; DBG("btwrite %c\n", b); - /* HCI_DEV_WRITE */ if (b != '0') rfkill_rk_sleep_bt(BT_WAKEUP); else @@ -497,6 +524,52 @@ static ssize_t bluesleep_write_proc_btwrite(struct file *file, return count; } +static ssize_t bluesleep_read_proc_powerupkey(struct file *file, + char __user *buffer, size_t count, + loff_t *data) +{ + struct rfkill_rk_data *rfkill = g_rfkill; + char src[2]; + + if (*data >= 1) + return 0; + + if (!rfkill) + return -EFAULT; + + src[0] = rfkill->enable_power_key ? '1' : '0'; + src[1] = '\n'; + if (copy_to_user(buffer, src, 2)) + return -EFAULT; + *data = 1; + + return 2; +} + +static ssize_t bluesleep_write_proc_powerupkey(struct file *file, + const char __user *buffer, + size_t count, loff_t *data) +{ + char b; + struct rfkill_rk_data *rfkill = g_rfkill; + + if (!rfkill) + return -EFAULT; + + if (count < 1) + return -EINVAL; + + if (copy_from_user(&b, buffer, 1)) + return -EFAULT; + + if (b != '0') + rfkill->enable_power_key = true; + else + rfkill->enable_power_key = false; + + return count; +} + #ifdef CONFIG_OF static int bluetooth_platdata_parse_dt(struct device *dev, struct rfkill_rk_platform_data *data) @@ -594,6 +667,37 @@ static const struct proc_ops bluesleep_btwrite = { .proc_write = bluesleep_write_proc_btwrite, }; +static const struct proc_ops bluesleep_powerupkey = { + .proc_read = bluesleep_read_proc_powerupkey, + .proc_write = bluesleep_write_proc_powerupkey, +}; + +static int rfkill_rk_register_power_key(struct device *dev) +{ + int ret = 0; + + /* register input device */ + power_key_dev = devm_input_allocate_device(dev); + if (!power_key_dev) { + LOG("%s: not enough memory for input device\n", __func__); + return -ENOMEM; + } + + power_key_dev->name = "bt-powerkey"; + power_key_dev->id.bustype = BUS_HOST; + + power_key_dev->evbit[0] = BIT_MASK(EV_KEY); + set_bit(KEY_POWER, power_key_dev->keybit); + + ret = input_register_device(power_key_dev); + if (ret) { + LOG("%s: register input device exception, exit\n", __func__); + return -EBUSY; + } + + return ret; +} + static int rfkill_rk_probe(struct platform_device *pdev) { struct rfkill_rk_data *rfkill; @@ -660,6 +764,14 @@ static int rfkill_rk_probe(struct platform_device *pdev) goto fail_alloc; } + /* read/write proc entries */ + ent = proc_create("powerupkey", 0444, sleep_dir, &bluesleep_powerupkey); + if (!ent) { + LOG("Unable to create /proc/%s/powerupkey entry", PROC_DIR); + ret = -ENOMEM; + goto fail_alloc; + } + DBG("init gpio\n"); ret = rfkill_rk_setup_gpio(pdev, &pdata->poweron_gpio, pdata->name, @@ -692,8 +804,10 @@ static int rfkill_rk_probe(struct platform_device *pdev) DBG("setup rfkill\n"); rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, &rfkill_rk_ops, rfkill); - if (!rfkill->rfkill_dev) + if (!rfkill->rfkill_dev) { + ret = -ENOMEM; goto fail_alloc; + } rfkill_init_sw_state(rfkill->rfkill_dev, BT_BLOCKED); rfkill_set_sw_state(rfkill->rfkill_dev, BT_BLOCKED); @@ -720,18 +834,18 @@ static int rfkill_rk_probe(struct platform_device *pdev) LOG("%s device registered.\n", pdata->name); + if (rfkill_rk_register_power_key(&pdev->dev) != 0) + goto fail_rfkill; + return 0; fail_rfkill: rfkill_destroy(rfkill->rfkill_dev); fail_alloc: - - remove_proc_entry("btwrite", sleep_dir); - remove_proc_entry("lpm", sleep_dir); + remove_proc_subtree("bluetooth/sleep", NULL); fail_setup_wake_irq: wake_lock_destroy(&rfkill->bt_irq_wl); fail_gpio: - g_rfkill = NULL; return ret; } @@ -744,6 +858,7 @@ static int rfkill_rk_remove(struct platform_device *pdev) rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); + remove_proc_subtree("bluetooth/sleep", NULL); cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work);