diff -Naur linux-pristine/drivers/input/misc/Kconfig linux-2.6.3-patched/drivers/input/misc/Kconfig --- linux-pristine/drivers/input/misc/Kconfig 2003-12-18 02:58:48.000000000 +0000 +++ linux-2.6.3-patched/drivers/input/misc/Kconfig 2004-02-21 18:45:14.000000000 +0000 @@ -63,3 +63,9 @@ XT mode like everyone else, so we need our own driver. Furthermore, the GSC PS/2 controller shares IRQ between mouse and keyboard. + +config INPUT_ACER_TABLETPC_BUTTONS + tristate "Acer Tablet PC Buttons support" + depends on INPUT && INPUT_MISC + help + Say Y here if you have an Acer Tablet PC. diff -Naur linux-pristine/drivers/input/misc/Makefile linux-2.6.3-patched/drivers/input/misc/Makefile --- linux-pristine/drivers/input/misc/Makefile 2003-12-18 02:58:57.000000000 +0000 +++ linux-2.6.3-patched/drivers/input/misc/Makefile 2004-02-21 18:45:14.000000000 +0000 @@ -10,3 +10,4 @@ obj-$(CONFIG_INPUT_98SPKR) += 98spkr.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_GSC) += gsc_ps2.o +obj-$(CONFIG_INPUT_ACER_TABLETPC_BUTTONS) += acer_tablet_buttons.o diff -Naur linux-pristine/drivers/input/misc/acer_tablet_buttons.c linux-2.6.3-patched/drivers/input/misc/acer_tablet_buttons.c --- linux-pristine/drivers/input/misc/acer_tablet_buttons.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3-patched/drivers/input/misc/acer_tablet_buttons.c 2004-02-21 18:46:06.000000000 +0000 @@ -0,0 +1,304 @@ +/* + * acer_tablet_buttons.c - Acer Tablet Buttons + * + * + * Copyright (C) 2004 Jon TURNEY + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ACER_TABLET_BUTTONS_VERSION "0.01" +#define ACER_TABLET_BUTTONS_NAME "Acer Tablet PC Buttons Driver" + +MODULE_AUTHOR("Jon TURNEY "); +MODULE_DESCRIPTION(ACER_TABLET_BUTTONS_NAME); +MODULE_LICENSE("GPL"); + +/* + * buttons device data structure + */ +struct acer_tablet_buttons { + unsigned int irq; // h/w resource information + unsigned long ioport; + unsigned long iosize; + unsigned int state; // the current button state + struct input_dev input_dev; // input device +}; + +/* + * The tablet buttons driver declaration + */ +static int acer_tablet_buttons_add(struct acpi_device *device); +static int acer_tablet_buttons_remove(struct acpi_device *device, int type); + +static struct acpi_driver acer_tablet_buttons_driver = { + .name = ACER_TABLET_BUTTONS_NAME, + .class = "", /* ??? */ + .ids = "MSTabletPCButtons", + .ops = { + .add = acer_tablet_buttons_add, + .remove = acer_tablet_buttons_remove, + }, +}; + +/* + On the Acer Tablet PC, the tablet buttons are read via an 8-bit latch, with + an interrupt generated when the state changes (and at other times as well) + + bit key (0=pressed, 1=released) + + 7 unused + 6 unused + 5 unused + 4 page down (down arrow) + 3 security (key) + 2 function (Fn) + 1 page up (up arrow) + 0 enter (circle dot/circle cross) + +*/ + +#define BIT_DOWN 0x10 +#define BIT_SAS 0x08 +#define BIT_FN 0x04 +#define BIT_UP 0x02 +#define BIT_ENTER 0x01 + +/* XXX: additional keycodes move to/reconcile with input.h */ +#define KEY_TABLET_SAS 227 +#define KEY_TABLET_FN 228 +#define KEY_TABLET_UP 229 +#define KEY_TABLET_DOWN 230 +#define KEY_TABLET_ENTER 231 + +static irqreturn_t acpi_buttons_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct acer_tablet_buttons *buttons = dev_id; + + unsigned int state = inb(buttons->ioport); + + /* We seem to get sporadic interrupts where nothing has changed */ + if (state != buttons->state) + { + buttons->state = state; + + /* report event to input core */ + input_report_key(&buttons->input_dev, KEY_TABLET_SAS, !(state & BIT_SAS)); + input_report_key(&buttons->input_dev, KEY_TABLET_FN, !(state & BIT_FN)); + input_report_key(&buttons->input_dev, KEY_TABLET_UP, !(state & BIT_UP)); + input_report_key(&buttons->input_dev, KEY_TABLET_DOWN, !(state & BIT_DOWN)); + input_report_key(&buttons->input_dev, KEY_TABLET_ENTER, !(state & BIT_ENTER)); + + /* XXX: probably not needed; unless we want to recognize pressing multiple buttons simultaneously */ + input_sync(&buttons->input_dev); + } + + /* interrupt is non-shareable, so we should have handled it */ + return IRQ_HANDLED; +} + +/* + Walk ACPI resources + (based on looking at drivers/serial/8250_acpi.c) +*/ + +static acpi_status acpi_buttons_port(struct acer_tablet_buttons *req, + struct acpi_resource_io *io) +{ + printk(KERN_NOTICE "ACPI ioport resource %x length %d\n", io->min_base_address, + io->range_length); + + /* range_length is ignored, we use inb */ + if (io->range_length) { + req->ioport = io->min_base_address; + req->iosize = io->range_length; + } else + printk(KERN_ERR "%s: zero-length IO port range?\n", __FUNCTION__); + return AE_OK; +} + +static acpi_status acpi_buttons_irq(struct acer_tablet_buttons *req, + struct acpi_resource_irq *irq) +{ + printk(KERN_NOTICE "ACPI irq resource %d length %d\n", irq->interrupts[0], + irq->number_of_interrupts); + + if (irq->number_of_interrupts > 0) { + req->irq = irq->interrupts[0]; + } + return AE_OK; +} + + +static acpi_status acpi_buttons_resource(struct acpi_resource *res, void *data) +{ + struct acer_tablet_buttons *buttons = (struct acer_tablet_buttons *) data; + + if (res->id == ACPI_RSTYPE_IO) + return acpi_buttons_port(buttons, &res->data.io); + else if (res->id == ACPI_RSTYPE_IRQ) + return acpi_buttons_irq(buttons, &res->data.irq); + /* ignore any memory, extended_irq resources */ + + return AE_OK; +} + + +static int __init acer_tablet_buttons_add(struct acpi_device *device) +{ + struct acer_tablet_buttons *buttons = NULL; + acpi_status status = AE_OK; + + if (!device) + return(-EINVAL); + + printk(KERN_NOTICE ACER_TABLET_BUTTONS_NAME " version %s\n", + ACER_TABLET_BUTTONS_VERSION); + + buttons = + (struct acer_tablet_buttons *) kmalloc(sizeof(struct acer_tablet_buttons), GFP_KERNEL); + if (!buttons) + return(-ENOMEM); + memset(buttons, 0, sizeof(struct acer_tablet_buttons)); + + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + acpi_buttons_resource, buttons); + if (ACPI_FAILURE(status)) { + kfree(buttons); + return -ENODEV; + } + + if (!buttons->irq || !buttons->ioport) { + printk(KERN_ERR "%s: no irq or port address in %s _CRS\n", + __FUNCTION__, device->pnp.bus_id); + kfree(buttons); + return -ENODEV; + } + else { + printk(KERN_NOTICE "irq %d, ioport %lx\n", buttons->irq, buttons->ioport); + } + + /* allocate h/w resources */ + if (request_irq(buttons->irq, acpi_buttons_interrupt, SA_SAMPLE_RANDOM, + "tabletbuttons", buttons)) + { + printk(KERN_ERR "%s: Can't allocate irq %d\n", __FUNCTION__, buttons->irq); + kfree(buttons); + return -EBUSY; + } + + if (!request_region(buttons->ioport, buttons->iosize, "tabletbuttons")) + { + free_irq(buttons->irq, buttons); + printk(KERN_ERR "%s: Can't allocate ioport %ld\n", __FUNCTION__, buttons->ioport); + kfree(buttons); + return -EBUSY; + } + + /* Stash a pointer to our local driver data in the ACPI device */ + acpi_driver_data(device) = buttons; + + /* Register as input device */ + buttons->input_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + + set_bit(KEY_TABLET_SAS, buttons->input_dev.keybit); + set_bit(KEY_TABLET_FN, buttons->input_dev.keybit); + set_bit(KEY_TABLET_UP, buttons->input_dev.keybit); + set_bit(KEY_TABLET_DOWN, buttons->input_dev.keybit); + set_bit(KEY_TABLET_ENTER, buttons->input_dev.keybit); + + buttons->input_dev.name = ACER_TABLET_BUTTONS_NAME; + /* Hmm.. work out what to put here... */ + /* buttons->input_dev.id = ; */ + /* buttons->input_dev.phys = ; */ + + input_register_device(&buttons->input_dev); + + printk(KERN_INFO "tabletbuttons: registered input device\n"); + + /* + Do some error handling + + + */ + + return(0); +} + + +static int acer_tablet_buttons_remove(struct acpi_device *device, int type) +{ + struct acer_tablet_buttons *buttons = NULL; + + if (!device || !acpi_driver_data(device)) + return(-EINVAL); + + buttons = (struct acer_tablet_buttons *) acpi_driver_data(device); + + /* release h/w resources */ + free_irq(buttons->irq, buttons); + release_region(buttons->ioport, buttons->iosize); + + input_unregister_device(&buttons->input_dev); + + kfree(buttons); + + return(0); +} + + + + +static int __init acer_tablet_buttons_init(void) +{ + int result; + + result = acpi_bus_register_driver(&acer_tablet_buttons_driver); + + if (result < 0) { + return(-ENODEV); + } + + return(0); +} + + + +static void __exit acer_tablet_buttons_exit(void) +{ + acpi_bus_unregister_driver(&acer_tablet_buttons_driver); + return; +} + +module_init(acer_tablet_buttons_init); +module_exit(acer_tablet_buttons_exit); + +/* + * Local Variables: + * c-file-style: "linux" + * End: + */