All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH] driver for the "Fujitsu Tablet PC Buttons" device
@ 2009-10-12 12:36 Robert Gerlach
  0 siblings, 0 replies; only message in thread
From: Robert Gerlach @ 2009-10-12 12:36 UTC (permalink / raw)
  To: linux-input

This is a driver for the tablet buttons and the tablet mode switch of  
many Fujitsu tablet PCs. The driver is based on reverse-engineering of  
the Windows driver, I have no specification for this device. 

Signed-off-by: Robert Gerlach <khnz@gmx.de>
---

 drivers/input/misc/fsc_btns.c |  714 ++++++++++++++++++++++++++++++++++++++++++
 drivers/input/misc/Kconfig    |   11 +
 drivers/input/misc/Makefile   |    1 +
 3 files changed, 726 insertions(+)

--- linux/drivers/input/misc/Kconfig
+++ linux/drivers/input/misc/Kconfig
@@ -54,6 +54,17 @@
 	 To compile this driver as a module, choose M here: the module will
 	 be called apanel.
 
+config INPUT_FSC_BTNS
+	tristate "Fujitsu tablet PCs buttons"
+	depends on X86
+	help
+	 Say Y here for support of the tablet buttons and the display
+	 orientation switch, used on many Fujitsu tablet PCs (like
+	 Stylistic ST5xxx, Lifebook P1xxx and Lifebook T-Series).
+
+	 To compile this driver as a module, choose M here: the module will
+	 be called fsc_btns.
+
 config INPUT_IXP4XX_BEEPER
 	tristate "IXP4XX Beeper support"
 	depends on ARCH_IXP4XX

--- linux/drivers/input/misc/fsc_btns.c
+++ linux/drivers/input/misc/fsc_btns.c
@@ -0,0 +1,714 @@
+/* driver for FSC tablet PC buttons
+ *
+ * Copyright (C) 2006-2009 Robert Gerlach <khnz@users.sourceforge.net>
+ * Copyright (C) 2005-2006 Jan Rychter <jan@rychter.com>
+ *
+ * You can redistribute and/or modify this program under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* #define DEBUG */
+
+#define REPEAT_RATE 16
+#define REPEAT_DELAY 700
+#define STICKY_TIMEOUT 1400
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/dmi.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+
+#define MODULENAME "fsc_btns"
+
+MODULE_AUTHOR("Robert Gerlach <khnz@users.sourceforge.net>");
+MODULE_DESCRIPTION("Fujitsu Siemens tablet button driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.1.0");
+
+static const struct acpi_device_id fscbtns_ids[] = {
+	{ .id = "FUJ02BD" },
+	{ .id = "FUJ02BF" },
+	{ .id = "" }
+};
+MODULE_DEVICE_TABLE(acpi, fscbtns_ids);
+
+#if defined(STICKY_TIMEOUT) && (STICKY_TIMEOUT > 0)
+static const unsigned long modification_mask[BITS_TO_LONGS(KEY_MAX)] = {
+		[BIT_WORD(KEY_LEFTSHIFT)]	= BIT_MASK(KEY_LEFTSHIFT),
+		[BIT_WORD(KEY_RIGHTSHIFT)]	= BIT_MASK(KEY_RIGHTSHIFT),
+		[BIT_WORD(KEY_LEFTCTRL)]	= BIT_MASK(KEY_LEFTCTRL),
+		[BIT_WORD(KEY_RIGHTCTRL)]	= BIT_MASK(KEY_RIGHTCTRL),
+		[BIT_WORD(KEY_LEFTALT)]		= BIT_MASK(KEY_LEFTALT),
+		[BIT_WORD(KEY_RIGHTALT)]	= BIT_MASK(KEY_RIGHTALT),
+		[BIT_WORD(KEY_LEFTMETA)]	= BIT_MASK(KEY_LEFTMETA),
+		[BIT_WORD(KEY_RIGHTMETA)]	= BIT_MASK(KEY_RIGHTMETA),
+		[BIT_WORD(KEY_COMPOSE)]		= BIT_MASK(KEY_COMPOSE),
+		[BIT_WORD(KEY_LEFTALT)]		= BIT_MASK(KEY_LEFTALT),
+		[BIT_WORD(KEY_FN)]		= BIT_MASK(KEY_FN)};
+#endif
+
+struct fscbtns_config {
+	int invert_orientation_bit;
+	unsigned int keymap[16];
+};
+
+static struct fscbtns_config config_Lifebook_Tseries __initdata = {
+	.invert_orientation_bit = 1,
+	.keymap = {
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_SCROLLDOWN,
+		KEY_SCROLLUP,
+		KEY_DIRECTION,
+		KEY_LEFTCTRL,
+		KEY_BRIGHTNESSUP,
+		KEY_BRIGHTNESSDOWN,
+		KEY_BRIGHTNESS_ZERO,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_LEFTALT
+	}
+};
+
+static struct fscbtns_config config_Lifebook_U810 __initdata = {
+	.invert_orientation_bit = 1,
+	.keymap = {
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_PROG1,
+		KEY_PROG2,
+		KEY_DIRECTION,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_UP,
+		KEY_DOWN,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_FN,
+		KEY_SLEEP
+	}
+};
+
+static struct fscbtns_config config_Stylistic_Tseries __initdata = {
+	.invert_orientation_bit = 0,
+	.keymap = {
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_PRINT,
+		KEY_BACKSPACE,
+		KEY_SPACE,
+		KEY_ENTER,
+		KEY_BRIGHTNESSUP,
+		KEY_BRIGHTNESSDOWN,
+		KEY_DOWN,
+		KEY_UP,
+		KEY_SCROLLUP,
+		KEY_SCROLLDOWN,
+		KEY_FN
+	}
+};
+
+static struct fscbtns_config config_Stylistic_ST5xxx __initdata = {
+	.invert_orientation_bit = 0,
+	.keymap = {
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_RESERVED,
+		KEY_MAIL,
+		KEY_DIRECTION,
+		KEY_ESC,
+		KEY_ENTER,
+		KEY_BRIGHTNESSUP,
+		KEY_BRIGHTNESSDOWN,
+		KEY_DOWN,
+		KEY_UP,
+		KEY_SCROLLUP,
+		KEY_SCROLLDOWN,
+		KEY_FN,
+		KEY_LEFTALT
+	}
+};
+
+static struct {						/* fscbtns_t */
+	struct platform_device *pdev;
+	struct input_dev *idev_b;		/* tablet buttons */
+	struct input_dev *idev_s;		/* orientation switch */
+#if (defined(STICKY_TIMEOUT) && (STICKY_TIMEOUT > 0))
+	struct timer_list timer;
+#endif
+
+	unsigned int interrupt;
+	unsigned int address;
+	struct fscbtns_config config;
+
+	int orientation;
+} fscbtns;
+
+static unsigned int user_model;
+module_param_named(model, user_model, uint, 0);
+MODULE_PARM_DESC(model, "model (1 = Stylistic, 2 = Lifebook T- and P-Series, 3 = Stylistic ST5xxx, 4 = Lifebook U800)");
+
+
+/*** HELPER *******************************************************************/
+
+static inline u8 fscbtns_ack(void)
+{
+	return inb(fscbtns.address+2);
+}
+
+static inline u8 fscbtns_status(void)
+{
+	return inb(fscbtns.address+6);
+}
+
+static inline u8 fscbtns_read_register(const u8 addr)
+{
+	outb(addr, fscbtns.address);
+	return inb(fscbtns.address+4);
+}
+
+static inline void fscbtns_use_config(struct fscbtns_config *config)
+{
+	memcpy(&fscbtns.config, config, sizeof(struct fscbtns_config));
+}
+
+
+/*** INPUT ********************************************************************/
+
+static int __devinit input_fscbtns_setup_buttons(struct device *dev)
+{
+	struct input_dev *idev;
+	int error;
+	int x;
+
+	idev = input_allocate_device();
+	if (!idev)
+		return -ENOMEM;
+
+	idev->dev.parent = dev;
+	idev->phys = "fsc/input0";
+	idev->name = "fsc tablet buttons";
+	idev->id.bustype = BUS_HOST;
+	idev->id.vendor  = 0x1734;
+	idev->id.product = 0x0001;
+	idev->id.version = 0x0101;
+
+	idev->keycode = fscbtns.config.keymap;
+	idev->keycodesize = sizeof(unsigned int);
+	idev->keycodemax = sizeof(fscbtns.config.keymap) / idev->keycodesize;
+
+	set_bit(EV_REP, idev->evbit);
+	set_bit(EV_KEY, idev->evbit);
+
+	for (x = 0; x < idev->keycodemax; x++)
+		if (((unsigned int *)idev->keycode)[x])
+			set_bit(((unsigned int *)idev->keycode)[x],
+					idev->keybit);
+
+	set_bit(EV_MSC, idev->evbit);
+	set_bit(MSC_SCAN, idev->mscbit);
+
+	error = input_register_device(idev);
+	if (error) {
+		input_free_device(idev);
+		return error;
+	}
+
+	idev->rep[REP_DELAY]  = REPEAT_DELAY;
+	idev->rep[REP_PERIOD] = 1000 / REPEAT_RATE;
+
+	fscbtns.idev_b = idev;
+	return 0;
+}
+
+static int __devinit input_fscbtns_setup_switch(struct device *dev)
+{
+	struct input_dev *idev;
+	int error;
+
+	idev = input_allocate_device();
+	if (!idev)
+		return -ENOMEM;
+
+	idev->dev.parent = dev;
+	idev->phys = "fsc/input1";
+	idev->name = "fsc tablet switch";
+	idev->id.bustype = BUS_HOST;
+	idev->id.vendor  = 0x1734;
+	idev->id.product = 0x0002;
+	idev->id.version = 0x0101;
+
+	set_bit(EV_SW, idev->evbit);
+	set_bit(SW_TABLET_MODE, idev->swbit);
+
+	error = input_register_device(idev);
+	if (error) {
+		input_free_device(idev);
+		return error;
+	}
+
+	fscbtns.idev_s = idev;
+	return 0;
+}
+
+static void input_fscbtns_remove(void)
+{
+	if (fscbtns.idev_b)
+		input_unregister_device(fscbtns.idev_b);
+	if (fscbtns.idev_s)
+		input_unregister_device(fscbtns.idev_s);
+}
+
+static void fscbtns_report_orientation(void)
+{
+	int orientation = fscbtns_read_register(0xdd);
+
+	if (orientation & 0x02) {
+		orientation ^= fscbtns.config.invert_orientation_bit;
+		orientation &= 0x01;
+
+		if (orientation != fscbtns.orientation) {
+			input_report_switch(fscbtns.idev_s, SW_TABLET_MODE,
+					fscbtns.orientation = orientation);
+			input_sync(fscbtns.idev_s);
+		}
+	}
+}
+
+#if defined(STICKY_TIMEOUT) && (STICKY_TIMEOUT > 0)
+static void fscbtns_sticky_timeout(unsigned long keycode)
+{
+	input_report_key(fscbtns.idev_b, keycode, 0);
+	input_sync(fscbtns.idev_b);
+	fscbtns.timer.data = 0;
+}
+
+static inline int fscbtns_sticky_report_key(unsigned int keycode, int pressed)
+{
+	if (pressed) {
+		del_timer(&fscbtns.timer);
+		fscbtns.timer.expires = jiffies + (STICKY_TIMEOUT*HZ)/1000;
+
+		if (fscbtns.timer.data == keycode) {
+			input_report_key(fscbtns.idev_b, keycode, 0);
+			input_sync(fscbtns.idev_b);
+		}
+
+		return 0;
+	}
+
+	if ((fscbtns.timer.data) && (fscbtns.timer.data != keycode)) {
+		input_report_key(fscbtns.idev_b, keycode, 0);
+		input_sync(fscbtns.idev_b);
+		input_report_key(fscbtns.idev_b, fscbtns.timer.data, 0);
+		fscbtns.timer.data = 0;
+		return 1;
+	}
+
+	if (test_bit(keycode, modification_mask) &&
+			(fscbtns.timer.expires > jiffies)) {
+		fscbtns.timer.data = keycode;
+		fscbtns.timer.function = fscbtns_sticky_timeout;
+		add_timer(&fscbtns.timer);
+		return 1;
+	}
+
+	return 0;
+}
+#endif
+
+static void fscbtns_report_key(unsigned int kmindex, int pressed)
+{
+	unsigned int keycode = fscbtns.config.keymap[kmindex];
+	if (keycode == KEY_RESERVED)
+		return;
+
+	if (pressed)
+		input_event(fscbtns.idev_b, EV_MSC, MSC_SCAN, kmindex);
+
+#if defined(STICKY_TIMEOUT) && (STICKY_TIMEOUT > 0)
+	if (fscbtns_sticky_report_key(keycode, pressed))
+		return;
+#endif
+
+	input_report_key(fscbtns.idev_b, keycode, pressed);
+	input_sync(fscbtns.idev_b);
+}
+
+static void fscbtns_event(void)
+{
+	unsigned long keymask;
+	unsigned long changed;
+	static unsigned long prev_keymask;
+
+	fscbtns_report_orientation();
+
+	keymask  = fscbtns_read_register(0xde);
+	keymask |= fscbtns_read_register(0xdf) << 8;
+	keymask ^= 0xffff;
+
+	changed = keymask ^ prev_keymask;
+
+	if (changed) {
+		int x = 0;
+		int pressed = !!(keymask & changed);
+
+		/* save current state and filter not changed bits */
+		prev_keymask = keymask;
+
+		/* get number of changed bit */
+		while (!test_bit(x, &changed))
+			x++;
+
+		fscbtns_report_key(x, pressed);
+	}
+}
+
+
+/*** INTERRUPT ****************************************************************/
+
+static void fscbtns_isr_do(struct work_struct *work)
+{
+	fscbtns_event();
+	fscbtns_ack();
+}
+
+static DECLARE_WORK(isr_wq, fscbtns_isr_do);
+
+static irqreturn_t fscbtns_isr(int irq, void *dev_id)
+{
+	if (!(fscbtns_status() & 0x01))
+		return IRQ_NONE;
+
+	schedule_work(&isr_wq);
+	return IRQ_HANDLED;
+}
+
+
+/*** DEVICE *******************************************************************/
+
+static int fscbtns_busywait(void)
+{
+	int timeout_counter = 100;
+
+	while (fscbtns_status() & 0x02 && --timeout_counter)
+		msleep(10);
+
+	return !timeout_counter;
+}
+
+static void fscbtns_reset(void)
+{
+	fscbtns_ack();
+	if (fscbtns_busywait())
+		printk(KERN_WARNING MODULENAME ": timeout, reset needed!\n");
+}
+
+static int __devinit fscbtns_probe(struct platform_device *pdev)
+{
+	int error;
+
+	error = input_fscbtns_setup_buttons(&pdev->dev);
+	if (error)
+		goto err_input;
+
+	error = input_fscbtns_setup_switch(&pdev->dev);
+	if (error)
+		goto err_input;
+
+	if (!request_region(fscbtns.address, 8, MODULENAME)) {
+		printk(KERN_ERR MODULENAME ": region 0x%04x busy\n",
+				fscbtns.address);
+		error = -EBUSY;
+		goto err_input;
+	}
+
+	fscbtns_reset();
+
+	fscbtns_report_orientation();
+	input_sync(fscbtns.idev_b);
+
+	error = request_irq(fscbtns.interrupt, fscbtns_isr,
+			IRQF_SHARED, MODULENAME, fscbtns_isr);
+	if (error) {
+		printk(KERN_ERR MODULENAME ": unable to get irq %d\n",
+				fscbtns.interrupt);
+		goto err_io;
+	}
+
+	return 0;
+
+err_io:
+	release_region(fscbtns.address, 8);
+err_input:
+	input_fscbtns_remove();
+	return error;
+}
+
+static int __devexit fscbtns_remove(struct platform_device *pdev)
+{
+	free_irq(fscbtns.interrupt, fscbtns_isr);
+	release_region(fscbtns.address, 8);
+	input_fscbtns_remove();
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int fscbtns_resume(struct platform_device *pdev)
+{
+	fscbtns_reset();
+#if 0 /* because Xorg Bug #9623 */
+	fscbtns_report_orientation();
+#endif
+	return 0;
+}
+#endif
+
+static struct platform_driver fscbtns_platform_driver = {
+	.driver		= {
+		.name	= MODULENAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= fscbtns_probe,
+	.remove		= __devexit_p(fscbtns_remove),
+#ifdef CONFIG_PM
+	.resume		= fscbtns_resume,
+#endif
+};
+
+
+/*** ACPI *********************************************************************/
+
+static acpi_status fscbtns_walk_resources(struct acpi_resource *res, void *data)
+{
+	switch (res->type) {
+	case ACPI_RESOURCE_TYPE_IRQ:
+		fscbtns.interrupt = res->data.irq.interrupts[0];
+		return AE_OK;
+
+	case ACPI_RESOURCE_TYPE_IO:
+		fscbtns.address = res->data.io.minimum;
+		return AE_OK;
+
+	case ACPI_RESOURCE_TYPE_END_TAG:
+		if (fscbtns.interrupt && fscbtns.address)
+			return AE_OK;
+		else
+			return AE_NOT_FOUND;
+
+	default:
+		return AE_ERROR;
+	}
+}
+
+static int acpi_fscbtns_add(struct acpi_device *adev)
+{
+	acpi_status status;
+
+	if (!adev)
+		return -EINVAL;
+
+	status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+			fscbtns_walk_resources, NULL);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	return 0;
+}
+
+static struct acpi_driver acpi_fscbtns_driver = {
+	.name  = MODULENAME,
+	.class = "hotkey",
+	.ids   = fscbtns_ids,
+	.ops   = {
+		.add    = acpi_fscbtns_add
+	}
+};
+
+
+/*** DMI **********************************************************************/
+
+static int __init fscbtns_dmi_matched(const struct dmi_system_id *dmi)
+{
+	printk(KERN_INFO MODULENAME ": found: %s\n", dmi->ident);
+	fscbtns_use_config(dmi->driver_data);
+	return 1;
+}
+
+static struct dmi_system_id dmi_ids[] __initdata = {
+	{
+		.callback = fscbtns_dmi_matched,
+		.ident = "Fujitsu Siemens P/T Series",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK")
+		},
+		.driver_data = &config_Lifebook_Tseries
+	},
+	{
+		.callback = fscbtns_dmi_matched,
+		.ident = "Fujitsu Lifebook T Series",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T")
+		},
+		.driver_data = &config_Lifebook_Tseries
+	},
+	{
+		.callback = fscbtns_dmi_matched,
+		.ident = "Fujitsu Siemens Stylistic T Series",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T")
+		},
+		.driver_data = &config_Stylistic_Tseries
+	},
+	{
+		.callback = fscbtns_dmi_matched,
+		.ident = "Fujitsu LifeBook U810",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810")
+		},
+		.driver_data = &config_Lifebook_U810
+	},
+	{
+		.callback = fscbtns_dmi_matched,
+		.ident = "Fujitsu Siemens Stylistic ST5xxx Series",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5")
+		},
+		.driver_data = &config_Stylistic_ST5xxx
+	},
+	{
+		.callback = fscbtns_dmi_matched,
+		.ident = "Fujitsu Siemens Stylistic ST5xxx Series",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5")
+		},
+		.driver_data = &config_Stylistic_ST5xxx
+	},
+	{
+		.callback = fscbtns_dmi_matched,
+		.ident = "Unknown (using defaults)",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, ""),
+			DMI_MATCH(DMI_PRODUCT_NAME, "")
+		},
+		.driver_data = &config_Lifebook_Tseries
+	},
+	{ NULL }
+};
+
+
+/*** MODULE *******************************************************************/
+
+static int __init fscbtns_module_init(void)
+{
+	int error;
+
+	switch (user_model) {
+	case 1:
+		fscbtns_use_config(&config_Stylistic_Tseries);
+		break;
+	case 2:
+		fscbtns_use_config(&config_Lifebook_Tseries);
+		break;
+	case 3:
+		fscbtns_use_config(&config_Stylistic_ST5xxx);
+		break;
+	case 4:
+		fscbtns_use_config(&config_Lifebook_U810);
+		break;
+	default:
+		dmi_check_system(dmi_ids);
+	}
+
+	error = acpi_bus_register_driver(&acpi_fscbtns_driver);
+	if (ACPI_FAILURE(error))
+		return -EINVAL;
+
+	if (!fscbtns.interrupt || !fscbtns.address)
+		return -ENODEV;
+
+#if defined(STICKY_TIMEOUT) && (STICKY_TIMEOUT > 0)
+	init_timer(&fscbtns.timer);
+#endif
+
+	error = platform_driver_register(&fscbtns_platform_driver);
+	if (error)
+		goto err;
+
+	fscbtns.pdev = platform_device_alloc(MODULENAME, -1);
+	if (!fscbtns.pdev) {
+		error = -ENOMEM;
+		goto err_pdrv;
+	}
+
+	error = platform_device_add(fscbtns.pdev);
+	if (error)
+		goto err_pdev;
+
+	return 0;
+
+err_pdev:
+	platform_device_put(fscbtns.pdev);
+err_pdrv:
+	platform_driver_unregister(&fscbtns_platform_driver);
+err:
+	acpi_bus_unregister_driver(&acpi_fscbtns_driver);
+
+#if (defined(STICKY_TIMEOUT) && (STICKY_TIMEOUT > 0))
+	del_timer_sync(&fscbtns.timer);
+#endif
+	return error;
+}
+
+static void __exit fscbtns_module_exit(void)
+{
+	platform_device_unregister(fscbtns.pdev);
+	platform_driver_unregister(&fscbtns_platform_driver);
+	acpi_bus_unregister_driver(&acpi_fscbtns_driver);
+
+#if (defined(STICKY_TIMEOUT) && (STICKY_TIMEOUT > 0))
+	del_timer_sync(&fscbtns.timer);
+#endif
+}
+
+module_init(fscbtns_module_init);
+module_exit(fscbtns_module_exit);

--- linux/drivers/input/misc/Makefile
+++ linux/drivers/input/misc/Makefile
@@ -5,6 +5,7 @@
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
+obj-$(CONFIG_INPUT_FSC_BTNS)		+= fsc_btns.o
 obj-$(CONFIG_INPUT_ATI_REMOTE)		+= ati_remote.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
 obj-$(CONFIG_INPUT_ATLAS_BTNS)		+= atlas_btns.o

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2009-10-12 12:37 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-12 12:36 [RFC][PATCH] driver for the "Fujitsu Tablet PC Buttons" device Robert Gerlach

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.