summaryrefslogtreecommitdiff
path: root/drivers/switch
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/switch')
-rw-r--r--drivers/switch/Kconfig17
-rw-r--r--drivers/switch/Makefile5
-rw-r--r--drivers/switch/switch_class.c174
-rw-r--r--drivers/switch/switch_gpio.c172
-rwxr-xr-xdrivers/switch/wmt/Kconfig16
-rwxr-xr-xdrivers/switch/wmt/Makefile1
-rwxr-xr-xdrivers/switch/wmt/wmt_switch.c424
7 files changed, 809 insertions, 0 deletions
diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig
new file mode 100644
index 00000000..b34ad750
--- /dev/null
+++ b/drivers/switch/Kconfig
@@ -0,0 +1,17 @@
+menuconfig SWITCH
+ tristate "Switch class support"
+ help
+ Say Y here to enable switch class support. This allows
+ monitoring switches by userspace via sysfs and uevent.
+
+if SWITCH
+
+config SWITCH_GPIO
+ tristate "GPIO Swith support"
+ depends on GENERIC_GPIO
+ help
+ Say Y here to enable GPIO based switch support.
+
+source "drivers/switch/wmt/Kconfig"
+
+endif # SWITCH
diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile
new file mode 100644
index 00000000..3ea7aa05
--- /dev/null
+++ b/drivers/switch/Makefile
@@ -0,0 +1,5 @@
+# Switch Class Driver
+obj-$(CONFIG_SWITCH) += switch_class.o
+obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o
+
+obj-$(CONFIG_WMT_SWITCH) += wmt/
diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c
new file mode 100644
index 00000000..e05fc259
--- /dev/null
+++ b/drivers/switch/switch_class.c
@@ -0,0 +1,174 @@
+/*
+ * drivers/switch/switch_class.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/switch.h>
+
+struct class *switch_class;
+static atomic_t device_count;
+
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct switch_dev *sdev = (struct switch_dev *)
+ dev_get_drvdata(dev);
+
+ if (sdev->print_state) {
+ int ret = sdev->print_state(sdev, buf);
+ if (ret >= 0)
+ return ret;
+ }
+ return sprintf(buf, "%d\n", sdev->state);
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct switch_dev *sdev = (struct switch_dev *)
+ dev_get_drvdata(dev);
+
+ if (sdev->print_name) {
+ int ret = sdev->print_name(sdev, buf);
+ if (ret >= 0)
+ return ret;
+ }
+ return sprintf(buf, "%s\n", sdev->name);
+}
+
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL);
+static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL);
+
+void switch_set_state(struct switch_dev *sdev, int state)
+{
+ char name_buf[120];
+ char state_buf[120];
+ char *prop_buf;
+ char *envp[3];
+ int env_offset = 0;
+ int length;
+
+ if (sdev->state != state) {
+ sdev->state = state;
+
+ prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+ if (prop_buf) {
+ length = name_show(sdev->dev, NULL, prop_buf);
+ if (length > 0) {
+ if (prop_buf[length - 1] == '\n')
+ prop_buf[length - 1] = 0;
+ snprintf(name_buf, sizeof(name_buf),
+ "SWITCH_NAME=%s", prop_buf);
+ envp[env_offset++] = name_buf;
+ }
+ length = state_show(sdev->dev, NULL, prop_buf);
+ if (length > 0) {
+ if (prop_buf[length - 1] == '\n')
+ prop_buf[length - 1] = 0;
+ snprintf(state_buf, sizeof(state_buf),
+ "SWITCH_STATE=%s", prop_buf);
+ envp[env_offset++] = state_buf;
+ }
+ envp[env_offset] = NULL;
+ kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
+ free_page((unsigned long)prop_buf);
+ } else {
+ printk(KERN_ERR "out of memory in switch_set_state\n");
+ kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(switch_set_state);
+
+static int create_switch_class(void)
+{
+ if (!switch_class) {
+ switch_class = class_create(THIS_MODULE, "switch");
+ if (IS_ERR(switch_class))
+ return PTR_ERR(switch_class);
+ atomic_set(&device_count, 0);
+ }
+
+ return 0;
+}
+
+int switch_dev_register(struct switch_dev *sdev)
+{
+ int ret;
+
+ if (!switch_class) {
+ ret = create_switch_class();
+ if (ret < 0)
+ return ret;
+ }
+
+ sdev->index = atomic_inc_return(&device_count);
+ sdev->dev = device_create(switch_class, NULL,
+ MKDEV(0, sdev->index), NULL, sdev->name);
+ if (IS_ERR(sdev->dev))
+ return PTR_ERR(sdev->dev);
+
+ ret = device_create_file(sdev->dev, &dev_attr_state);
+ if (ret < 0)
+ goto err_create_file_1;
+ ret = device_create_file(sdev->dev, &dev_attr_name);
+ if (ret < 0)
+ goto err_create_file_2;
+
+ dev_set_drvdata(sdev->dev, sdev);
+ sdev->state = 0;
+ return 0;
+
+err_create_file_2:
+ device_remove_file(sdev->dev, &dev_attr_state);
+err_create_file_1:
+ device_destroy(switch_class, MKDEV(0, sdev->index));
+ printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(switch_dev_register);
+
+void switch_dev_unregister(struct switch_dev *sdev)
+{
+ device_remove_file(sdev->dev, &dev_attr_name);
+ device_remove_file(sdev->dev, &dev_attr_state);
+ device_destroy(switch_class, MKDEV(0, sdev->index));
+ dev_set_drvdata(sdev->dev, NULL);
+}
+EXPORT_SYMBOL_GPL(switch_dev_unregister);
+
+static int __init switch_class_init(void)
+{
+ return create_switch_class();
+}
+
+static void __exit switch_class_exit(void)
+{
+ class_destroy(switch_class);
+}
+
+module_init(switch_class_init);
+module_exit(switch_class_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("Switch class driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c
new file mode 100644
index 00000000..7e9faa21
--- /dev/null
+++ b/drivers/switch/switch_gpio.c
@@ -0,0 +1,172 @@
+/*
+ * drivers/switch/switch_gpio.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/switch.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+
+struct gpio_switch_data {
+ struct switch_dev sdev;
+ unsigned gpio;
+ const char *name_on;
+ const char *name_off;
+ const char *state_on;
+ const char *state_off;
+ int irq;
+ struct work_struct work;
+};
+
+static void gpio_switch_work(struct work_struct *work)
+{
+ int state;
+ struct gpio_switch_data *data =
+ container_of(work, struct gpio_switch_data, work);
+
+ state = gpio_get_value(data->gpio);
+ switch_set_state(&data->sdev, state);
+}
+
+static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_switch_data *switch_data =
+ (struct gpio_switch_data *)dev_id;
+
+ schedule_work(&switch_data->work);
+ return IRQ_HANDLED;
+}
+
+static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf)
+{
+ struct gpio_switch_data *switch_data =
+ container_of(sdev, struct gpio_switch_data, sdev);
+ const char *state;
+ if (switch_get_state(sdev))
+ state = switch_data->state_on;
+ else
+ state = switch_data->state_off;
+
+ if (state)
+ return sprintf(buf, "%s\n", state);
+ return -1;
+}
+
+static int gpio_switch_probe(struct platform_device *pdev)
+{
+ struct gpio_switch_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_switch_data *switch_data;
+ int ret = 0;
+
+ if (!pdata)
+ return -EBUSY;
+
+ switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL);
+ if (!switch_data)
+ return -ENOMEM;
+
+ switch_data->sdev.name = pdata->name;
+ switch_data->gpio = pdata->gpio;
+ switch_data->name_on = pdata->name_on;
+ switch_data->name_off = pdata->name_off;
+ switch_data->state_on = pdata->state_on;
+ switch_data->state_off = pdata->state_off;
+ switch_data->sdev.print_state = switch_gpio_print_state;
+
+ ret = switch_dev_register(&switch_data->sdev);
+ if (ret < 0)
+ goto err_switch_dev_register;
+
+ ret = gpio_request(switch_data->gpio, pdev->name);
+ if (ret < 0)
+ goto err_request_gpio;
+
+ ret = gpio_direction_input(switch_data->gpio);
+ if (ret < 0)
+ goto err_set_gpio_input;
+
+ INIT_WORK(&switch_data->work, gpio_switch_work);
+
+ switch_data->irq = gpio_to_irq(switch_data->gpio);
+ if (switch_data->irq < 0) {
+ ret = switch_data->irq;
+ goto err_detect_irq_num_failed;
+ }
+
+ ret = request_irq(switch_data->irq, gpio_irq_handler,
+ IRQF_TRIGGER_LOW, pdev->name, switch_data);
+ if (ret < 0)
+ goto err_request_irq;
+
+ /* Perform initial detection */
+ gpio_switch_work(&switch_data->work);
+
+ return 0;
+
+err_request_irq:
+err_detect_irq_num_failed:
+err_set_gpio_input:
+ gpio_free(switch_data->gpio);
+err_request_gpio:
+ switch_dev_unregister(&switch_data->sdev);
+err_switch_dev_register:
+ kfree(switch_data);
+
+ return ret;
+}
+
+static int __devexit gpio_switch_remove(struct platform_device *pdev)
+{
+ struct gpio_switch_data *switch_data = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&switch_data->work);
+ gpio_free(switch_data->gpio);
+ switch_dev_unregister(&switch_data->sdev);
+ kfree(switch_data);
+
+ return 0;
+}
+
+static struct platform_driver gpio_switch_driver = {
+ .probe = gpio_switch_probe,
+ .remove = __devexit_p(gpio_switch_remove),
+ .driver = {
+ .name = "switch-gpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gpio_switch_init(void)
+{
+ return platform_driver_register(&gpio_switch_driver);
+}
+
+static void __exit gpio_switch_exit(void)
+{
+ platform_driver_unregister(&gpio_switch_driver);
+}
+
+module_init(gpio_switch_init);
+module_exit(gpio_switch_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("GPIO Switch driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/switch/wmt/Kconfig b/drivers/switch/wmt/Kconfig
new file mode 100755
index 00000000..2495aa26
--- /dev/null
+++ b/drivers/switch/wmt/Kconfig
@@ -0,0 +1,16 @@
+#
+# ZET6221 capacity touch screen driver configuration
+#
+config WMT_SWITCH
+ tristate "WMT GPIO Switch Support"
+ depends on ARCH_WMT
+ default y
+ help
+ Say Y here if you have an WMT based board with switch
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wmt_switch
+
diff --git a/drivers/switch/wmt/Makefile b/drivers/switch/wmt/Makefile
new file mode 100755
index 00000000..1ed1858c
--- /dev/null
+++ b/drivers/switch/wmt/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_WMT_SWITCH) += wmt_switch.o
diff --git a/drivers/switch/wmt/wmt_switch.c b/drivers/switch/wmt/wmt_switch.c
new file mode 100755
index 00000000..b4b9c1d1
--- /dev/null
+++ b/drivers/switch/wmt/wmt_switch.c
@@ -0,0 +1,424 @@
+/*++
+ *
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/workqueue.h>
+#include <linux/switch.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/input.h>
+#include <linux/suspend.h>
+
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+
+#define WMT_SWITCH_TIMER_INTERVAL 250 // ms
+#define WMT_WIREDKEY_TIMER_INTERVAL 100 // ms
+
+#define WMT_WIREDKEY_PLUGIN_DEBOUNCE 2000 // ms
+#define WMT_WIREDKEY_PRESS_COUNT 1
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+struct wmt_switch_priv {
+ struct work_struct switch_work;
+ struct timer_list switch_timer;
+
+ struct input_dev *idev;
+ struct work_struct wiredkey_work;
+ struct timer_list wiredkey_timer;
+ struct notifier_block pm_notifier;
+
+ int prepare_suspend;
+};
+
+struct wmt_switch {
+ struct list_head list;
+ int gpio;
+ int active;
+ int state;
+ struct switch_dev switch_dev;
+};
+
+struct wmt_wiredkey {
+ struct list_head list;
+ int gpio;
+ int active;
+ unsigned int keycode;
+ int count;
+};
+
+static LIST_HEAD(wmt_switch_list);
+static LIST_HEAD(wmt_wiredkey_list);
+
+static void wmt_switch_do_work(struct work_struct *work)
+{
+ struct wmt_switch_priv *priv = container_of(work, struct wmt_switch_priv, switch_work);
+ struct wmt_switch *wmt_switch;
+ int new;
+
+ list_for_each_entry(wmt_switch, &wmt_switch_list, list) {
+ new = gpio_get_value(wmt_switch->gpio);
+ if (new != wmt_switch->state) {
+ if (new == wmt_switch->active)
+ switch_set_state(&wmt_switch->switch_dev, 1);
+ else
+ switch_set_state(&wmt_switch->switch_dev, 0);
+
+ wmt_switch->state = new;
+
+ if (!list_empty(&wmt_wiredkey_list) && !strcmp(wmt_switch->switch_dev.name, "h2w")) {
+ if (wmt_switch->switch_dev.state > 0) {
+ // headset plugin, start hook-key detect in 2s for debounce
+ mod_timer(&priv->wiredkey_timer, jiffies + msecs_to_jiffies(WMT_WIREDKEY_PLUGIN_DEBOUNCE));
+ }
+ else {
+ // headset plugout, stop hook-key detect
+ del_timer_sync(&priv->wiredkey_timer);
+ }
+ }
+ }
+ }
+}
+
+static void wmt_switch_timer_handler(unsigned long data)
+{
+ struct wmt_switch_priv *priv = (struct wmt_switch_priv *)data;
+ schedule_work(&priv->switch_work);
+ mod_timer(&priv->switch_timer, jiffies + msecs_to_jiffies(WMT_SWITCH_TIMER_INTERVAL));
+}
+
+static void wmt_wiredkey_do_work(struct work_struct *work)
+{
+ struct wmt_switch_priv *priv = container_of(work, struct wmt_switch_priv, wiredkey_work);
+ struct wmt_wiredkey *wmt_wiredkey;
+ int new;
+ if (priv->prepare_suspend)
+ return;
+ list_for_each_entry(wmt_wiredkey, &wmt_wiredkey_list, list) {
+ new = gpio_get_value(wmt_wiredkey->gpio);
+ if (new != wmt_wiredkey->active) {
+ // key release
+ if (wmt_wiredkey->count > WMT_WIREDKEY_PRESS_COUNT) {
+ printk("Wired-key(%d): release\n", wmt_wiredkey->keycode);
+ input_event(priv->idev, EV_KEY, wmt_wiredkey->keycode, 0);
+ input_sync(priv->idev);
+ }
+ wmt_wiredkey->count = 0; // reset press count caused by key-release
+ }
+ else {
+ // key pressed
+ if (wmt_wiredkey->count == WMT_WIREDKEY_PRESS_COUNT) {
+ printk("Wired-key(%d): pressed\n", wmt_wiredkey->keycode);
+ input_event(priv->idev, EV_KEY, wmt_wiredkey->keycode, 1);
+ input_sync(priv->idev);
+ }
+ wmt_wiredkey->count++; // increass press count caused by key-pressed
+ }
+ }
+}
+
+static void wmt_wiredkey_timer_handler(unsigned long data)
+{
+ struct wmt_switch_priv *priv = (struct wmt_switch_priv *)data;
+ schedule_work(&priv->wiredkey_work);
+ mod_timer(&priv->wiredkey_timer, jiffies + msecs_to_jiffies(WMT_WIREDKEY_TIMER_INTERVAL));
+}
+
+static int wmt_switch_pm_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct wmt_switch_priv *priv = container_of(this, struct wmt_switch_priv, pm_notifier);
+ //return 0;//it goes before driver suspend, well system fails to suspend.
+ //our timer can't be added again, so move it to driver suspend. 2013-9-30
+ switch (event) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ break;
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ priv->prepare_suspend = 1;// add 2013-10-21
+ //del_timer_sync(&priv->switch_timer);
+ //del_timer_sync(&priv->wiredkey_timer);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+ return 0;
+}
+
+static int __devinit wmt_switch_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct wmt_switch_priv *priv;
+ struct wmt_switch *wmt_switch, *next;
+ struct wmt_wiredkey *wmt_wiredkey, *next2;
+
+ priv = kzalloc(sizeof(struct wmt_switch_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ list_for_each_entry_safe(wmt_switch, next, &wmt_switch_list, list) {
+ ret = gpio_request(wmt_switch->gpio, wmt_switch->switch_dev.name);
+ if (ret < 0) {
+ printk("Switch(%s): gpio(%d) request failed\n",
+ wmt_switch->switch_dev.name, wmt_switch->gpio);
+ list_del_init(&wmt_switch->list);
+ kfree(wmt_switch);
+ }
+ else {
+ printk("Switch(%s): gpio(%d) request success\n",
+ wmt_switch->switch_dev.name, wmt_switch->gpio);
+ gpio_direction_input(wmt_switch->gpio);
+ wmt_switch->state = -1;
+ switch_dev_register(&wmt_switch->switch_dev);
+ }
+ }
+
+ if (list_empty(&wmt_switch_list)) {
+ kfree(priv);
+ return -EINVAL;
+ }
+
+ list_for_each_entry_safe(wmt_wiredkey, next2, &wmt_wiredkey_list, list) {
+ ret = gpio_request(wmt_wiredkey->gpio, "Wired-key");
+ if (ret < 0) {
+ printk("Wired-key: gpio(%d) request failed\n", wmt_wiredkey->gpio);
+ list_del_init(&wmt_wiredkey->list);
+ kfree(wmt_wiredkey);
+ }
+ else {
+ printk("Wired-key: gpio(%d) request success\n", wmt_wiredkey->gpio);
+ gpio_direction_input(wmt_wiredkey->gpio);
+ wmt_wiredkey->count = 0;
+ }
+ }
+
+ // register input device for wired-key
+ priv->idev = input_allocate_device();
+ if (priv->idev == NULL) {
+ printk(KERN_ERR "Wired-key: input_allocate_device failed\n");
+ return -ENOMEM;
+ }
+ priv->idev->name = "Wired-key";
+ priv->idev->phys = "Wired-key";
+ set_bit(EV_KEY, priv->idev->evbit);
+ list_for_each_entry(wmt_wiredkey, &wmt_wiredkey_list, list) {
+ set_bit(wmt_wiredkey->keycode, priv->idev->keybit);
+ }
+ input_register_device(priv->idev);
+
+ // init wired-key devices
+ INIT_WORK(&priv->wiredkey_work, wmt_wiredkey_do_work);
+ init_timer(&priv->wiredkey_timer);
+ priv->wiredkey_timer.data = (unsigned long)priv;
+ priv->wiredkey_timer.function = wmt_wiredkey_timer_handler;
+
+ // init switch devices
+ INIT_WORK(&priv->switch_work, wmt_switch_do_work);
+ init_timer(&priv->switch_timer);
+ priv->switch_timer.data = (unsigned long)priv;
+ priv->switch_timer.function = wmt_switch_timer_handler;
+ mod_timer(&priv->switch_timer, jiffies + msecs_to_jiffies(WMT_SWITCH_TIMER_INTERVAL));
+
+ // register pm event
+ priv->pm_notifier.notifier_call = wmt_switch_pm_event;
+ register_pm_notifier(&priv->pm_notifier);
+
+ return 0;
+}
+
+static int __devexit wmt_switch_remove(struct platform_device *pdev)
+{
+ struct wmt_switch_priv *priv = platform_get_drvdata(pdev);
+ struct wmt_switch *wmt_switch, *next;
+ struct wmt_wiredkey *wmt_wiredkey, *next2;
+
+ unregister_pm_notifier(&priv->pm_notifier);
+
+ del_timer_sync(&priv->switch_timer);
+ del_timer_sync(&priv->wiredkey_timer);
+
+ list_for_each_entry_safe(wmt_wiredkey, next2, &wmt_wiredkey_list, list) {
+ gpio_free(wmt_wiredkey->gpio);
+ list_del_init(&wmt_wiredkey->list);
+ kfree(wmt_wiredkey);
+ }
+
+ list_for_each_entry_safe(wmt_switch, next, &wmt_switch_list, list) {
+ switch_dev_unregister(&wmt_switch->switch_dev);
+ gpio_free(wmt_switch->gpio);
+ list_del_init(&wmt_switch->list);
+ kfree(wmt_switch);
+ }
+
+ input_unregister_device(priv->idev);
+
+ kfree(priv);
+ return 0;
+}
+
+static int wmt_switch_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct wmt_switch_priv *priv = platform_get_drvdata(pdev);
+ printk("<<<< %s\n", __FUNCTION__);
+ del_timer_sync(&priv->wiredkey_timer);
+ del_timer_sync(&priv->switch_timer);
+ return 0;
+}
+
+static int wmt_switch_resume(struct platform_device *pdev)
+{
+ struct wmt_switch_priv *priv = platform_get_drvdata(pdev);
+ struct wmt_switch *wmt_switch;
+ printk("<<<< %s\n", __FUNCTION__);
+ priv->prepare_suspend = 0;//add 2013-10-21
+
+ mod_timer(&priv->switch_timer, jiffies + msecs_to_jiffies(500));
+
+ // if headset plugin, start hook-key detect
+ list_for_each_entry(wmt_switch, &wmt_switch_list, list) {
+ if (!list_empty(&wmt_wiredkey_list) && !strcmp(wmt_switch->switch_dev.name, "h2w")) {
+ if (wmt_switch->switch_dev.state > 0)
+ mod_timer(&priv->wiredkey_timer, jiffies + msecs_to_jiffies(WMT_WIREDKEY_PLUGIN_DEBOUNCE));
+ }
+
+ if (!strcmp(wmt_switch->switch_dev.name, "rotation") && !wmt_switch->active)
+ {
+ wmt_gpio_setpull(wmt_switch->gpio, WMT_GPIO_PULL_UP);
+ }
+ }
+ return 0;
+}
+
+static struct platform_driver wmt_switch_driver = {
+ .driver = {
+ .name = "wmt-switch",
+ .owner = THIS_MODULE,
+ },
+ .probe = wmt_switch_probe,
+ .remove = __devexit_p(wmt_switch_remove),
+ .suspend = wmt_switch_suspend,
+ .resume = wmt_switch_resume,
+};
+
+static __init int wmt_switch_init(void)
+{
+ int ret, gpio, active, keycode;
+ char buf[128];
+ int len = ARRAY_SIZE(buf);
+ struct wmt_switch *wmt_switch;
+ struct wmt_wiredkey *wmt_wiredkey;
+
+ ret = wmt_getsyspara("wmt.switch.sim", buf, &len);
+ if (ret == 0) {
+ sscanf(buf, "%d:%d", &gpio, &active);
+ if (gpio_is_valid(gpio)) {
+ wmt_switch = kzalloc(sizeof(*wmt_switch), GFP_KERNEL);
+ if (wmt_switch) {
+ wmt_switch->switch_dev.name = "sim";
+ wmt_switch->gpio = gpio;
+ wmt_switch->active = active;
+ list_add_tail(&wmt_switch->list, &wmt_switch_list);
+ }
+ }
+ }
+
+ ret = wmt_getsyspara("wmt.switch.sim2", buf, &len);
+ if (ret == 0) {
+ sscanf(buf, "%d:%d", &gpio, &active);
+ if (gpio_is_valid(gpio)) {
+ wmt_switch = kzalloc(sizeof(*wmt_switch), GFP_KERNEL);
+ if (wmt_switch) {
+ wmt_switch->switch_dev.name = "sim2";
+ wmt_switch->gpio = gpio;
+ wmt_switch->active = active;
+ list_add_tail(&wmt_switch->list, &wmt_switch_list);
+ }
+ }
+ }
+
+ ret = wmt_getsyspara("wmt.switch.hs", buf, &len);
+ if (ret == 0) {
+ sscanf(buf, "%d:%d", &gpio, &active);
+ if (gpio_is_valid(gpio)) {
+ wmt_switch = kzalloc(sizeof(*wmt_switch), GFP_KERNEL);
+ if (wmt_switch) {
+ wmt_switch->switch_dev.name = "h2w";
+ wmt_switch->gpio = gpio;
+ wmt_switch->active = active;
+ list_add_tail(&wmt_switch->list, &wmt_switch_list);
+ }
+ }
+ }
+ //add by rambo 2013-8-28, for lock key.
+ //well also can be used for other ways,so we call emukey.
+ ret = wmt_getsyspara("wmt.switch.rotation", buf, &len);
+ if (ret == 0) {
+ sscanf(buf, "%d:%d", &gpio, &active);
+ if (gpio_is_valid(gpio)) {
+ wmt_switch = kzalloc(sizeof(*wmt_switch), GFP_KERNEL);
+ if (wmt_switch) {
+ wmt_switch->switch_dev.name = "rotation";
+ wmt_switch->gpio = gpio;
+ wmt_switch->active = active;
+ list_add_tail(&wmt_switch->list, &wmt_switch_list);
+ }
+ if (active == 0)
+ wmt_gpio_setpull(gpio, WMT_GPIO_PULL_UP);
+ }
+ }
+
+ ret = wmt_getsyspara("wmt.wirekey.hook", buf, &len);
+ if (ret == 0) {
+ sscanf(buf, "%d:%d:%d", &gpio, &active, &keycode);
+ if (gpio_is_valid(gpio)) {
+ wmt_wiredkey = kzalloc(sizeof(*wmt_wiredkey), GFP_KERNEL);
+ if (wmt_wiredkey) {
+ wmt_wiredkey->gpio = gpio;
+ wmt_wiredkey->active = active;
+ wmt_wiredkey->keycode = keycode;
+ list_add_tail(&wmt_wiredkey->list, &wmt_wiredkey_list);
+ }
+ }
+ }
+
+ return platform_driver_register(&wmt_switch_driver);
+}
+module_init(wmt_switch_init);
+
+static __exit void wmt_switch_exit(void)
+{
+ platform_driver_unregister(&wmt_switch_driver);
+}
+module_exit(wmt_switch_exit);
+
+MODULE_DESCRIPTION("WMT GPIO Switch");
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_LICENSE("GPL");