1/* 2 * hp-wireless button for Windows 8 3 * 4 * Copyright (C) 2014 Alex Hung <alex.hung@canonical.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 */ 20 21#include <linux/kernel.h> 22#include <linux/module.h> 23#include <linux/init.h> 24#include <linux/input.h> 25#include <linux/platform_device.h> 26#include <linux/acpi.h> 27#include <acpi/acpi_bus.h> 28 29MODULE_LICENSE("GPL"); 30MODULE_AUTHOR("Alex Hung"); 31MODULE_ALIAS("acpi*:HPQ6001:*"); 32 33static struct input_dev *hpwl_input_dev; 34 35static const struct acpi_device_id hpwl_ids[] = { 36 {"HPQ6001", 0}, 37 {"", 0}, 38}; 39 40static int hp_wireless_input_setup(void) 41{ 42 int err; 43 44 hpwl_input_dev = input_allocate_device(); 45 if (!hpwl_input_dev) 46 return -ENOMEM; 47 48 hpwl_input_dev->name = "HP Wireless hotkeys"; 49 hpwl_input_dev->phys = "hpq6001/input0"; 50 hpwl_input_dev->id.bustype = BUS_HOST; 51 hpwl_input_dev->evbit[0] = BIT(EV_KEY); 52 set_bit(KEY_RFKILL, hpwl_input_dev->keybit); 53 54 err = input_register_device(hpwl_input_dev); 55 if (err) 56 goto err_free_dev; 57 58 return 0; 59 60err_free_dev: 61 input_free_device(hpwl_input_dev); 62 return err; 63} 64 65static void hp_wireless_input_destroy(void) 66{ 67 input_unregister_device(hpwl_input_dev); 68} 69 70static void hpwl_notify(struct acpi_device *acpi_dev, u32 event) 71{ 72 if (event != 0x80) { 73 pr_info("Received unknown event (0x%x)\n", event); 74 return; 75 } 76 77 input_report_key(hpwl_input_dev, KEY_RFKILL, 1); 78 input_sync(hpwl_input_dev); 79 input_report_key(hpwl_input_dev, KEY_RFKILL, 0); 80 input_sync(hpwl_input_dev); 81} 82 83static int hpwl_add(struct acpi_device *device) 84{ 85 int err; 86 87 err = hp_wireless_input_setup(); 88 if (err) 89 pr_err("Failed to setup hp wireless hotkeys\n"); 90 91 return err; 92} 93 94static int hpwl_remove(struct acpi_device *device) 95{ 96 hp_wireless_input_destroy(); 97 return 0; 98} 99 100static struct acpi_driver hpwl_driver = { 101 .name = "hp-wireless", 102 .owner = THIS_MODULE, 103 .ids = hpwl_ids, 104 .ops = { 105 .add = hpwl_add, 106 .remove = hpwl_remove, 107 .notify = hpwl_notify, 108 }, 109}; 110 111static int __init hpwl_init(void) 112{ 113 int err; 114 115 pr_info("Initializing HPQ6001 module\n"); 116 err = acpi_bus_register_driver(&hpwl_driver); 117 if (err) { 118 pr_err("Unable to register HP wireless control driver.\n"); 119 goto error_acpi_register; 120 } 121 122 return 0; 123 124error_acpi_register: 125 return err; 126} 127 128static void __exit hpwl_exit(void) 129{ 130 pr_info("Exiting HPQ6001 module\n"); 131 acpi_bus_unregister_driver(&hpwl_driver); 132} 133 134module_init(hpwl_init); 135module_exit(hpwl_exit); 136