From: Mathieu Bérard Date: Fri, 10 Nov 2006 23:22:06 +0000 (+0000) Subject: * Merge (189:HEAD) experimental branch back to trunk X-Git-Url: https://gitweb.devoid-pointer.net/?a=commitdiff_plain;h=727879645c7ba0de11cb6f717bbfb6143f7f7ab7;p=omnibook.git * Merge (189:HEAD) experimental branch back to trunk --- diff --git a/Makefile b/Makefile index dc57aef..71ec838 100644 --- a/Makefile +++ b/Makefile @@ -47,10 +47,10 @@ EXTRA_CFLAGS += -D OMNIBOOK_STANDALONE $(DEBUG) EXTRA_LDFLAGS += $(src)/sections.lds -OBJS = init.o ec.o compal.o acpi.o nbsmi.o \ - ac.o apmemu.o battery.o blank.o bluetooth.o display.o dock.o dump.o \ - fan.o fan_policy.o hotkeys.o info.o lcd.o muteled.o temperature.o \ - touchpad.o wireless.o +OBJS = init.o lib.o ec.o kbc.o pio.o compal.o acpi.o nbsmi.o \ + ac.o battery.o blank.o bluetooth.o display.o dock.o dump.o \ + fan.o fan_policy.o hotkeys.o info.o lcd.o muteled.o polling.o \ + temperature.o touchpad.o wireless.o obj-m += $(MODULE_NAME).o omnibook-objs := $(OBJS) diff --git a/ac.c b/ac.c index 3b2ce96..b3bebc7 100644 --- a/ac.c +++ b/ac.c @@ -16,29 +16,19 @@ */ #include "omnibook.h" -#include "ec.h" - -int omnibook_get_ac(struct omnibook_operation *io_op) -{ - u8 ac; - int retval; - - retval = io_op->backend->byte_read(io_op, &ac); - if (!retval) - retval = !!ac; - return retval; -} +#include "hardware.h" static int omnibook_ac_read(char *buffer, struct omnibook_operation *io_op) { int len = 0; - int ac; + u8 ac; + int retval; - ac = omnibook_get_ac(io_op); - if (ac < 0) - return ac; + retval = backend_byte_read(io_op, &ac); + if (retval < 0) + return retval; - len += sprintf(buffer + len, "AC %s\n", (ac) ? "on-line" : "off-line"); + len += sprintf(buffer + len, "AC %s\n", (!!ac) ? "on-line" : "off-line"); return len; } diff --git a/acpi.c b/acpi.c index 369e4f9..02dd3e6 100644 --- a/acpi.c +++ b/acpi.c @@ -16,7 +16,7 @@ */ #include "omnibook.h" -#include "ec.h" +#include "hardware.h" #ifdef CONFIG_ACPI @@ -79,7 +79,6 @@ static struct acpi_driver omnibook_bt_driver = { struct acpi_backend_data { acpi_handle ec_handle; /* Handle on ACPI EC device */ acpi_handle bt_handle; /* Handle on ACPI BT device */ - struct kref refcount; /* Reference counter of this backend */ }; /* @@ -99,16 +98,16 @@ static int omnibook_acpi_init(const struct omnibook_operation *io_op) } if (!priv_data) { - dprintk("Try to init ACPI backend\n"); - + dprintk("Try to init ACPI backend\n"); + mutex_init(&io_op->backend->mutex); + mutex_lock(&io_op->backend->mutex); + kref_init(&io_op->backend->kref); priv_data = kzalloc(sizeof(struct acpi_backend_data), GFP_KERNEL); if (!priv_data) { retval = -ENOMEM; - goto out; + goto error0; } - kref_init(&priv_data->refcount); - /* Locate ACPI EC device, acpi_get_handle set dev_handle to NULL if not found */ for (i = 0; i < ARRAY_SIZE(ec_dev_list); i++) { if (acpi_get_handle(NULL, ec_dev_list[i], &dev_handle) == AE_OK) { @@ -121,45 +120,46 @@ static int omnibook_acpi_init(const struct omnibook_operation *io_op) if(!dev_handle) { printk(O_ERR "Can't get handle on ACPI EC device.\n"); retval = -ENODEV; - goto err; + goto error1; } - - io_op->backend->data = (void *) priv_data; + io_op->backend->data = (void *) priv_data; + /* attempt to register Toshiba bluetooth ACPI driver */ acpi_bus_register_driver(&omnibook_bt_driver); - + dprintk("ACPI backend init OK\n"); - goto out; + mutex_unlock(&io_op->backend->mutex); + return 0; } else { dprintk("ACPI backend has already been initialized\n"); - kref_get(&priv_data->refcount); + kref_get(&io_op->backend->kref); return 0; } - err: + error1: kfree(priv_data); io_op->backend->data = NULL; - out: + error0: + mutex_unlock(&io_op->backend->mutex); + mutex_destroy(&io_op->backend->mutex); return retval; } static void omnibook_acpi_free(struct kref *ref) { - struct acpi_backend_data *priv_data; - priv_data = container_of(ref, struct acpi_backend_data, refcount); + struct omnibook_backend *backend; + backend = container_of(ref, struct omnibook_backend, kref); dprintk("ACPI backend not used anymore: disposing\n"); acpi_bus_unregister_driver(&omnibook_bt_driver); - kfree(priv_data); + kfree(backend->data); } static void omnibook_acpi_exit(const struct omnibook_operation *io_op) { - struct acpi_backend_data *priv_data; dprintk("Trying to dispose ACPI backend\n"); - priv_data = (struct acpi_backend_data *) io_op->backend->data; - kref_put(&priv_data->refcount, omnibook_acpi_free); + kref_put(&io_op->backend->kref, omnibook_acpi_free); } /* @@ -168,10 +168,11 @@ static void omnibook_acpi_exit(const struct omnibook_operation *io_op) */ static int omnibook_acpi_execute(acpi_handle dev_handle, char *method, const int *param, int *result) { + struct acpi_object_list args_list; struct acpi_buffer buff; union acpi_object arg, out_objs[1]; - + if (param) { args_list.count = 1; args_list.pointer = &arg; @@ -210,6 +211,7 @@ static int omnibook_acpi_bt_add(struct acpi_device *device) dprintk("Enabling found Toshiba Bluetooth ACPI device.\n"); strcpy(acpi_device_name(device), TOSHIBA_ACPI_DEVICE_NAME); strcpy(acpi_device_class(device), TOSHIBA_ACPI_BT_CLASS); + /* Save handle in backend private data structure. ugly. */ priv_data->bt_handle = device->handle; diff --git a/apmemu.c b/apmemu.c deleted file mode 100644 index 7c9a57e..0000000 --- a/apmemu.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * apmemu.c -- /proc/apm emulation - * - * 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, 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. - * - * Written by Soós Péter , 2002-2004 - * Modified by Mathieu Bérard , 2006 - */ - -#include "omnibook.h" - -#ifdef CONFIG_OMNIBOOK_LEGACY - -#ifdef CONFIG_APM -#include -#endif - -#include -#include - -#include "apmemu.h" - -/* Arguments, with symbols from linux/apm_bios.h. Information is - from the Get Power Status (0x0a) call unless otherwise noted. - 0) Linux driver version (this will change if format changes) - 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2. - 2) APM flags from APM Installation Check (0x00): - bit 0: APM_16_BIT_SUPPORT - bit 1: APM_32_BIT_SUPPORT - bit 2: APM_IDLE_SLOWS_CLOCK - bit 3: APM_BIOS_DISABLED - bit 4: APM_BIOS_DISENGAGED - 3) AC line status - 0x00: Off-line - 0x01: On-line - 0x02: On backup power (BIOS >= 1.1 only) - 0xff: Unknown - 4) Battery status - 0x00: High - 0x01: Low - 0x02: Critical - 0x03: Charging - 0x04: Selected battery not present (BIOS >= 1.2 only) - 0xff: Unknown - 5) Battery flag - bit 0: High - bit 1: Low - bit 2: Critical - bit 3: Charging - bit 7: No system battery - 0xff: Unknown - 6) Remaining battery life (percentage of charge): - 0-100: valid - -1: Unknown - 7) Remaining battery life (time units): - Number of remaining minutes or seconds - -1: Unknown - 8) min = minutes; sec = seconds */ - -extern struct omnibook_feature ac_driver; - -static int omnibook_apmemu_read(char *buffer, struct omnibook_operation *io_op) -{ - int retval; - int len = 0; - int ac; - - struct omnibook_battery_state battstat; - - struct apm_features { - char *drvver; - char *apmver; - u8 apmflags; - u8 ac; - u8 battstat; - u8 battflags; - u8 gauge; - int time; - char *units; - }; - - struct apm_features apm = { - APMEMU_DRIVER_VERSION, - APMEMU_APM_VERSION, - APMEMU_32_BIT_SUPPORT | APMEMU_BIOS_DISABLED, - APMEMU_AC_UNKNOWN, - APMEMU_BATTSTAT_UNKN, - 0x00, - APMEMU_BATTLIFE_UNKN, - APMEMU_BATTLIFE_UNKN, - "?" - }; - -/* - * FIXME: Broken, how do we know ac and battery are in a usable state ? - */ - ac = omnibook_get_ac(ac_driver.io_op); - apm.ac = (ac) ? APMEMU_AC_ONLINE : APMEMU_AC_OFFLINE; - /* Asking for Battery 0 as APM does */ - retval = omnibook_get_battery_status(0, &battstat); - if (retval == 0) - apm.gauge = battstat.gauge; - if (apm.gauge >= APMEMU_BATTERY_LOW) { - apm.battflags = apm.battflags | APMEMU_BATTFLAG_HIGH; - apm.battstat = APMEMU_BATTSTAT_HIGH; - } else { - apm.battflags = apm.battflags | APMEMU_BATTFLAG_LOW; - apm.battstat = APMEMU_BATTSTAT_LOW; - } - if (battstat.status == OMNIBOOK_BATTSTAT_CHARGING) { - apm.battflags = apm.battflags | APMEMU_BATTFLAG_CHR; - apm.battstat = APMEMU_BATTSTAT_CHR; - } - if (battstat.status == OMNIBOOK_BATTSTAT_CRITICAL) { - apm.battflags = apm.battflags | APMEMU_BATTFLAG_CRIT; - apm.battstat = APMEMU_BATTSTAT_CRIT; - } - - len += sprintf(buffer + len, "%s %s 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", - apm.drvver, apm.apmver, apm.apmflags, apm.ac, apm.battstat, apm.battflags, - apm.gauge, apm.time, apm.units); - - return len; -} - -static int __init omnibook_apmemu_init(struct omnibook_operation *io_op) -{ -#ifdef CONFIG_APM - if (!apm_info.disabled) { - printk(O_INFO "Real APM support is present, emulation is not necessary.\n"); - return -ENODEV; - } -#endif - return 0; -} - -static struct omnibook_feature __declared_feature apmemu_driver = { - .name = "apmemu", - .proc_entry = "apm", /* create /proc/apm */ - .enabled = 0, /* This feature is broken */ - .read = omnibook_apmemu_read, - .init = omnibook_apmemu_init, - .ectypes = XE3GF | XE3GC | TSP10, -}; - -module_param_named(apmemu, apmemu_driver.enabled, int, S_IRUGO); -MODULE_PARM_DESC(apmemu, "Use 0 to disable, 1 to enable /proc/apm emulation"); - -#endif /* CONFIG_OMNIBOOK_LEGACY */ -/* End of file */ diff --git a/apmemu.h b/apmemu.h deleted file mode 100644 index b12cab5..0000000 --- a/apmemu.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * apmemu.c -- code to emulate /proc/apm - * - * 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, 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. - * - * Written by Soós Péter , 2002-2004 - */ - - -#define APMEMU_DRIVER_VERSION "1.16" -#define APMEMU_APM_VERSION "1.2" - -#define APMEMU_BATTERY_LOW 30 /* Battery low threshold */ - -#define APMEMU_16_BIT_SUPPORT 0x01 /* 16 bit APM BIOS */ -#define APMEMU_32_BIT_SUPPORT 0x02 /* 32 bit APM BIOS */ -#define APMEMU_IDLE_SLOWS_CLOCK 0x04 -#define APMEMU_BIOS_DISABLED 0x08 /* APM BIOS disabled */ -#define APMEMU_BIOS_DISENGAGED 0x10 /* APM BIOS disengaged */ - -#define APMEMU_AC_OFFLINE 0x00 /* AC offline */ -#define APMEMU_AC_ONLINE 0x01 /* AC online */ -#define APMEMU_AC_BACKUP 0x02 /* On backup power */ -#define APMEMU_AC_UNKNOWN 0xFF /* Unkonwn status */ - -#define APMEMU_BATTSTAT_HIGH 0x00 /* Remaining battery capacity is high */ -#define APMEMU_BATTSTAT_LOW 0x01 /* Remaining battery capacity is low */ -#define APMEMU_BATTSTAT_CRIT 0x02 /* Battery status is critical */ -#define APMEMU_BATTSTAT_CHR 0x03 /* Battery is charging */ -#define APMEMU_BATTSTAT_MISS 0x04 /* Battery is not present */ - -#define APMEMU_BATTFLAG_HIGH 0x01 /* Remaining battery capacity is high bit */ -#define APMEMU_BATTFLAG_LOW 0x02 /* Remaining battery capacity is low bit */ -#define APMEMU_BATTFLAG_CRIT 0x04 /* Battery status is critical bit */ -#define APMEMU_BATTFLAG_CHR 0x08 /* Battery is charging bit */ -#define APMEMU_BATTFLAG_MISS 0x80 /* Battery is not present bit */ - -#define APMEMU_BATTSTAT_UNKN 0xff /* Status is unknown */ - -#define APMEMU_BATTLIFE_UNKN -1 /* Remaining battery capacity is unknown */ diff --git a/battery.c b/battery.c index 77e1e8a..189afdf 100644 --- a/battery.c +++ b/battery.c @@ -16,8 +16,7 @@ */ #include "omnibook.h" - -#include "ec.h" +#include "hardware.h" static int ec_read16(u8 addr, u16 * data) { @@ -25,7 +24,6 @@ static int ec_read16(u8 addr, u16 * data) u8 high; u8 low; u16 result; - retval = legacy_ec_read(addr, &low); if (retval) return retval; @@ -197,7 +195,7 @@ static int omnibook_get_battery_info(int num, struct omnibook_battery_info *batt * 1 - Battery is not present * 2 - Not supported */ -int omnibook_get_battery_status(int num, struct omnibook_battery_state *battstat) +static int omnibook_get_battery_status(int num, struct omnibook_battery_state *battstat) { int retval; u8 status; diff --git a/blank.c b/blank.c index 7380412..a059d86 100644 --- a/blank.c +++ b/blank.c @@ -18,30 +18,29 @@ #include "omnibook.h" #include -#include "ec.h" +#include "hardware.h" static struct omnibook_feature blank_driver; -static int omnibook_console_blank_enabled = 0; - +/* + * console_blank_hook pointer manipulation is lock protected + */ extern int (*console_blank_hook) (int); +static DEFINE_SPINLOCK(blank_spinlock); -/* - * We would need an io_op parameter,but we are bound to the crapy - * console_blank_hook here - */ int omnibook_lcd_blank(int blank) { + struct omnibook_feature *blank_feature = omnibook_find_feature("blank"); int retval = 0; - if(!blank_driver.io_op) + if(!blank_feature) return -ENODEV; - if (blank_driver.io_op->backend == PIO) - omnibook_apply_write_mask(blank_driver.io_op, blank); - else if (blank_driver.io_op->backend == KBC || blank_driver.io_op->backend == CDI) - omnibook_toggle(blank_driver.io_op, blank); + if (blank_feature->io_op->backend == PIO) + omnibook_apply_write_mask(blank_feature->io_op, blank); + else if (blank_feature->io_op->backend == KBC || blank_feature->io_op->backend == CDI) + omnibook_toggle(blank_feature->io_op, blank); else { retval = -ENODEV; } @@ -51,42 +50,46 @@ int omnibook_lcd_blank(int blank) static int console_blank_register_hook(void) { - if (omnibook_console_blank_enabled == 0) { + spin_lock(&blank_spinlock); + if (console_blank_hook != omnibook_lcd_blank) { if (console_blank_hook == NULL) { console_blank_hook = omnibook_lcd_blank; printk(O_INFO "LCD backlight turn off at console blanking is enabled.\n"); - - omnibook_console_blank_enabled = 1; - } else { + } else printk(O_INFO "There is a console blanking solution already registered.\n"); - } } + spin_unlock(&blank_spinlock); return 0; } static int console_blank_unregister_hook(void) { + int retval; + spin_lock(&blank_spinlock); if (console_blank_hook == omnibook_lcd_blank) { console_blank_hook = NULL; printk(O_INFO "LCD backlight turn off at console blanking is disabled.\n"); - omnibook_console_blank_enabled = 0; } else if (console_blank_hook) { printk(O_WARN "You can not disable another console blanking solution.\n"); - return -EBUSY; + retval = -EBUSY; } else { printk(O_INFO "Console blanking already disabled.\n"); - return 0; } - return 0; + spin_unlock(&blank_spinlock); + return retval; } static int omnibook_console_blank_read(char *buffer, struct omnibook_operation *io_op) { int len = 0; + spin_lock(&blank_spinlock); + len += sprintf(buffer + len, "LCD console blanking hook is %s\n", - (omnibook_console_blank_enabled) ? "enabled" : "disabled"); + (console_blank_hook == omnibook_lcd_blank) ? "enabled" : "disabled"); + + spin_unlock(&blank_spinlock); return len; } @@ -97,17 +100,15 @@ static int omnibook_console_blank_write(char *buffer, struct omnibook_operation switch (*buffer) { case '0': - if ((retval = console_blank_unregister_hook())) - return retval; + retval = console_blank_unregister_hook(); break; case '1': - if ((retval = console_blank_register_hook())) - return retval; + retval = console_blank_register_hook(); break; default: - return -EINVAL; + retval = -EINVAL; } - return 0; + return retval; } static int __init omnibook_console_blank_init(struct omnibook_operation *io_op) diff --git a/bluetooth.c b/bluetooth.c index 8e84afb..acb0e5b 100644 --- a/bluetooth.c +++ b/bluetooth.c @@ -16,7 +16,7 @@ */ #include "omnibook.h" -#include "ec.h" +#include "hardware.h" static int omnibook_bt_read(char *buffer, struct omnibook_operation *io_op) { @@ -24,7 +24,7 @@ static int omnibook_bt_read(char *buffer, struct omnibook_operation *io_op) int retval; unsigned int state; - if ((retval = io_op->backend->aerial_get(io_op, &state))) + if ((retval = backend_aerial_get(io_op, &state))) return retval; len += @@ -42,23 +42,29 @@ static int omnibook_bt_write(char *buffer, struct omnibook_operation *io_op) int retval = 0; unsigned int state; - if ((retval = io_op->backend->aerial_get(io_op, &state))) - return retval; + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + + if ((retval = __backend_aerial_get(io_op, &state))) + goto out; if (*buffer == '0') state &= ~BT_STA; else if (*buffer == '1') state |= BT_STA; - else - return -EINVAL; + else { + retval = -EINVAL; + goto out; + } - if ((retval = io_op->backend->aerial_set(io_op, state))) - return retval; + retval = __backend_aerial_set(io_op, state); + out: + mutex_unlock(&io_op->backend->mutex); return retval; } -static struct omnibook_feature bt_feature; +static struct omnibook_feature bt_driver; static int __init omnibook_bt_init(struct omnibook_operation *io_op) { @@ -69,11 +75,11 @@ static int __init omnibook_bt_init(struct omnibook_operation *io_op) * Refuse enabling/disabling a non-existent device */ - if ((retval = io_op->backend->aerial_get(io_op, &state))) + if ((retval = backend_aerial_get(io_op, &state))) return retval; if (!(state & BT_EX)) - bt_feature.write = NULL; + bt_driver.write = NULL; return retval; } diff --git a/compal.c b/compal.c index 277f2ed..d91593d 100644 --- a/compal.c +++ b/compal.c @@ -23,8 +23,7 @@ #include #include -#include "ec.h" -#include "compat.h" +#include "hardware.h" /* * ATI's IXP PCI-LPC bridge @@ -57,22 +56,15 @@ #define PIO_PORT_COMMAND2 0x2 #define PIO_PORT_DATA 0x3 -/* - * We protect access to the Command/Data/Index interface by a Mutex - */ -static DEFINE_MUTEX(compal_lock); - /* * Private data of this backend */ -static struct kref *refcount; /* Reference counter of this backend */ static struct pci_dev *lpc_bridge; /* Southbridge chip ISA bridge/LPC interface PCI device */ static u32 ioport_base; /* PIO base adress */ static union { u16 word; u32 dword; } pci_reg_state; /* Saved state of register in PCI config spave */ -static int already_failed = 0; /* Backend init already failed at leat once */ /* * Possible list of supported southbridges @@ -310,26 +302,21 @@ static int omnibook_cdimode_init(const struct omnibook_operation *io_op) int retval = 0; int i; -/* ectypes other than TSM30X have no business with this backend */ + /* ectypes other than TSM30X have no business with this backend */ if (!(omnibook_ectype & TSM30X)) return -ENODEV; - if (already_failed) { + if (io_op->backend->already_failed) { dprintk("CDI backend init already failed, skipping.\n"); return -ENODEV; } - if (!refcount) { + if (!lpc_bridge) { /* Fist use of the backend */ - mutex_lock(&compal_lock); dprintk("Try to init cdimode\n"); - refcount = kmalloc(sizeof(struct kref), GFP_KERNEL); - if (!refcount) { - retval = -ENOMEM; - goto out; - } - - kref_init(refcount); + mutex_init(&io_op->backend->mutex); + mutex_lock(&io_op->backend->mutex); + kref_init(&io_op->backend->kref); /* PCI probing: find the LPC Super I/O bridge PCI device */ for (i = 0; !lpc_bridge && lpc_bridge_table[i].vendor; ++i) @@ -377,10 +364,11 @@ static int omnibook_cdimode_init(const struct omnibook_operation *io_op) clear_cdimode_pci(); dprintk("Cdimode init ok\n"); - goto out; + mutex_unlock(&io_op->backend->mutex); + return 0; } else { dprintk("Cdimode has already been initialized\n"); - kref_get(refcount); + kref_get(&io_op->backend->kref); return 0; } @@ -391,32 +379,34 @@ static int omnibook_cdimode_init(const struct omnibook_operation *io_op) pci_dev_put(lpc_bridge); lpc_bridge = NULL; error1: - kfree(refcount); - refcount = NULL; - already_failed = 1; - out: - mutex_unlock(&compal_lock); + io_op->backend->already_failed = 1; + mutex_unlock(&io_op->backend->mutex); + mutex_destroy(&io_op->backend->mutex); return retval; } static void cdimode_free(struct kref *ref) { - mutex_lock(&compal_lock); + struct omnibook_backend *backend; + dprintk("Cdimode not used anymore: disposing\n"); + + backend = container_of(ref, struct omnibook_backend, kref); + + mutex_lock(&backend->mutex); pci_dev_put(lpc_bridge); release_region(ioport_base, 4); - kfree(refcount); lpc_bridge = NULL; - refcount = NULL; - mutex_unlock(&compal_lock); + mutex_unlock(&backend->mutex); + mutex_destroy(&backend->mutex); } static void omnibook_cdimode_exit(const struct omnibook_operation *io_op) { -/* ectypes other than TSM30X have no business with this backend */ + /* ectypes other than TSM30X have no business with this backend */ BUG_ON(!(omnibook_ectype & TSM30X)); dprintk("Trying to dispose cdimode\n"); - kref_put(refcount, cdimode_free); + kref_put(&io_op->backend->kref, cdimode_free); } /* @@ -430,9 +420,6 @@ static int omnibook_cdimode_read(const struct omnibook_operation *io_op, u8 * va if (!lpc_bridge) return -ENODEV; - if (mutex_lock_interruptible(&compal_lock)) - return -ERESTARTSYS; - retval = enable_cdimode(); if (retval) goto out; @@ -448,7 +435,6 @@ static int omnibook_cdimode_read(const struct omnibook_operation *io_op, u8 * va clear_cdimode(); out: clear_cdimode_pci(); - mutex_unlock(&compal_lock); return retval; } @@ -463,9 +449,6 @@ static int omnibook_cdimode_write(const struct omnibook_operation *io_op, u8 val if (!lpc_bridge) return -ENODEV; - if (mutex_lock_interruptible(&compal_lock)) - return -ERESTARTSYS; - retval = enable_cdimode(); if (retval) goto out; @@ -477,7 +460,6 @@ static int omnibook_cdimode_write(const struct omnibook_operation *io_op, u8 val clear_cdimode(); out: clear_cdimode_pci(); - mutex_unlock(&compal_lock); return retval; } @@ -492,23 +474,17 @@ static int omnibook_cdimode_hotkeys(const struct omnibook_operation *io_op, unsi { CDI, 0, TSM70_FN_INDEX, 0, TSM70_FN_ENABLE, TSM70_FN_DISABLE}; /* Fn+foo handling */ - retval = omnibook_toggle(&hotkeys_op, !!(state & HKEY_FN)); + retval = __omnibook_toggle(&hotkeys_op, !!(state & HKEY_FN)); if (retval < 0) return retval; /* Multimedia keys handling */ - if (state & HKEY_MULTIMEDIA) { - hotkeys_op.write_addr = TSM70_HOTKEYS_INDEX; - retval = omnibook_cdimode_write(&hotkeys_op, TSM70_HOTKEYS_ENABLE); - } else { - /* FIXME: quirk use kbc backend */ - retval = kbc_backend.byte_write(NULL, OMNIBOOK_KBC_CMD_ONETOUCH_DISABLE); - } + hotkeys_op.write_addr = TSM70_HOTKEYS_INDEX; + hotkeys_op.on_mask = TSM70_HOTKEYS_ENABLE; + hotkeys_op.off_mask = TSM70_HOTKEYS_DISABLE; + retval = __omnibook_toggle(&hotkeys_op, !!(state & HKEY_MULTIMEDIA)); - if (retval < 0) - return retval; - else - return HKEY_MULTIMEDIA | HKEY_FN; + return retval; } /* Scan index space, this hard locks my machine */ @@ -535,6 +511,7 @@ static int compal_scan(char *buffer) struct omnibook_backend compal_backend = { .name = "compal", + .hotkeys_write_cap = HKEY_MULTIMEDIA | HKEY_FN, .init = omnibook_cdimode_init, .exit = omnibook_cdimode_exit, .byte_read = omnibook_cdimode_read, diff --git a/compat.h b/compat.h index c719127..d456053 100644 --- a/compat.h +++ b/compat.h @@ -1,5 +1,5 @@ /* - * compat.h -- Older kernel (=> 2.6.9) support + * compat.h -- Older kernel (=> 2.6.11) support * * 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 @@ -21,13 +21,14 @@ * Mutex to Semaphore fallback */ - #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)) #include #define DEFINE_MUTEX(lock) DECLARE_MUTEX(lock) +#define mutex_init(lock) init_MUTEX(lock) #define mutex_lock(lock) down(lock) #define mutex_lock_interruptible(lock) down_interruptible(lock) #define mutex_unlock(lock) up(lock) +#define mutex_destroy(lock) do { } while(0) #else #include #endif diff --git a/display.c b/display.c index 19f40b7..b26af5c 100644 --- a/display.c +++ b/display.c @@ -16,7 +16,7 @@ */ #include "omnibook.h" -#include "ec.h" +#include "hardware.h" static const char display_name[][16] = { "Internal LCD", @@ -31,7 +31,7 @@ static int omnibook_display_read(char *buffer, struct omnibook_operation *io_op) int retval; unsigned int sta, en_mask, det_mask; - retval = io_op->backend->display_get(io_op, &sta); + retval = backend_display_get(io_op, &sta); if (retval < 0) return retval; @@ -64,7 +64,7 @@ static int omnibook_display_write(char *buffer, struct omnibook_operation *io_op if (endp == buffer) return -EINVAL; else - retval = io_op->backend->display_set(io_op, state); + retval = backend_display_set(io_op, state); return retval; } diff --git a/doc/ChangeLog b/doc/ChangeLog index 75b1057..c29f791 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -6,6 +6,10 @@ Changelog file for omnibook package: support with a far more complete autodetection database (see http://www2.informatik.hu-berlin.de/~tauber/acerhk/) * Fix and improve bluetooth handling for TSM30X class laptops +* Implement Volume down,up and Mute buttons polling for ectype 2 + It was the last missing part from the "omke" module which never + got ported to linux 2.6 +* Implement ectype 13 Fn hotkeys handling. * DMI signature added: HP Pavilion ze4500 (ectype 7) Toshiba Satellite 1130 (ectype 1) diff --git a/dock.c b/dock.c index b7a5942..3264f70 100644 --- a/dock.c +++ b/dock.c @@ -16,8 +16,7 @@ */ #include "omnibook.h" - -#include "ec.h" +#include "hardware.h" static int omnibook_dock_read(char *buffer, struct omnibook_operation *io_op) { @@ -25,7 +24,7 @@ static int omnibook_dock_read(char *buffer, struct omnibook_operation *io_op) u8 dock; int retval; - if ((retval = io_op->backend->byte_read(io_op, &dock))) + if ((retval = backend_byte_read(io_op, &dock))) return retval; len += sprintf(buffer + len, "Laptop is %s\n", (dock) ? "docked" : "undocked"); @@ -33,6 +32,35 @@ static int omnibook_dock_read(char *buffer, struct omnibook_operation *io_op) return len; } +static int omnibook_dock_write(char *buffer, struct omnibook_operation *io_op) +{ + int retval; + + switch (*buffer) { + case '0': + retval = backend_byte_write(io_op, 0); + break; + case '1': + retval = backend_byte_write(io_op, 1); + break; + default: + retval = -EINVAL; + } + + return retval; +} + +static struct omnibook_feature dock_driver; + +static int __init omnibook_dock_init(struct omnibook_operation *io_op) +{ + /* writing is only supported on ectype 13 */ + if(!(omnibook_ectype & TSM40)) + dock_driver.write = NULL; + + return 0; +} + static struct omnibook_tbl dock_table[] __initdata = { {XE3GF, SIMPLE_BYTE(EC, XE3GF_CSPR, XE3GF_CSPR_MASK)}, {OB500 | OB510 | OB6000 | OB6100, SIMPLE_BYTE(EC, OB500_STA1, OB500_DCKS_MASK)}, @@ -44,7 +72,9 @@ static struct omnibook_tbl dock_table[] __initdata = { static struct omnibook_feature __declared_feature dock_driver = { .name = "dock", .enabled = 0, + .init = omnibook_dock_init, .read = omnibook_dock_read, + .write = omnibook_dock_write, .ectypes = XE3GF | OB500 | OB510 | OB6000 | OB6100 | OB4150 | TSM40, .tbl = dock_table, }; diff --git a/dump.c b/dump.c index 7b1a0fe..b891bf0 100644 --- a/dump.c +++ b/dump.c @@ -20,7 +20,7 @@ */ #include "omnibook.h" -#include "ec.h" +#include "hardware.h" static u8 ecdump_regs[256]; diff --git a/ec.c b/ec.c index e8e30f0..3960966 100644 --- a/ec.c +++ b/ec.c @@ -1,6 +1,5 @@ /* * ec.c -- low level functions to access Embedded Controller, - * Keyboard Controller and system I/O ports or memory * * 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 @@ -25,8 +24,7 @@ #include #include -#include "ec.h" -#include "compat.h" +#include "hardware.h" /* * Interrupt control @@ -34,12 +32,6 @@ static DEFINE_SPINLOCK(omnibook_ec_lock); -/* - * Timeout in ms for sending to controller - */ - -#define OMNIBOOK_TIMEOUT 250 - /* * Registers of the embedded controller */ @@ -104,7 +96,7 @@ static int omnibook_ec_read(const struct omnibook_operation *io_op, u8 * data) retval = ec_read((u8) io_op->read_addr, data); if (io_op->read_mask) *data &= io_op->read_mask; - dprintk("ACPI EC read at %lx success %i.\n", io_op->read_addr, retval); +// dprintk("ACPI EC read at %lx success %i.\n", io_op->read_addr, retval); return retval; } #endif @@ -125,7 +117,7 @@ static int omnibook_ec_read(const struct omnibook_operation *io_op, u8 * data) *data &= io_op->read_mask; end: spin_unlock_irq(&omnibook_ec_lock); - dprintk("Custom EC read at %lx success %i.\n", io_op->read_addr, retval); +// dprintk("Custom EC read at %lx success %i.\n", io_op->read_addr, retval); return retval; } @@ -145,7 +137,7 @@ static int omnibook_ec_write(const struct omnibook_operation *io_op, u8 data) #ifdef CONFIG_ACPI_EC if (likely(!acpi_disabled)) { retval = ec_write((u8) io_op->write_addr, data); - dprintk("ACPI EC write at %lx success %i.\n", io_op->write_addr, retval); +// dprintk("ACPI EC write at %lx success %i.\n", io_op->write_addr, retval); return retval; } #endif @@ -165,7 +157,7 @@ static int omnibook_ec_write(const struct omnibook_operation *io_op, u8 data) outb(data, OMNIBOOK_EC_DATA); end: spin_unlock_irq(&omnibook_ec_lock); - dprintk("Custom EC write at %lx success %i.\n", io_op->write_addr, retval); +// dprintk("Custom EC write at %lx success %i.\n", io_op->write_addr, retval); return retval; } @@ -198,7 +190,7 @@ static int omnibook_ec_display(const struct omnibook_operation *io_op, unsigned int retval; u8 raw_state; - retval = io_op->backend->byte_read(io_op, &raw_state); + retval = __backend_byte_read(io_op, &raw_state); if (retval < 0) return retval; @@ -207,268 +199,10 @@ static int omnibook_ec_display(const struct omnibook_operation *io_op, unsigned return DISPLAY_CRT_DET; } -/* - * Registers of the keyboard controller - */ - -#define OMNIBOOK_KBC_DATA 0x60 -#define OMNIBOOK_KBC_SC 0x64 - -/* - * Keyboard controller status register bits - */ - -#define OMNIBOOK_KBC_STAT_OBF 0x01 /* Output buffer full */ -#define OMNIBOOK_KBC_STAT_IBF 0x02 /* Input buffer full */ -#define OMNIBOOK_KBC_STAT_CMD 0x08 /* Last write was a command write (0=data) */ - -/* - * Wait for keyboard buffer - */ - -static int omnibook_kbc_wait(u8 event) -{ - int timeout = OMNIBOOK_TIMEOUT; - - switch (event) { - case OMNIBOOK_KBC_STAT_OBF: - while (!(inb(OMNIBOOK_KBC_SC) & event) && timeout--) - mdelay(1); - break; - case OMNIBOOK_KBC_STAT_IBF: - while ((inb(OMNIBOOK_KBC_SC) & event) && timeout--) - mdelay(1); - break; - default: - return -EINVAL; - } - if (timeout > 0) - return 0; - return -ETIME; -} - -/* - * Write to the keyboard command register - */ - -static int omnibook_kbc_write_command(u8 cmd) -{ - int retval; - - spin_lock_irq(&omnibook_ec_lock); - retval = omnibook_kbc_wait(OMNIBOOK_KBC_STAT_IBF); - if (retval) - goto end; - outb(cmd, OMNIBOOK_KBC_SC); - retval = omnibook_kbc_wait(OMNIBOOK_KBC_STAT_IBF); - end: - spin_unlock_irq(&omnibook_ec_lock); - return retval; -} - -/* - * Write to the keyboard data register - */ - -static int omnibook_kbc_write_data(u8 data) -{ - int retval; - - spin_lock_irq(&omnibook_ec_lock); - retval = omnibook_kbc_wait(OMNIBOOK_KBC_STAT_IBF); - if (retval) - goto end;; - outb(data, OMNIBOOK_KBC_DATA); - retval = omnibook_kbc_wait(OMNIBOOK_KBC_STAT_IBF); - end: - spin_unlock_irq(&omnibook_ec_lock); - return retval; -} - -/* - * Send a command to keyboard controller - */ - -static int omnibook_kbc_command(const struct omnibook_operation *io_op, u8 data) -{ - int retval; - - if ((retval = omnibook_kbc_write_command(OMNIBOOK_KBC_CONTROL_CMD))) - return retval; - - retval = omnibook_kbc_write_data(data); - return retval; -} - -/* - * Onetouch button hotkey handler - */ -static int omnibook_kbc_hotkeys(const struct omnibook_operation *io_op, unsigned int state) -{ - int retval; - - retval = omnibook_toggle(io_op, !!(state & HKEY_ONETOUCH)); - - if (retval < 0) - return retval; - else - return HKEY_ONETOUCH; -} - -/* - * IO port backend. Only support single or dual ports operations - * private data structure: it's the linked list of requested ports - * - * Race condition issue: omnibook_pio_init/exit functions are only called from - * omnibook_backend_match and omnibook_remove from init.c, this should happen - * only at module init/exit time so there is no need for a lock. - */ - -struct pio_private_data_t { - unsigned long addr; - struct kref refcount; - struct list_head list; -}; - -static struct pio_private_data_t pio_private_data = { - .addr = 0, - .list = LIST_HEAD_INIT(pio_private_data.list), -}; - -/* - * Match an entry in the linked list helper function: see if we have and entry - * whose addr field match maddr - */ -static struct pio_private_data_t *omnibook_match_port(struct pio_private_data_t *data, - unsigned long maddr) -{ - struct list_head *p; - struct pio_private_data_t *cursor; - - list_for_each(p, &data->list) { - cursor = list_entry(p, struct pio_private_data_t, list); - if (cursor->addr == maddr) { - return cursor; - } - } - return NULL; -} - -/* - * See if we have to request raddr - */ -static int omnibook_claim_port(struct pio_private_data_t *data, unsigned long raddr) -{ - struct pio_private_data_t *match, *new; - - match = omnibook_match_port(data, raddr); - if (match) { - /* Already requested by us: increment kref and quit */ - kref_get(&match->refcount); - return 0; - } - - /* there was no match: request the region and add to list */ - if (!request_region(raddr, 1, OMNIBOOK_MODULE_NAME)) { - printk(O_ERR "Request I/O port error\n"); - return -ENODEV; - } - - new = kmalloc(sizeof(struct pio_private_data_t), GFP_KERNEL); - if (!new) { - release_region(raddr, 1); - return -ENOMEM; - } - - kref_init(&new->refcount); - new->addr = raddr; - list_add(&new->list, &data->list); - - return 0; -} - -/* - * Register read_addr and write_addr - */ -static int omnibook_pio_init(const struct omnibook_operation *io_op) -{ - int retval = 0; - - if (io_op->read_addr - && (retval = omnibook_claim_port(io_op->backend->data, io_op->read_addr))) - goto out; - - if (io_op->write_addr && (io_op->write_addr != io_op->read_addr)) - retval = omnibook_claim_port(io_op->backend->data, io_op->write_addr); - - out: - return retval; -} - -/* - * REALLY release a port - */ -static void omnibook_free_port(struct kref *ref) -{ - struct pio_private_data_t *data; - - data = container_of(ref, struct pio_private_data_t, refcount); - release_region(data->addr, 1); - list_del(&data->list); - kfree(data); -} - -/* - * Unregister read_addr and write_addr - */ -static void omnibook_pio_exit(const struct omnibook_operation *io_op) -{ - struct pio_private_data_t *match; - - match = omnibook_match_port(io_op->backend->data, io_op->read_addr); - if (match) - kref_put(&match->refcount, omnibook_free_port); - - match = NULL; - match = omnibook_match_port(io_op->backend->data, io_op->write_addr); - if (match) - kref_put(&match->refcount, omnibook_free_port); - -} - -static int omnibook_io_read(const struct omnibook_operation *io_op, u8 * value) -{ - *value = inb(io_op->read_addr); - if (io_op->read_mask) - *value &= io_op->read_mask; - return 0; -} - -static int omnibook_io_write(const struct omnibook_operation *io_op, u8 value) -{ - outb(io_op->write_addr, value); - return 0; -} - /* * Backend interface declarations */ -struct omnibook_backend kbc_backend = { - .name = "i8042", - .byte_write = omnibook_kbc_command, - .hotkeys_set = omnibook_kbc_hotkeys, -}; - -struct omnibook_backend pio_backend = { - .name = "pio", - .data = &pio_private_data, - .init = omnibook_pio_init, - .exit = omnibook_pio_exit, - .byte_read = omnibook_io_read, - .byte_write = omnibook_io_write, -}; - struct omnibook_backend ec_backend = { .name = "ec", .byte_read = omnibook_ec_read, @@ -476,45 +210,4 @@ struct omnibook_backend ec_backend = { .display_get = omnibook_ec_display, }; -int omnibook_apply_write_mask(const struct omnibook_operation *io_op, int toggle) -{ - int retval = 0; - int mask; - u8 data; - - if ((retval = io_op->backend->byte_read(io_op, &data))) - return retval; - - if (toggle == 1) - mask = io_op->on_mask; - else if (toggle == 0) - mask = io_op->off_mask; - else - return -EINVAL; - - if (mask > 0) - data |= (u8) mask; - else if (mask < 0) - data &= ~((u8) (-mask)); - else - return -EINVAL; - - retval = io_op->backend->byte_write(io_op, data); - - return retval; -} - -/* - * Helper for toggle like operations - */ -int omnibook_toggle(const struct omnibook_operation *io_op, int toggle) -{ - int retval; - u8 data; - - data = toggle ? io_op->on_mask : io_op->off_mask; - retval = io_op->backend->byte_write(io_op, data); - return retval; -} - /* End of file */ diff --git a/fan.c b/fan.c index ba88cbd..1587227 100644 --- a/fan.c +++ b/fan.c @@ -19,7 +19,7 @@ #include #include -#include "ec.h" +#include "hardware.h" static const struct omnibook_operation ctmp_io_op = { EC, XE3GF_CTMP, 0, 0, 0, 0 }; static const struct omnibook_operation fot_io_op = { EC, XE3GF_FOT, XE3GF_FOT, 0, 0, 0 }; @@ -29,7 +29,7 @@ static int omnibook_get_fan(struct omnibook_operation *io_op) u8 fan; int retval; - if ((retval = io_op->backend->byte_read(io_op, &fan))) + if ((retval = backend_byte_read(io_op, &fan))) return retval; /* @@ -57,39 +57,49 @@ static int omnibook_fan_off(struct omnibook_operation *io_op) { int i, retval = 0; -/* - * Special handling for XE3GF & TSP10 - */ - if (omnibook_ectype & (XE3GF | TSP10)) { - u8 fot, temp; - retval = omnibook_get_fan(io_op); + if (!(omnibook_ectype & (XE3GF | TSP10))) { + retval = omnibook_apply_write_mask(io_op, 0); + return retval; + } else { + /* + * Special handling for XE3GF & TSP10 + */ + u8 fot, temp, fan; + + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + + retval = __backend_byte_read(io_op, &fan); /* error or fan is already off */ - if (retval <= 0) - return retval; + if (retval || !fan) + goto out; /* now we set FOT to current temp, then reset to initial value */ - if ((retval = fot_io_op.backend->byte_read(&fot_io_op, &fot))) - return retval; - if ((retval = ctmp_io_op.backend->byte_read(&ctmp_io_op, &temp))) - return retval; + if ((retval = __backend_byte_read(&fot_io_op, &fot))) + goto out; + if ((retval = __backend_byte_read(&ctmp_io_op, &temp))) + goto out; -/* - * Wait for no longer than 250ms, this is arbitrary - */ + /* Wait for no longer than 250ms (this is arbitrary). */ for (i = 0; i < 250; i++) { - fot_io_op.backend->byte_write(&fot_io_op, temp); + __backend_byte_write(&fot_io_op, temp); mdelay(1); - if (omnibook_get_fan(io_op) == 0) { - retval = fot_io_op.backend->byte_write(&fot_io_op, fot); - return retval; - } + __backend_byte_read(io_op, &fan); + if (!fan) /* Fan is off */ + break; + } + __backend_byte_write(&fot_io_op, fot); + + if(i == 250 ) { + printk(O_ERR "Attempt to switch off the fan failed.\n"); + retval = -EIO; } - fot_io_op.backend->byte_write(&fot_io_op, fot); - printk(O_ERR "Attempt to switch off the fan failed.\n"); - return -EIO; - } else - retval = omnibook_apply_write_mask(io_op, 0); + + out: + mutex_unlock(&io_op->backend->mutex); + } + return retval; } @@ -119,20 +129,18 @@ static int omnibook_fan_write(char *buffer, struct omnibook_operation *io_op) switch (*buffer) { case '0': - if ((retval = omnibook_fan_off(io_op))) - return retval; + retval = omnibook_fan_off(io_op); break; case '1': - if ((retval = omnibook_fan_on(io_op))) - return retval; + retval = omnibook_fan_on(io_op); break; default: - return -EINVAL; + retval = -EINVAL; } - return 0; + return retval; } -static struct omnibook_feature fan_feature; +static struct omnibook_feature fan_driver; static int __init omnibook_fan_init(struct omnibook_operation *io_op) { @@ -143,7 +151,7 @@ static int __init omnibook_fan_init(struct omnibook_operation *io_op) * They only support fan reading */ if (omnibook_ectype & (OB4150 | XE2 | AMILOD)) - fan_feature.write = NULL; + fan_driver.write = NULL; return 0; } @@ -170,6 +178,6 @@ static struct omnibook_feature __declared_feature fan_driver = { .tbl = fan_table, }; -module_param_named(fan, fan_feature.enabled, int, S_IRUGO); +module_param_named(fan, fan_driver.enabled, int, S_IRUGO); MODULE_PARM_DESC(fan, "Use 0 to disable, 1 to enable fan status monitor and control"); /* End of file */ diff --git a/fan_policy.c b/fan_policy.c index ab86a6b..cee6bdf 100644 --- a/fan_policy.c +++ b/fan_policy.c @@ -18,7 +18,7 @@ #include "omnibook.h" #include -#include "ec.h" +#include "hardware.h" /* * Default temperature limits. diff --git a/ec.h b/hardware.h similarity index 78% rename from ec.h rename to hardware.h index c80514c..5abf2fb 100644 --- a/ec.h +++ b/hardware.h @@ -1,5 +1,5 @@ /* - * ec.h -- low level definitions to access Embedded Controller and co. + * hardware.h -- low level definitions to access Embedded Controller and co. * * 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 @@ -16,6 +16,7 @@ */ #include +#include "compat.h" /* * Quite ugly: @@ -30,8 +31,8 @@ struct omnibook_operation { unsigned long read_addr; /* address for data reading */ unsigned long write_addr; /* address for data writing */ u8 read_mask; /* read mask */ - int on_mask; /* mask to set (pos value) or unset (neg value) to put feature to on state */ - int off_mask; /* mask to set (pos value) or unset (neg value) to put feature to off state */ + int on_mask; /* mask to set (pos value) or unset (neg value) to put feature in on state */ + int off_mask; /* mask to set (pos value) or unset (neg value) to put feature in off state */ }; #define COMMAND(backend,data_on,data_off) { backend, 0, 0, 0, data_on, data_off } @@ -44,16 +45,19 @@ struct omnibook_tbl { /* * Backend interface definition - * - * Note: - * display_get/set and hotkey_set/get return a positive value on success - * which set bits corresponds to the actual supported states - * (see omnibook.h for the DISPLAY_* masks) */ struct omnibook_backend { const char *name; /* backend name */ - void *data; /* backend private data pointer */ + struct mutex mutex; /* serializes all access to backend functions */ + const unsigned int hotkeys_read_cap; /* hotkey probing mask */ + const unsigned int hotkeys_write_cap; /* hotkey setting mask */ + + /* Public data fields, access with mutex held */ + unsigned int hotkeys_state; /* saved hotkeys state */ + unsigned int misc_state; /* various status bit: touchpad and muteled */ + + /* Public function pointers */ int (*init) (const struct omnibook_operation *); void (*exit) (const struct omnibook_operation *); int (*byte_read) (const struct omnibook_operation *, u8 *); @@ -64,6 +68,11 @@ struct omnibook_backend { int (*hotkeys_set) (const struct omnibook_operation *, unsigned int); int (*display_get) (const struct omnibook_operation *, unsigned int *); int (*display_set) (const struct omnibook_operation *, unsigned int); + + /* Private fields, never to be accessed outside backend code */ + struct kref kref; /* Reference counter of this backend */ + void *data; /* private data pointer */ + int already_failed; /* Backend init already failed at least once */ }; extern struct omnibook_backend kbc_backend; @@ -82,8 +91,113 @@ extern struct omnibook_backend compal_backend; int legacy_ec_read(u8 addr, u8 *data); int legacy_ec_write(u8 addr, u8 data); -int omnibook_apply_write_mask(const struct omnibook_operation *io_op, int toggle); -int omnibook_toggle(const struct omnibook_operation *io_op, int toggle); +int __omnibook_apply_write_mask(const struct omnibook_operation *io_op, int toggle); +int __omnibook_toggle(const struct omnibook_operation *io_op, int toggle); + +/* + * Lock helper functions. Defines locking and __prefixed non locking variants. + */ + +#define helper_func(func) \ +static inline int backend_##func##_get(const struct omnibook_operation *io_op, unsigned int *data) \ +{ \ + int retval; \ + if(mutex_lock_interruptible(&io_op->backend->mutex)) \ + return -ERESTARTSYS; \ + retval = io_op->backend->func##_get(io_op, data); \ + mutex_unlock(&io_op->backend->mutex); \ + return retval; \ +} \ +static inline int backend_##func##_set(const struct omnibook_operation *io_op, unsigned int data) \ +{ \ + int retval; \ + if(mutex_lock_interruptible(&io_op->backend->mutex)) \ + return -ERESTARTSYS; \ + retval = io_op->backend->func##_set(io_op, data); \ + mutex_unlock(&io_op->backend->mutex); \ + return retval; \ +}\ +static inline int __backend_##func##_get(const struct omnibook_operation *io_op, unsigned int *data) \ +{ \ + int retval; \ + WARN_ON(!mutex_is_locked(&io_op->backend->mutex)); \ + retval = io_op->backend->func##_get(io_op, data); \ + return retval; \ +} \ +static inline int __backend_##func##_set(const struct omnibook_operation *io_op, unsigned int data) \ +{ \ + int retval; \ + WARN_ON(!mutex_is_locked(&io_op->backend->mutex)); \ + retval = io_op->backend->func##_set(io_op, data); \ + return retval; \ +} + +helper_func(aerial) +helper_func(hotkeys) +helper_func(display) + +static inline int backend_byte_read(const struct omnibook_operation *io_op, u8 *data) +{ + int retval; + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + retval = io_op->backend->byte_read(io_op, data); + mutex_unlock(&io_op->backend->mutex); + return retval; +} + +static inline int backend_byte_write(const struct omnibook_operation *io_op, u8 data) +{ + int retval; + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + retval = io_op->backend->byte_write(io_op, data); + mutex_unlock(&io_op->backend->mutex); + return retval; +} + +static inline int __backend_byte_read(const struct omnibook_operation *io_op, u8 *data) +{ + int retval; + WARN_ON(!mutex_is_locked(&io_op->backend->mutex)); + retval = io_op->backend->byte_read(io_op, data); + return retval; +} + +static inline int __backend_byte_write(const struct omnibook_operation *io_op, u8 data) +{ + int retval; + WARN_ON(!mutex_is_locked(&io_op->backend->mutex)); + retval = io_op->backend->byte_write(io_op, data); + return retval; +} + +static inline int omnibook_apply_write_mask(const struct omnibook_operation *io_op, int toggle) +{ + int retval; + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + retval = __omnibook_apply_write_mask(io_op, toggle); + mutex_unlock(&io_op->backend->mutex); + return retval; +} + +static inline int omnibook_toggle(const struct omnibook_operation *io_op, int toggle) +{ + int retval; + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + retval = __omnibook_toggle(io_op, toggle); + mutex_unlock(&io_op->backend->mutex); + return retval; +} + +/* + * Timeout in ms for sending to controller + */ + +#define OMNIBOOK_TIMEOUT 250 + /* * Embedded controller adresses @@ -207,14 +321,6 @@ int omnibook_toggle(const struct omnibook_operation *io_op, int toggle); #define XE3GC_BRGT_MASK 0x40 /* Fn+F1/Fn+F2 - Brightness up or down pressed */ #define XE3GC_BTVL_MASK 0x0F /* LCD brightness */ -/* - * Emulated scancodes - */ - -#define XE3GC_VOLD_SCAN 0x2E /* Volume down button scancode */ -#define XE3GC_VOLU_SCAN 0x30 /* Volume up button scancode */ -#define XE3GC_MUTE_SCAN 0x20 /* Volume up button scancode */ - /* * Toshiba Satellite A105 values and mask */ @@ -382,6 +488,7 @@ int omnibook_toggle(const struct omnibook_operation *io_op, int toggle); /* * Index and values for Command/Data/Index interface + * Notice similitudes with commands code for kbc */ #define TSM70_FN_INDEX 0x45 @@ -389,6 +496,7 @@ int omnibook_toggle(const struct omnibook_operation *io_op, int toggle); #define TSM70_FN_DISABLE 0x74 #define TSM70_HOTKEYS_INDEX 0x59 #define TSM70_HOTKEYS_ENABLE 0x90 +#define TSM70_HOTKEYS_DISABLE 0x91 #define TSM70_LCD_READ 0x5C #define TSM70_LCD_WRITE 0x5D #define TSM70_TOUCHPAD_ON 0x80 @@ -397,8 +505,8 @@ int omnibook_toggle(const struct omnibook_operation *io_op, int toggle); #define TSM100_LCD_ON 0xe1 #define TSM100_LCD_OFF 0xe2 -/* Toshiba SMI funtion */ -#define SMI_FN_PRESSED 0x00 +/* Toshiba SMI funtions and constants*/ +#define SMI_FN_PRESSED 0x8f #define SMI_SET_LCD_BRIGHTNESS 0xa2 #define SMI_GET_LCD_BRIGHTNESS 0xa3 #define SMI_GET_KILL_SWITCH 0xa4 @@ -417,3 +525,6 @@ int omnibook_toggle(const struct omnibook_operation *io_op, int toggle); #define SMI_STICK_KEYS_MASK 0x02 #define SMI_FN_TWICE_LOCK_MASK 0x04 #define SMI_FN_DOCK_MASK 0x08 + +#define SMI_FN_SCAN 0x6d /* Fn key scancode */ +#define SMI_DOCK_SCAN 0x6e /* Dock scancode */ diff --git a/hotkeys.c b/hotkeys.c index 935bcfc..5e52a12 100644 --- a/hotkeys.c +++ b/hotkeys.c @@ -16,48 +16,60 @@ */ #include "omnibook.h" -#include "ec.h" - -/* - * Save state for suspend/resume operation - */ -static unsigned int saved_state; +#include "hardware.h" /* Predefined convinient on/off states */ #define HKEY_ON HKEY_ONETOUCH|HKEY_MULTIMEDIA|HKEY_FN|HKEY_DOCK|HKEY_FNF5 #define HKEY_OFF 0 -static int omnibook_hotkeys_set(struct omnibook_operation *io_op, unsigned int state) +/* + * Set hotkeys status and update recorded saved state + */ +static int hotkeys_set_save(struct omnibook_operation *io_op, unsigned int state) { - int write_capability; + int retval; - write_capability = io_op->backend->hotkeys_set(io_op, state); - if (write_capability < 0) + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + + retval = __backend_hotkeys_set(io_op, state); + if (retval < 0) goto out; /* Update saved state */ - saved_state = state & write_capability; + io_op->backend->hotkeys_state = state & io_op->backend->hotkeys_write_cap; - out: - return write_capability; + out: + mutex_unlock(&io_op->backend->mutex); + return retval; } -static int omnibook_hotkeys_get(struct omnibook_operation *io_op, unsigned int *state) +/* + * Read hotkeys status, fallback to reading saved state if real probing is not + * supported. + */ +static int hotkeys_get_save(struct omnibook_operation *io_op, unsigned int *state) { unsigned int read_state = 0; - int read_capability = 0; + int retval = 0; + + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; if (io_op->backend->hotkeys_get) - read_capability = io_op->backend->hotkeys_get(io_op, &read_state); - if (read_capability < 0) + retval = __backend_hotkeys_get(io_op, &read_state); + if (retval < 0) goto out; /* Return previously set state for the fields that are write only */ - *state = (read_state & read_capability) + (saved_state & ~read_capability); + *state = (read_state & io_op->backend->hotkeys_read_cap) + + (io_op->backend->hotkeys_state & ~io_op->backend->hotkeys_read_cap); + - out: - return read_capability; + out: + mutex_unlock(&io_op->backend->mutex); + return 0; } /* @@ -70,28 +82,20 @@ static int omnibook_hotkeys_get(struct omnibook_operation *io_op, unsigned int * static int omnibook_hotkeys_resume(struct omnibook_operation *io_op) { int retval; - retval = io_op->backend->hotkeys_set(io_op, saved_state); - if(retval < 0) - return retval; - return 0; + mutex_lock(&io_op->backend->mutex); + retval = __backend_hotkeys_set(io_op, io_op->backend->hotkeys_state); + mutex_unlock(&io_op->backend->mutex); + return retval; } /* - * Save state and disable hotkeys upon suspend (FIXME is the disabling required ?) + * Disable hotkeys upon suspend (FIXME is the disabling required ?) */ static int omnibook_hotkeys_suspend(struct omnibook_operation *io_op) { int retval = 0; - - retval = omnibook_hotkeys_get(io_op, &saved_state); - if (retval < 0) - return retval; - - retval = io_op->backend->hotkeys_set(io_op, HKEY_OFF); - if (retval < 0) - return retval; - - return 0; + retval = backend_hotkeys_set(io_op, HKEY_OFF); + return retval; } static const char pretty_name[][27] = { @@ -101,30 +105,31 @@ static const char pretty_name[][27] = { "Stick key is", "Press Fn twice to lock is", "Dock events are", - "Fn + F5 hotkey is" + "Fn + F5 hotkey is", }; static int omnibook_hotkeys_read(char *buffer, struct omnibook_operation *io_op) { int len = 0; - int read_capability, write_capability; - unsigned int read_state, mask; + int retval; + unsigned int read_state = 0; /* buggy gcc 4.1 warning fix */ + unsigned int shift, mask; - read_capability = omnibook_hotkeys_get(io_op, &read_state); - if (read_capability < 0) - return read_capability; + retval = hotkeys_get_save(io_op, &read_state); - write_capability = omnibook_hotkeys_set(io_op, read_state); - if (write_capability < 0) - return write_capability; + if (retval < 0) + return retval; - for (mask = HKEY_ONETOUCH; mask <= HKEY_FNF5; mask = mask << 1) { + for (shift = 0; shift <= HKEY_LAST_SHIFT ; shift++) { + mask = 1 << shift; /* we assume write capability or read capability imply support */ - if ((read_capability | write_capability) & mask) + if ((io_op->backend->hotkeys_read_cap | io_op->backend->hotkeys_write_cap) & mask) len += - sprintf(buffer + len, "%s %s.\n", pretty_name[ffs(mask) - 1], + sprintf(buffer + len, "%s %s.\n", pretty_name[shift], (read_state & mask) ? "enabled" : "disabled"); } + + return len; } @@ -134,15 +139,15 @@ static int omnibook_hotkeys_write(char *buffer, struct omnibook_operation *io_op char *endp; if (strncmp(buffer, "off", 3) == 0) - omnibook_hotkeys_set(io_op, HKEY_OFF); + hotkeys_set_save(io_op, HKEY_OFF); else if (strncmp(buffer, "on", 2) == 0) - omnibook_hotkeys_set(io_op, HKEY_ON); + hotkeys_set_save(io_op, HKEY_ON); else { state = simple_strtoul(buffer, &endp, 16); if (endp == buffer) return -EINVAL; else - omnibook_hotkeys_set(io_op, state); + hotkeys_set_save(io_op, state); } return 0; } @@ -150,18 +155,19 @@ static int omnibook_hotkeys_write(char *buffer, struct omnibook_operation *io_op static int __init omnibook_hotkeys_init(struct omnibook_operation *io_op) { int retval; + printk(O_INFO "Enabling all hotkeys.\n"); - retval = omnibook_hotkeys_set(io_op, HKEY_ON); + retval = hotkeys_set_save(io_op, HKEY_ON); return retval < 0 ? retval : 0; } static void __exit omnibook_hotkeys_cleanup(struct omnibook_operation *io_op) { printk(O_INFO "Disabling all hotkeys.\n"); - omnibook_hotkeys_set(io_op, HKEY_OFF); + hotkeys_set_save(io_op, HKEY_OFF); } -static struct omnibook_tbl hotkey_table[] __initdata = { +static struct omnibook_tbl hotkeys_table[] __initdata = { {XE3GF | XE3GC | OB500 | OB510 | OB6000 | OB6100 | XE4500 | AMILOD | TSP10, COMMAND(KBC,OMNIBOOK_KBC_CMD_ONETOUCH_ENABLE,OMNIBOOK_KBC_CMD_ONETOUCH_DISABLE)}, {TSM30X, {CDI,}}, @@ -181,7 +187,7 @@ static struct omnibook_feature __declared_feature hotkeys_driver = { .ectypes = XE3GF | XE3GC | OB500 | OB510 | OB6000 | OB6100 | XE4500 | AMILOD | TSP10 | TSM30X | TSM40, - .tbl = hotkey_table, + .tbl = hotkeys_table, }; module_param_named(hotkeys, hotkeys_driver.enabled, int, S_IRUGO); diff --git a/init.c b/init.c index 9d74019..9c9ba5f 100644 --- a/init.c +++ b/init.c @@ -22,9 +22,8 @@ #include #include -#include "ec.h" +#include "hardware.h" #include "laptop.h" -#include "compat.h" #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) #include @@ -87,6 +86,8 @@ static char *laptop_model __initdata; static int omnibook_userset = 0; +struct input_dev *omnibook_input_dev; + /* * The platform_driver interface was added in linux 2.6.15 */ @@ -278,13 +279,8 @@ static int __init omnibook_init(struct omnibook_feature *feature) if (omnibook_userset) pmode |= S_IWUGO; } - /* - * FIXME: Special case for apmemu (not under /proc/omnibook) - */ - if (feature->proc_entry) - proc_entry = create_proc_entry(feature->proc_entry, pmode, NULL); - else - proc_entry = create_proc_entry(feature->name, pmode, omnibook_proc_root); + + proc_entry = create_proc_entry(feature->name, pmode, omnibook_proc_root); if (!proc_entry) { printk(O_ERR "Unable to create proc entry %s\n", feature->name); @@ -315,9 +311,13 @@ static int __init omnibook_init(struct omnibook_feature *feature) static int __init omnibook_probe(struct platform_device *dev) { int i; - struct list_head *p; struct omnibook_feature *feature; + /* temporary hack */ + mutex_init(&kbc_backend.mutex); + mutex_init(&pio_backend.mutex); + mutex_init(&ec_backend.mutex); + omnibook_available_feature = kzalloc(sizeof(struct omnibook_feature), GFP_KERNEL); if (!omnibook_available_feature) return -ENOMEM; @@ -335,8 +335,7 @@ static int __init omnibook_probe(struct platform_device *dev) } printk(O_INFO "Enabled features:"); - list_for_each(p, &omnibook_available_feature->list) { - feature = list_entry(p, struct omnibook_feature, list); + list_for_each_entry(feature, &omnibook_available_feature->list, list) { if (feature->name) printk(" %s", feature->name); } @@ -350,21 +349,17 @@ static int __init omnibook_probe(struct platform_device *dev) */ static int __exit omnibook_remove(struct platform_device *dev) { - struct list_head *p, *n; - struct omnibook_feature *feature; + struct omnibook_feature *feature, *temp; - list_for_each_safe(p, n, &omnibook_available_feature->list) { - feature = list_entry(p, struct omnibook_feature, list); - list_del(p); + list_for_each_entry_safe(feature, temp, &omnibook_available_feature->list, list) { + list_del(&feature->list); /* Feature specific cleanup */ if (feature->exit) feature->exit(feature->io_op); /* Generic backend cleanup */ if (feature->io_op && feature->io_op->backend->exit) feature->io_op->backend->exit(feature->io_op); - if (feature->proc_entry) - remove_proc_entry(feature->proc_entry, NULL); - else if (feature->name) + if (feature->name) remove_proc_entry(feature->name, omnibook_proc_root); kfree(feature->io_op); } @@ -379,11 +374,9 @@ static int __exit omnibook_remove(struct platform_device *dev) static int omnibook_suspend(struct platform_device *dev, pm_message_t state) { int retval; - struct list_head *p; struct omnibook_feature *feature; - list_for_each(p, &omnibook_available_feature->list) { - feature = list_entry(p, struct omnibook_feature, list); + list_for_each_entry(feature, &omnibook_available_feature->list, list) { if (feature->suspend) { retval = feature->suspend(feature->io_op); if (retval) @@ -399,11 +392,9 @@ static int omnibook_suspend(struct platform_device *dev, pm_message_t state) static int omnibook_resume(struct platform_device *dev) { int retval; - struct list_head *p; struct omnibook_feature *feature; - list_for_each(p, &omnibook_available_feature->list) { - feature = list_entry(p, struct omnibook_feature, list); + list_for_each_entry(feature, &omnibook_available_feature->list, list) { if (feature->resume) { retval = feature->resume(feature->io_op); if (retval) @@ -413,6 +404,20 @@ static int omnibook_resume(struct platform_device *dev) return 0; } +/* + * Find a given available feature by its name + */ +struct omnibook_feature *omnibook_find_feature(char *name) +{ + struct omnibook_feature *feature; + + list_for_each_entry(feature, &omnibook_available_feature->list, list) { + if (!strcmp(feature->name, name)) + return feature; + } + return NULL; +} + /* * Maintain compatibility with the old ectype numbers: * ex: The user set/get ectype=12 for TSM30X=2^(12-1) diff --git a/kbc.c b/kbc.c new file mode 100644 index 0000000..7177a7c --- /dev/null +++ b/kbc.c @@ -0,0 +1,155 @@ +/* + * kbc.c -- low level functions to access Keyboard Controller + * + * 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, 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. + * + * Written by Soós Péter , 2002-2004 + * Modified by Mathieu Bérard , 2006 + */ + +#include "omnibook.h" + +#include +#include +#include +#include +#include + +#include +#include "hardware.h" + +extern int omnibook_key_polling_enable(void); +extern int omnibook_key_polling_disable(void); + +/* + * Registers of the keyboard controller + */ + +#define OMNIBOOK_KBC_DATA 0x60 +#define OMNIBOOK_KBC_SC 0x64 + +/* + * Keyboard controller status register bits + */ + +#define OMNIBOOK_KBC_STAT_OBF 0x01 /* Output buffer full */ +#define OMNIBOOK_KBC_STAT_IBF 0x02 /* Input buffer full */ +#define OMNIBOOK_KBC_STAT_CMD 0x08 /* Last write was a command write (0=data) */ + +/* + * Interrupt control + */ + +static DEFINE_SPINLOCK(omnibook_kbc_lock); + +/* + * Wait for keyboard buffer + */ + +static int omnibook_kbc_wait(u8 event) +{ + int timeout = OMNIBOOK_TIMEOUT; + + switch (event) { + case OMNIBOOK_KBC_STAT_OBF: + while (!(inb(OMNIBOOK_KBC_SC) & event) && timeout--) + mdelay(1); + break; + case OMNIBOOK_KBC_STAT_IBF: + while ((inb(OMNIBOOK_KBC_SC) & event) && timeout--) + mdelay(1); + break; + default: + return -EINVAL; + } + if (timeout > 0) + return 0; + return -ETIME; +} + +/* + * Write to the keyboard command register + */ + +static int omnibook_kbc_write_command(u8 cmd) +{ + int retval; + + spin_lock_irq(&omnibook_kbc_lock); + retval = omnibook_kbc_wait(OMNIBOOK_KBC_STAT_IBF); + if (retval) + goto end; + outb(cmd, OMNIBOOK_KBC_SC); + retval = omnibook_kbc_wait(OMNIBOOK_KBC_STAT_IBF); + end: + spin_unlock_irq(&omnibook_kbc_lock); + return retval; +} + +/* + * Write to the keyboard data register + */ + +static int omnibook_kbc_write_data(u8 data) +{ + int retval; + + spin_lock_irq(&omnibook_kbc_lock); + retval = omnibook_kbc_wait(OMNIBOOK_KBC_STAT_IBF); + if (retval) + goto end;; + outb(data, OMNIBOOK_KBC_DATA); + retval = omnibook_kbc_wait(OMNIBOOK_KBC_STAT_IBF); + end: + spin_unlock_irq(&omnibook_kbc_lock); + return retval; +} + +/* + * Send a command to keyboard controller + */ + +static int omnibook_kbc_command(const struct omnibook_operation *io_op, u8 data) +{ + int retval; + + if ((retval = omnibook_kbc_write_command(OMNIBOOK_KBC_CONTROL_CMD))) + return retval; + + retval = omnibook_kbc_write_data(data); + return retval; +} + +/* + * Onetouch button hotkey handler + */ +static int omnibook_kbc_hotkeys(const struct omnibook_operation *io_op, unsigned int state) +{ + int retval; + + retval = __omnibook_toggle(io_op, !!(state & HKEY_ONETOUCH)); + return retval; +} + +/* + * Backend interface declarations + */ + +struct mutex kbc_mutex; + +struct omnibook_backend kbc_backend = { + .name = "i8042", + .hotkeys_write_cap = HKEY_ONETOUCH, + .byte_write = omnibook_kbc_command, + .hotkeys_set = omnibook_kbc_hotkeys, +}; + +/* End of file */ diff --git a/lcd.c b/lcd.c index c6ddfcd..df348a8 100644 --- a/lcd.c +++ b/lcd.c @@ -23,12 +23,11 @@ #include #endif -#include "ec.h" +#include "hardware.h" -static unsigned int omnibook_max_brightness; +unsigned int omnibook_max_brightness; #ifdef CONFIG_OMNIBOOK_BACKLIGHT - static struct backlight_device *omnibook_backlight_device; static int omnibook_get_backlight(struct backlight_device *bd); @@ -40,9 +39,6 @@ static struct backlight_properties omnibookbl_data = { .update_status = omnibook_set_backlight, }; -#endif - -#ifdef CONFIG_OMNIBOOK_BACKLIGHT static int omnibook_get_backlight(struct backlight_device *bd) { int retval = 0; @@ -50,7 +46,7 @@ static int omnibook_get_backlight(struct backlight_device *bd) u8 brgt; io_op = class_get_devdata(&bd->class_dev); - retval = io_op->backend->byte_read(io_op, &brgt); + retval = backend_byte_read(io_op, &brgt); if (!retval) retval = brgt; @@ -63,16 +59,16 @@ static int omnibook_set_backlight(struct backlight_device *bd) struct omnibook_operation *io_op; io_op = class_get_devdata(&bd->class_dev); - return io_op->backend->byte_write(io_op, intensity); + return backend_byte_write(io_op, intensity); } -#endif +#endif /* CONFIG_OMNIBOOK_BACKLIGHT */ static int omnibook_brightness_read(char *buffer, struct omnibook_operation *io_op) { int len = 0; u8 brgt; - io_op->backend->byte_read(io_op, &brgt); + backend_byte_read(io_op, &brgt); len += sprintf(buffer + len, "LCD brightness: %2d (max value: %d)\n", brgt, @@ -83,7 +79,7 @@ static int omnibook_brightness_read(char *buffer, struct omnibook_operation *io_ static int omnibook_brightness_write(char *buffer, struct omnibook_operation *io_op) { - int brgt = 0; + unsigned int brgt = 0; char *endp; if (strncmp(buffer, "off", 3) == 0) @@ -92,12 +88,12 @@ static int omnibook_brightness_write(char *buffer, struct omnibook_operation *io omnibook_lcd_blank(0); else { brgt = simple_strtoul(buffer, &endp, 10); - if ((endp == buffer) || (brgt < 0) || (brgt > omnibook_max_brightness)) + if ((endp == buffer) || (brgt > omnibook_max_brightness)) return -EINVAL; else { - io_op->backend->byte_write(io_op, brgt); + backend_byte_write(io_op, brgt); #ifdef CONFIG_OMNIBOOK_BACKLIGHT - omnibookbl_data.brightness=brgt; + omnibookbl_data.brightness = brgt; #endif } } @@ -127,7 +123,7 @@ static int __init omnibook_brightness_init(struct omnibook_operation *io_op) } #ifdef CONFIG_OMNIBOOK_BACKLIGHT - io_op->backend->byte_read(io_op, (u8*) &omnibookbl_data.brightness); + backend_byte_read(io_op, (u8*) &omnibookbl_data.brightness); omnibookbl_data.max_brightness = omnibook_max_brightness; omnibook_backlight_device = backlight_device_register(OMNIBOOK_MODULE_NAME, (void *)io_op, &omnibookbl_data); diff --git a/lib.c b/lib.c new file mode 100644 index 0000000..04328b8 --- /dev/null +++ b/lib.c @@ -0,0 +1,73 @@ +/* + * lib.c -- Generic helpers functions + * + * 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, 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. + * + * Written by Soós Péter , 2002-2004 + * Modified by Mathieu Bérard , 2006 + */ + +#include "omnibook.h" + +#include "hardware.h" +#include "compat.h" +#include + +int __omnibook_apply_write_mask(const struct omnibook_operation *io_op, int toggle) +{ + int retval = 0; + int mask; + u8 data; + + if ((retval = __backend_byte_read(io_op, &data))) + return retval; + + if (toggle == 1) + mask = io_op->on_mask; + else if (toggle == 0) + mask = io_op->off_mask; + else + return -EINVAL; + + if (mask > 0) + data |= (u8) mask; + else if (mask < 0) + data &= ~((u8) (-mask)); + else + return -EINVAL; + + retval = __backend_byte_write(io_op, data); + + return retval; +} + +/* + * Helper for toggle like operations + */ +int __omnibook_toggle(const struct omnibook_operation *io_op, int toggle) +{ + int retval; + u8 data; + + data = toggle ? io_op->on_mask : io_op->off_mask; + retval = __backend_byte_write(io_op, data); + return retval; +} + +void omnibook_report_key( struct input_dev *dev, unsigned int keycode) +{ + input_report_key(dev, keycode, 1); + input_sync(dev); + input_report_key(dev, keycode, 0); + input_sync(dev); +} + +/* End of file */ diff --git a/muteled.c b/muteled.c index 78b1b96..751b8ef 100644 --- a/muteled.c +++ b/muteled.c @@ -16,18 +16,26 @@ */ #include "omnibook.h" -#include "ec.h" - -/* There is no information about reading MUTE LED status */ -static int omnibook_muteled_enabled = 0; +#include "hardware.h" static int omnibook_muteled_set(struct omnibook_operation *io_op, int status) { - if (omnibook_toggle(io_op, !!status)) { + int retval = 0; + + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + + if((retval = __omnibook_toggle(io_op, !!status))) { printk(O_ERR "Failed muteled %s command.\n", status ? "on" : "off"); - return -EIO; + goto out; } - return 0; + + io_op->backend->misc_state = + (io_op->backend->misc_state & ~MUTELED) | (MUTELED * !!status); + + out: + mutex_unlock(&io_op->backend->mutex); + return retval; } /* @@ -37,10 +45,13 @@ static int omnibook_muteled_read(char *buffer, struct omnibook_operation *io_op) { int len = 0; + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; len += sprintf(buffer + len, "Last mute LED action was an %s command.\n", - (omnibook_muteled_enabled) ? "on" : "off"); + (io_op->backend->misc_state & MUTELED) ? "on" : "off"); + mutex_unlock(&io_op->backend->mutex); return len; } @@ -51,8 +62,7 @@ static int omnibook_muteled_write(char *buffer, struct omnibook_operation *io_op if (*buffer == '0' || *buffer == '1') { cmd = *buffer - '0'; if (!omnibook_muteled_set(io_op, cmd)) { - omnibook_muteled_enabled = cmd; - printk(O_INFO "Switching mute LED to %s state.\n", cmd ? "on" : "off"); + dprintk("Switching mute LED to %s state.\n", cmd ? "on" : "off"); } } else { return -EINVAL; @@ -60,9 +70,24 @@ static int omnibook_muteled_write(char *buffer, struct omnibook_operation *io_op return 0; } +/* + * May re-enable muteled upon resume + */ static int omnibook_muteled_resume(struct omnibook_operation *io_op) +{ + int retval; + mutex_lock(&io_op->backend->mutex); + retval = __omnibook_toggle(io_op, !!(io_op->backend->misc_state & MUTELED)); + mutex_unlock(&io_op->backend->mutex); + return retval; +} + +/* + * Switch muteled off upon exit + */ +static void __exit omnibook_muteled_cleanup(struct omnibook_operation *io_op) { - return omnibook_muteled_set(io_op, omnibook_muteled_enabled); + omnibook_muteled_set(io_op, 0); } static struct omnibook_tbl muteled_table[] __initdata = { @@ -75,6 +100,7 @@ static struct omnibook_feature __declared_feature muteled_driver = { .enabled = 1, .read = omnibook_muteled_read, .write = omnibook_muteled_write, + .exit = omnibook_muteled_cleanup, .resume = omnibook_muteled_resume, .ectypes = XE4500, .tbl = muteled_table, diff --git a/nbsmi.c b/nbsmi.c index 99c54d9..c8da23b 100644 --- a/nbsmi.c +++ b/nbsmi.c @@ -21,14 +21,16 @@ */ #include "omnibook.h" - +#include "hardware.h" #include #include #include #include #include -#include "ec.h" -#include "compat.h" +#include + +/* copied from drivers/input/serio/i8042-io.h */ +#define I8042_KBD_PHYS_DESC "isa0060/serio0" /* * ATI's IXP PCI-LPC bridge @@ -59,19 +61,16 @@ #define BTAT_MASK 0x2 /* - * We serialize access to this backend using a mutex * Crital sections around #SMI triggering are run atomically using a spinlock */ -static DEFINE_MUTEX(smi_lock); static DEFINE_SPINLOCK(smi_spinlock); /* * Private data of this backend */ -static struct kref *refcount; static struct pci_dev *lpc_bridge; /* Southbridge chip ISA bridge/LPC interface PCI device */ static u8 start_offset; -static int already_failed = 0; /* Backend init already failed at leat once */ +static struct input_dev *nbsmi_input_dev; /* * Possible list of supported southbridges @@ -85,11 +84,7 @@ extern const struct pci_device_id lpc_bridge_table[]; * Since we are going to trigger an SMI, all registers (I assume this does not * include esp and maybe ebp) and eflags may be mangled in the * process. - * So we save and restore all registers and eflags using the stack. * We also disable preemtion and IRQs upon SMI call. - * FIXME: To be sorted out: - * -> Can we reliably use spin_lock_irqsave/restore and remove the pushf/popf ? - * -> Can we remove the pusha/popa and add eax ebx ecx edx esi edi to clobber list ? */ static inline u32 ati_do_smi_call( u16 function) @@ -146,6 +141,7 @@ static inline u32 intel_do_smi_call(u16 function) sci_en = sci_en & 0xff80; /* Keep bits 15:7 */ sci_en += INTEL_GPE0_EN; /* GPEO_EN offset */ state = inl(sci_en); + outl(0,sci_en); /* * eflags, eax, ebx, ecx, edx, esi and edi are clobbered upon writing to SMI_PORT @@ -222,9 +218,6 @@ static int nbsmi_smi_read_command(const struct omnibook_operation *io_op, u8 * d if (!lpc_bridge) return -ENODEV; - if (mutex_lock_interruptible(&smi_lock)) - return -ERESTARTSYS; - inputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL); if (!inputbuffer) { retval = -ENOMEM; @@ -251,7 +244,6 @@ static int nbsmi_smi_read_command(const struct omnibook_operation *io_op, u8 * d error2: kfree(inputbuffer); error1: - mutex_unlock(&smi_lock); return retval; } @@ -264,9 +256,6 @@ static int nbsmi_smi_write_command(const struct omnibook_operation *io_op, u8 da if (!lpc_bridge) return -ENODEV; - if (mutex_lock_interruptible(&smi_lock)) - return -ERESTARTSYS; - inputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL); if (!inputbuffer) { retval = -ENOMEM; @@ -287,63 +276,218 @@ static int nbsmi_smi_write_command(const struct omnibook_operation *io_op, u8 da error2: kfree(inputbuffer); error1: - mutex_unlock(&smi_lock); return retval; } /* * Read/Write to INDEX/DATA interface at port 0x300 (SMSC Mailbox registers) - * Used by Hotkeys feature under already taken mutex. */ -static void nbsmi_ec_read_command(u16 index, u16 * data) +void nbsmi_ec_read_command(u8 index, u8 * data) { spin_lock_irq(&smi_spinlock); - outw(index, EC_INDEX_PORT); - *data = inw(EC_DATA_PORT); + outb(index, EC_INDEX_PORT); + *data = inb(EC_DATA_PORT); spin_unlock_irq(&smi_spinlock); } -static void nbsmi_ec_write_command(u16 index, u16 data) +#if 0 +static void nbsmi_ec_write_command(u8 index, u8 data) { spin_lock_irq(&smi_spinlock); - outw(index, EC_INDEX_PORT); - outw(data, EC_DATA_PORT); + outb(index, EC_INDEX_PORT); + outb(data, EC_DATA_PORT); spin_unlock_irq(&smi_spinlock); } +#endif + + +/* + * Hotkeys workflow: + * 1. Fn+Foo pressed + * 2. Scancode 0x6d generated by kbd controller + * 3. Scancode 0x6d caught by omnibook input handler + * 4. SMI Call issued -> Got keycode of last actually pressed Fn key + * 5. nbsmi_scan_table used to associate a detected keycode with a generated one + * 6. Generated keycode issued using the omnibook input device + */ + +/* + * The input handler should only bind with the standard AT keyboard. + * XXX: Scancode 0x6d won't be detected if the keyboard has already been + * grabbed (the Xorg event input driver do that) + */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) +static struct input_handle *hook_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +#else +static struct input_handle *hook_connect(struct input_handler *handler, + struct input_dev *dev, + struct input_device_id *id) +#endif +{ + struct input_handle *handle; + + /* the 0x0001 vendor magic number is found in atkbd.c */ + if(!(dev->id.bustype == BUS_I8042 && dev->id.vendor == 0x0001)) + return NULL; + + if(!strstr(dev->phys, I8042_KBD_PHYS_DESC)) + return NULL; + + dprintk("hook_connect for device %s.\n", dev->name); + + if(dev->grab) + printk(O_WARN "Input device is grabbed by %s, Fn hotkeys won't work.\n", + dev->grab->name); + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return NULL; + + handle->dev = dev; + handle->handler = handler; + handle->name = "omnibook_scancode_hook"; + + input_open_device(handle); + + return handle; +} + +static void hook_disconnect(struct input_handle *handle) +{ + dprintk("hook_disconnect.\n"); + input_close_device(handle); + kfree(handle); +} + +/* + * Hook for scancode 0x6d. Actual handling is done in a workqueue as + * the nbsmi backend might sleep. + */ + +static void omnibook_handle_fnkey(void* data); +DECLARE_WORK(omnibook_fnkey_work, *omnibook_handle_fnkey, NULL); + +static void hook_event(struct input_handle *handle, unsigned int event_type, + unsigned int event_code, int value) +{ + if (event_type == EV_MSC && event_code == MSC_SCAN && value == SMI_FN_SCAN ) + schedule_work(&omnibook_fnkey_work); +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) +static const struct input_device_id hook_ids[] = { +#else +static struct input_device_id hook_ids[] = { +#endif + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT(EV_KEY) }, + }, + { }, /* Terminating entry */ +}; + +static struct input_handler hook_handler = { + .event = hook_event, + .connect = hook_connect, + .disconnect = hook_disconnect, + .name = OMNIBOOK_MODULE_NAME, + .id_table = hook_ids, +}; + +/* + * Define some KEY_ that may be missing in input.h for some kernel versions + */ +#ifndef KEY_WLAN +#define KEY_WLAN 238 +#endif + +/* + * Detected scancode to keycode table + */ +static struct { + unsigned int scancode; + unsigned int keycode; +} nbsmi_scan_table[] = { + { KEY_ESC, KEY_MUTE}, + { KEY_F1, KEY_FN_F1}, + { KEY_F2, KEY_PROG1}, + { KEY_F3, KEY_SLEEP}, + { KEY_F4, KEY_SUSPEND}, + { KEY_F5, KEY_SWITCHVIDEOMODE}, + { KEY_F6, KEY_BRIGHTNESSDOWN}, + { KEY_F7, KEY_BRIGHTNESSUP}, + { KEY_F8, KEY_WLAN}, + { KEY_F9, KEY_FN_F9}, + { KEY_SPACE, KEY_ZOOM}, + { 0,0}, +}; + +/* + * Register the input handler and the input device in the input subsystem + */ +static int register_input_subsystem(void) +{ + int i, retval = 0; + + nbsmi_input_dev = input_allocate_device(); + if (!nbsmi_input_dev) { + retval = -ENOMEM; + goto out; + } + + nbsmi_input_dev->name = "Omnibook NbSMI scancode generator"; + nbsmi_input_dev->phys = "omnibook/input0"; + nbsmi_input_dev->id.bustype = BUS_HOST; + + set_bit(EV_KEY, nbsmi_input_dev->evbit); + + for(i=0 ; i < ARRAY_SIZE(nbsmi_scan_table); i++) + set_bit(nbsmi_scan_table[i].keycode, nbsmi_input_dev->keybit); + + retval = input_register_device(nbsmi_input_dev); + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) + retval = input_register_handler(&hook_handler); + +#else + input_register_handler(&hook_handler); +#endif + + out: + return retval; +} + /* * Try to init the backend * This function can be called blindly as it use a kref * to check if the init sequence was already done. */ - static int omnibook_nbsmi_init(const struct omnibook_operation *io_op) { int retval = 0; int i; - u16 ec_data; + u8 ec_data; u32 smi_port = 0; -/* ectypes other than TSM40 have no business with this backend */ + /* ectypes other than TSM40 have no business with this backend */ if (!(omnibook_ectype & TSM40)) return -ENODEV; - if (already_failed) { + if (io_op->backend->already_failed) { dprintk("NbSmi backend init already failed, skipping.\n"); return -ENODEV; } - if (!refcount) { + if (!lpc_bridge) { /* Fist use of the backend */ - mutex_lock(&smi_lock); dprintk("Try to init NbSmi\n"); - refcount = kmalloc(sizeof(struct kref), GFP_KERNEL); - if (!refcount) { - retval = -ENOMEM; - goto out; - } - - kref_init(refcount); + mutex_init(&io_op->backend->mutex); + mutex_lock(&io_op->backend->mutex); + kref_init(&io_op->backend->kref); /* PCI probing: find the LPC Super I/O bridge PCI device */ for (i = 0; !lpc_bridge && lpc_bridge_table[i].vendor; ++i) @@ -389,22 +533,24 @@ static int omnibook_nbsmi_init(const struct omnibook_operation *io_op) /* * Try some heuristic tests to avoid enabling this interface on unsuported laptops: - * See what a port 300h read index 8f gives. Guess there is nothing if read 0xffff + * See what a port 300h read index 8f gives. Guess there is nothing if read 0xff */ nbsmi_ec_read_command(SMI_FN_PRESSED, &ec_data); dprintk("NbSmi test probe read: %x\n", ec_data); - if (ec_data == 0xffff) { + if (ec_data == 0xff) { printk(O_ERR "Probing at SMSC Mailbox registers failed, disabling NbSmi\n"); retval = -ENODEV; goto error4; } + register_input_subsystem(); dprintk("NbSmi init ok\n"); - goto out; + mutex_unlock(&io_op->backend->mutex); + return 0; } else { dprintk("NbSmi has already been initialized\n"); - kref_get(refcount); + kref_get(&io_op->backend->kref); return 0; } error4: @@ -415,21 +561,30 @@ static int omnibook_nbsmi_init(const struct omnibook_operation *io_op) pci_dev_put(lpc_bridge); lpc_bridge = NULL; error1: - kfree(refcount); - refcount = NULL; - already_failed = 1; - out: - mutex_unlock(&smi_lock); + io_op->backend->already_failed = 1; + mutex_unlock(&io_op->backend->mutex); + mutex_destroy(&io_op->backend->mutex); return retval; } +/* + * Free all allocated stuff and unregister from the input subsystem + */ static void nbsmi_free(struct kref *ref) { u32 smi_port = 0; + struct omnibook_backend *backend; - mutex_lock(&smi_lock); dprintk("NbSmi not used anymore: disposing\n"); + flush_scheduled_work(); + input_unregister_handler(&hook_handler); + input_unregister_device(nbsmi_input_dev); + + backend = container_of(ref, struct omnibook_backend, kref); + + mutex_lock(&backend->mutex); + switch (lpc_bridge->vendor) { case PCI_VENDOR_ID_INTEL: smi_port = INTEL_SMI_PORT; @@ -444,18 +599,85 @@ static void nbsmi_free(struct kref *ref) pci_dev_put(lpc_bridge); release_region(smi_port, 2); release_region(EC_INDEX_PORT, 2); - kfree(refcount); lpc_bridge = NULL; - refcount = NULL; - mutex_unlock(&smi_lock); + mutex_unlock(&backend->mutex); + mutex_destroy(&backend->mutex); } static void omnibook_nbsmi_exit(const struct omnibook_operation *io_op) { -/* ectypes other than TSM40 have no business with this backend */ + /* ectypes other than TSM40 have no business with this backend */ BUG_ON(!(omnibook_ectype & TSM40)); dprintk("Trying to dispose NbSmi\n"); - kref_put(refcount, nbsmi_free); + kref_put(&io_op->backend->kref, nbsmi_free); +} + +/* + * Adjust the lcd backlight level by delta. + * Used for Fn+F6/F7 keypress + */ +static int adjust_brighness(int delta) +{ + struct omnibook_feature *lcd_feature = omnibook_find_feature("lcd"); + struct omnibook_operation *io_op; + int retval = 0; + u8 brgt; + + if(!lcd_feature) + return -ENODEV; + + io_op = lcd_feature->io_op; + + mutex_lock(&io_op->backend->mutex); + + if(( retval = __backend_byte_read(io_op, &brgt))) + goto out; + + dprintk("FnF6/F7 pressed: adjusting britghtnes.\n"); + + if (((int) brgt + delta) < 0) + brgt = 0; + else if ((brgt + delta) > omnibook_max_brightness) + brgt = omnibook_max_brightness; + else + brgt += delta; + + retval = __backend_byte_write(io_op, brgt); + + out: + mutex_unlock(&io_op->backend->mutex); + return retval; +} + +static const struct omnibook_operation last_scan_op = SIMPLE_BYTE(SMI,SMI_GET_FN_LAST_SCAN,0); + +/* + * Workqueue hanlder for Fn hotkeys + */ +static void omnibook_handle_fnkey(void* data) +{ + int i; + u8 gen_scan; + + if(nbsmi_smi_read_command(&last_scan_op, &gen_scan)) + return; + + dprintk("detected scancode %x.\n", gen_scan); + switch(gen_scan) { + case KEY_F6: + adjust_brighness(-1); + break; + case KEY_F7: + adjust_brighness(+1); + break; + } + for(i=0 ; i < ARRAY_SIZE(nbsmi_scan_table); i++) { + if( gen_scan == nbsmi_scan_table[i].scancode) { + dprintk("generating keycode %i.\n", nbsmi_scan_table[i].keycode); + omnibook_report_key(nbsmi_input_dev, nbsmi_scan_table[i].keycode); + break; + } + } } static int omnibook_nbsmi_get_wireless(const struct omnibook_operation *io_op, unsigned int *state) @@ -534,7 +756,7 @@ static int omnibook_nbmsi_hotkeys_get(const struct omnibook_operation *io_op, un *state |= (data & SMI_FN_TWICE_LOCK_MASK) ? HKEY_TWICE_LOCK : 0; *state |= (data & SMI_FN_DOCK_MASK) ? HKEY_DOCK : 0; - return HKEY_FN | HKEY_STICK | HKEY_TWICE_LOCK | HKEY_DOCK; + return 0; } #endif @@ -562,10 +784,8 @@ static int omnibook_nbmsi_hotkeys_set(const struct omnibook_operation *io_op, un dprintk("set_hotkeys (Fn F5) raw_state: %x\n", data); retval = nbsmi_smi_write_command(&hotkeys_op, data); - if (retval < 0) - return retval; - else - return HKEY_FN | HKEY_STICK | HKEY_TWICE_LOCK | HKEY_DOCK | HKEY_FNF5; + + return retval; } static const unsigned int nbsmi_display_mode_list[] = { @@ -619,6 +839,8 @@ static int omnibook_nbmsi_display_set(const struct omnibook_operation *io_op, un struct omnibook_backend nbsmi_backend = { .name = "nbsmi", +/* .hotkey_read_cap = HKEY_FN | HKEY_STICK | HKEY_TWICE_LOCK | HKEY_DOCK, */ + .hotkeys_write_cap = HKEY_FN | HKEY_STICK | HKEY_TWICE_LOCK | HKEY_DOCK | HKEY_FNF5, .init = omnibook_nbsmi_init, .exit = omnibook_nbsmi_exit, .byte_read = nbsmi_smi_read_command, diff --git a/omnibook.h b/omnibook.h index 06ed4af..e85e74c 100644 --- a/omnibook.h +++ b/omnibook.h @@ -18,6 +18,7 @@ #include #include +#include #include /* @@ -25,7 +26,7 @@ */ #define OMNIBOOK_MODULE_NAME "omnibook" -#define OMNIBOOK_MODULE_VERSION "2.20060000" +#define OMNIBOOK_MODULE_VERSION "2.20060000-exp" /* * EC types @@ -57,7 +58,6 @@ struct omnibook_operation; struct omnibook_feature { char *name; /* Name */ - char *proc_entry; /* Specify proc entry relative to /proc (will be omnibook/name otherwise) */ int enabled; /* Set from module parameter */ int (*read) (char *,struct omnibook_operation *); /* Procfile read function */ int (*write) (char *,struct omnibook_operation *);/* Procfile write function */ @@ -118,6 +118,16 @@ enum { HKEY_FNF5 = (1<<6), /* 64 Fn + F5 (toggle display) is enabled */ }; +#define HKEY_LAST_SHIFT 6 + +/* + * Various status bits + */ +enum { + MUTELED = (1<<0), /* Mute LED status */ + TOUCHPAD = (1<<1), /* Touchpad status */ +}; + /* * Display state backend neutral masks @@ -136,12 +146,11 @@ enum { DISPLAY_DVI_DET = (1<<7), /* 128 External DVI port */ }; - - -int omnibook_lcd_blank(int blank); -int omnibook_get_ac(struct omnibook_operation *io_op); -int omnibook_get_battery_status(int num, struct omnibook_battery_state *battstat); +extern unsigned int omnibook_max_brightness; int set_omnibook_param(const char *val, struct kernel_param *kp); +int omnibook_lcd_blank(int blank); +struct omnibook_feature *omnibook_find_feature(char *name); +void omnibook_report_key(struct input_dev *dev, unsigned int keycode); #define __declared_feature __attribute__ (( __section__(".features"), __aligned__(__alignof__ (struct omnibook_feature)))) __attribute_used__ @@ -161,14 +170,17 @@ int set_omnibook_param(const char *val, struct kernel_param *kp); #define dprintk_simple(fmt, args...) do { } while(0) #endif + + + /* * Configuration for standalone compilation: * -Register as backlight depends on kernel config (requires 2.6.17+ interface) - * -APM emulation is disabled by default + * -Legacy features disbled by default */ #ifdef OMNIBOOK_STANDALONE -#if (defined (CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE)) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) +#if (defined (CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE)) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) #define CONFIG_OMNIBOOK_BACKLIGHT #else #undef CONFIG_OMNIBOOK_BACKLIGHT diff --git a/pio.c b/pio.c new file mode 100644 index 0000000..e3d3a93 --- /dev/null +++ b/pio.c @@ -0,0 +1,174 @@ +/* + * pio.c -- low level functions I/O ports + * + * 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, 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. + * + * Written by Soós Péter , 2002-2004 + * Modified by Mathieu Bérard , 2006 + */ + +#include "omnibook.h" + +#include +#include +#include +#include +#include + +#include +#include "hardware.h" + +/* + * IO port backend. Only support single or dual ports operations + * private data structure: it's the linked list of requested ports + * + * Race condition issue: omnibook_pio_init/exit functions are only called from + * omnibook_backend_match and omnibook_remove from init.c, this should happen + * only at module init/exit time so there is no need for a lock. + */ + +struct pio_private_data_t { + unsigned long addr; + struct kref refcount; + struct list_head list; +}; + +static struct pio_private_data_t pio_private_data = { + .addr = 0, + .list = LIST_HEAD_INIT(pio_private_data.list), +}; + +/* + * Match an entry in the linked list helper function: see if we have and entry + * whose addr field match maddr + */ +static struct pio_private_data_t *omnibook_match_port(struct pio_private_data_t *data, + unsigned long maddr) +{ + struct pio_private_data_t *cursor; + + list_for_each_entry(cursor, &data->list, list) { + if (cursor->addr == maddr) { + return cursor; + } + } + return NULL; +} + +/* + * See if we have to request raddr + */ +static int omnibook_claim_port(struct pio_private_data_t *data, unsigned long raddr) +{ + struct pio_private_data_t *match, *new; + + match = omnibook_match_port(data, raddr); + if (match) { + /* Already requested by us: increment kref and quit */ + kref_get(&match->refcount); + return 0; + } + + /* there was no match: request the region and add to list */ + if (!request_region(raddr, 1, OMNIBOOK_MODULE_NAME)) { + printk(O_ERR "Request I/O port error\n"); + return -ENODEV; + } + + new = kmalloc(sizeof(struct pio_private_data_t), GFP_KERNEL); + if (!new) { + release_region(raddr, 1); + return -ENOMEM; + } + + kref_init(&new->refcount); + new->addr = raddr; + list_add(&new->list, &data->list); + + return 0; +} + +/* + * Register read_addr and write_addr + */ +static int omnibook_pio_init(const struct omnibook_operation *io_op) +{ + int retval = 0; + + if (io_op->read_addr + && (retval = omnibook_claim_port(io_op->backend->data, io_op->read_addr))) + goto out; + + if (io_op->write_addr && (io_op->write_addr != io_op->read_addr)) + retval = omnibook_claim_port(io_op->backend->data, io_op->write_addr); + + out: + return retval; +} + +/* + * REALLY release a port + */ +static void omnibook_free_port(struct kref *ref) +{ + struct pio_private_data_t *data; + + data = container_of(ref, struct pio_private_data_t, refcount); + release_region(data->addr, 1); + list_del(&data->list); + kfree(data); +} + +/* + * Unregister read_addr and write_addr + */ +static void omnibook_pio_exit(const struct omnibook_operation *io_op) +{ + struct pio_private_data_t *match; + + match = omnibook_match_port(io_op->backend->data, io_op->read_addr); + if (match) + kref_put(&match->refcount, omnibook_free_port); + + match = NULL; + match = omnibook_match_port(io_op->backend->data, io_op->write_addr); + if (match) + kref_put(&match->refcount, omnibook_free_port); + +} + +static int omnibook_io_read(const struct omnibook_operation *io_op, u8 * value) +{ + *value = inb(io_op->read_addr); + if (io_op->read_mask) + *value &= io_op->read_mask; + return 0; +} + +static int omnibook_io_write(const struct omnibook_operation *io_op, u8 value) +{ + outb(io_op->write_addr, value); + return 0; +} + +/* + * Backend interface declarations + */ +struct omnibook_backend pio_backend = { + .name = "pio", + .data = &pio_private_data, + .init = omnibook_pio_init, + .exit = omnibook_pio_exit, + .byte_read = omnibook_io_read, + .byte_write = omnibook_io_write, +}; + +/* End of file */ diff --git a/polling.c b/polling.c new file mode 100644 index 0000000..3befba9 --- /dev/null +++ b/polling.c @@ -0,0 +1,256 @@ +/* + * polling.c -- scancode emulation for volume buttons + * + * 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, 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. + * + * Written by Soós Péter , 2002-2004 + * Modified by Mathieu Bérard , 2006 + */ + +#include "omnibook.h" +#include "hardware.h" +#include + +/* Predefined convinient on/off states */ +#define HKEY_ON HKEY_ONETOUCH|HKEY_MULTIMEDIA|HKEY_FN|HKEY_DOCK|HKEY_FNF5|HKEY_VOL +#define HKEY_OFF 0 + +/* + * XE3GC type key_polling polling: + * + * Polling interval for keys (100 ms) + */ + +#define OMNIBOOK_POLL msecs_to_jiffies(100) + +/* + * workqueue manipulations are mutex protected and thus kept in sync with key_polling_enabled + */ +static struct workqueue_struct *omnibook_wq; +static int key_polling_enabled; +DEFINE_MUTEX(poll_mutex); + +static void omnibook_key_poller(void *data); +static struct omnibook_feature key_polling_driver; +DECLARE_WORK(omnibook_poll_work, *omnibook_key_poller, &key_polling_driver.io_op); + +static struct input_dev *poll_input_dev; + +static void omnibook_key_poller(void *data) +{ + u8 q0a; + int retval; + struct omnibook_operation *io_op; + + io_op = *( (struct omnibook_operation **) data); + + mutex_lock(&io_op->backend->mutex); + __backend_byte_read(io_op, &q0a); + __backend_byte_write(io_op, 0); + mutex_unlock(&io_op->backend->mutex); + +#ifdef OMNIBOOK_DEBUG + if (unlikely(q0a & XE3GC_SLPB_MASK)) + dprintk("Sleep button pressed.\n"); + if (unlikely(q0a & XE3GC_F5_MASK)) + dprintk("Fn-F5 - LCD/CRT switch pressed.\n"); + if (unlikely(q0a & XE3GC_CNTR_MASK)) + dprintk("Fn+F3/Fn+F4 - Contrast up or down pressed.\n"); + if (unlikely(q0a & XE3GC_BRGT_MASK)) + dprintk("Fn+F1/Fn+F2 - Brightness up or down pressed.\n"); +#endif + + /* + * Volume button scancode emulaton + * It emulates a key press and a release without repeat as other OneTouch buttons do. + */ + + if (unlikely(q0a & XE3GC_VOLD_MASK)) { + dprintk("Fn-down arrow or Volume down pressed.\n"); + omnibook_report_key(poll_input_dev, KEY_VOLUMEDOWN); + } + if (unlikely(q0a & XE3GC_VOLU_MASK)) { + dprintk("Fn-up arrow or Volume up pressed.\n"); + omnibook_report_key(poll_input_dev, KEY_VOLUMEUP); + } + if (unlikely(q0a & XE3GC_MUTE_MASK)) { + dprintk("Fn+F7 - Volume mute pressed.\n"); + omnibook_report_key(poll_input_dev, KEY_MUTE); + } + + retval = queue_delayed_work(omnibook_wq, &omnibook_poll_work, OMNIBOOK_POLL); + if(unlikely(!retval)) /* here non-zero on success */ + printk(O_ERR "Key_poller failed to rearm.\n"); +} + +static int omnibook_key_polling_enable(void) +{ + int retval = 0; + + if(mutex_lock_interruptible(&poll_mutex)) + return -ERESTARTSYS; + + if(key_polling_enabled) + goto out; + + retval = !queue_delayed_work(omnibook_wq, &omnibook_poll_work, OMNIBOOK_POLL); + if(retval) + printk(O_ERR "Key_poller enabling failed.\n"); + else { + dprintk("Scancode emulation for volume buttons enabled.\n"); + key_polling_enabled = 1; + } + + out: + mutex_unlock(&poll_mutex); + return retval; +} + +static int omnibook_key_polling_disable(void) +{ + if(mutex_lock_interruptible(&poll_mutex)) + return -ERESTARTSYS; + + if(!key_polling_enabled) + goto out; + + cancel_rearming_delayed_workqueue(omnibook_wq, &omnibook_poll_work); + dprintk("Scancode emulation for volume buttons disabled.\n"); + key_polling_enabled = 0; + + out: + mutex_unlock(&poll_mutex); + return 0; +} + + +static int omnibook_key_polling_read(char *buffer, struct omnibook_operation *io_op) +{ + int len = 0; + + if(mutex_lock_interruptible(&poll_mutex)) + return -ERESTARTSYS; + + len += sprintf(buffer + len, "Volume buttons polling is %s.\n", + (key_polling_enabled) ? "enabled" : "disabled"); +#ifdef OMNIBOOK_DEBUG + if(key_polling_enabled) + len += sprintf(buffer + len, "Will poll in %i msec.\n", + jiffies_to_msecs(omnibook_poll_work.timer.expires - jiffies)); +#endif + mutex_unlock(&poll_mutex); + return len; +} + +static int omnibook_key_polling_write(char *buffer, struct omnibook_operation *io_op) +{ + int retval; + switch (*buffer) { + case '0': + retval = omnibook_key_polling_disable(); + break; + case '1': + retval = omnibook_key_polling_enable(); + break; + default: + retval = -EINVAL; + } + return retval; +} + + +/* + * Stop polling upon suspend an restore it upon resume + */ +static int omnibook_key_polling_resume(struct omnibook_operation *io_op) +{ + int retval = 0; + + mutex_lock(&poll_mutex); + if(key_polling_enabled) + retval = !queue_delayed_work(omnibook_wq, &omnibook_poll_work, OMNIBOOK_POLL); + mutex_unlock(&poll_mutex); + return retval; +} + +static int omnibook_key_polling_suspend(struct omnibook_operation *io_op) +{ + mutex_lock(&poll_mutex); + if(key_polling_enabled) + cancel_rearming_delayed_workqueue(omnibook_wq, &omnibook_poll_work); + mutex_unlock(&poll_mutex); + return 0; +} + +static int __init omnibook_key_polling_init(struct omnibook_operation *io_op) +{ + int retval = 0; + + poll_input_dev = input_allocate_device(); + if (!poll_input_dev) { + retval = -ENOMEM; + goto out; + } + + poll_input_dev->name = "Omnibook legacy laptop scancode generator"; + poll_input_dev->phys = "omnibook/input0"; + poll_input_dev->id.bustype = BUS_HOST; + + /* this device has three keys */ + set_bit(EV_KEY, poll_input_dev->evbit); + set_bit(KEY_VOLUMEDOWN, poll_input_dev->keybit); + set_bit(KEY_VOLUMEUP, poll_input_dev->keybit); + set_bit(KEY_MUTE, poll_input_dev->keybit); + + retval = input_register_device(poll_input_dev); + if (retval) { + input_free_device(poll_input_dev); + goto out; + } + + omnibook_wq = create_singlethread_workqueue("omnibook"); + if(!omnibook_wq) + retval = -ENOMEM; + else + retval = omnibook_key_polling_enable(); + +out: + return retval; +} + +static void __exit omnibook_key_polling_cleanup(struct omnibook_operation *io_op) +{ + omnibook_key_polling_disable(); + destroy_workqueue(omnibook_wq); + input_unregister_device(poll_input_dev); +} + +static struct omnibook_tbl key_polling_table[] __initdata = { + {XE3GC, SIMPLE_BYTE(EC, XE3GC_Q0A, 0)}, + {0,} +}; + +static struct omnibook_feature __declared_feature key_polling_driver = { + .name = "key_polling", + .enabled = 0, /* dangerous */ + .read = omnibook_key_polling_read, + .write = omnibook_key_polling_write, + .init = omnibook_key_polling_init, + .exit = omnibook_key_polling_cleanup, + .suspend = omnibook_key_polling_suspend, + .resume = omnibook_key_polling_resume, + .ectypes = XE3GC, + .tbl = key_polling_table, +}; + +module_param_named(key_polling, key_polling_driver.enabled, int, S_IRUGO); +MODULE_PARM_DESC(key_polling, "Use 0 to disable, 1 to enable key polling"); +/* End of file */ diff --git a/temperature.c b/temperature.c index 61bc6c3..d5c109e 100644 --- a/temperature.c +++ b/temperature.c @@ -16,7 +16,7 @@ */ #include "omnibook.h" -#include "ec.h" +#include "hardware.h" static int omnibook_temperature_read(char *buffer, struct omnibook_operation *io_op) { @@ -24,7 +24,7 @@ static int omnibook_temperature_read(char *buffer, struct omnibook_operation *io int retval; u8 temp; - if ((retval = io_op->backend->byte_read(io_op, &temp))) + if ((retval = backend_byte_read(io_op, &temp))) return retval; len += sprintf(buffer + len, "CPU temperature: %2d C\n", temp); diff --git a/touchpad.c b/touchpad.c index 93035af..679d2d3 100644 --- a/touchpad.c +++ b/touchpad.c @@ -16,27 +16,37 @@ */ #include "omnibook.h" -#include "ec.h" - -/* Touchpad is assumed to be enabled by default */ -static int omnibook_touchpad_enabled = 1; +#include "hardware.h" static int omnibook_touchpad_set(struct omnibook_operation *io_op, int status) { int retval = 0; - if ((retval = omnibook_toggle(io_op, !!status))) { + + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + + if ((retval = __omnibook_toggle(io_op, !!status))) { printk(O_ERR "Failed touchpad %sable command.\n", status ? "en" : "dis"); + goto out; } + + io_op->backend->misc_state = + (io_op->backend->misc_state & ~TOUCHPAD) | (TOUCHPAD * !!status); + + out: + mutex_unlock(&io_op->backend->mutex); return retval; } /* - * Power management handlers: redisable touchpad on resume (if requested) + * Power management handlers: redisable touchpad on resume (if necessary) */ static int omnibook_touchpad_resume(struct omnibook_operation *io_op) { int retval; - retval = (omnibook_touchpad_enabled ? 0 : omnibook_touchpad_set(io_op, 0)); + mutex_lock(&io_op->backend->mutex); + retval = __omnibook_toggle(io_op, !!(io_op->backend->misc_state & TOUCHPAD)); + mutex_unlock(&io_op->backend->mutex); return retval; } @@ -47,10 +57,14 @@ static int omnibook_touchpad_read(char *buffer, struct omnibook_operation *io_op { int len = 0; + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + len += sprintf(buffer + len, "Last touchpad action was an %s command.\n", - (omnibook_touchpad_enabled) ? "enable" : "disable"); + (io_op->backend->misc_state & TOUCHPAD) ? "enable" : "disable"); + mutex_unlock(&io_op->backend->mutex); return len; } @@ -61,8 +75,7 @@ static int omnibook_touchpad_write(char *buffer, struct omnibook_operation *io_o if (*buffer == '0' || *buffer == '1') { cmd = *buffer - '0'; if (!omnibook_touchpad_set(io_op, cmd)) { - omnibook_touchpad_enabled = cmd; - printk(O_INFO "%sabling touchpad.\n", cmd ? "En" : "Dis"); + dprintk("%sabling touchpad.\n", cmd ? "En" : "Dis"); } } else { return -EINVAL; @@ -70,6 +83,16 @@ static int omnibook_touchpad_write(char *buffer, struct omnibook_operation *io_o return 0; } + +static int __init omnibook_touchpad_init(struct omnibook_operation *io_op) +{ + mutex_lock(&io_op->backend->mutex); + /* Touchpad is assumed to be enabled by default */ + io_op->backend->misc_state |= TOUCHPAD; + mutex_unlock(&io_op->backend->mutex); + return 0; +} + /* * Reenable touchpad upon exit */ @@ -91,6 +114,7 @@ static struct omnibook_feature __declared_feature touchpad_driver = { .enabled = 1, .read = omnibook_touchpad_read, .write = omnibook_touchpad_write, + .init = omnibook_touchpad_init, .exit = omnibook_touchpad_cleanup, .resume = omnibook_touchpad_resume, .ectypes = XE3GF | XE3GC | TSP10 | TSM30X, diff --git a/wireless.c b/wireless.c index 045cd1d..aad5ca9 100644 --- a/wireless.c +++ b/wireless.c @@ -16,7 +16,7 @@ */ #include "omnibook.h" -#include "ec.h" +#include "hardware.h" static int omnibook_wifi_read(char *buffer, struct omnibook_operation *io_op) { @@ -24,7 +24,7 @@ static int omnibook_wifi_read(char *buffer, struct omnibook_operation *io_op) int retval; unsigned int state; - if ((retval = io_op->backend->aerial_get(io_op, &state))) + if ((retval = backend_aerial_get(io_op, &state))) return retval; len += @@ -45,23 +45,30 @@ static int omnibook_wifi_write(char *buffer, struct omnibook_operation *io_op) int retval = 0; unsigned int state; - if ((retval = io_op->backend->aerial_get(io_op, &state))) - return retval; + if(mutex_lock_interruptible(&io_op->backend->mutex)) + return -ERESTARTSYS; + + if ((retval = __backend_aerial_get(io_op, &state))) + goto out; if (*buffer == '0') state &= ~WIFI_STA; else if (*buffer == '1') state |= WIFI_STA; - else - return -EINVAL; + else { + retval = -EINVAL; + goto out; + } - if ((retval = io_op->backend->aerial_set(io_op, state))) + if ((retval = __backend_aerial_set(io_op, state))) return retval; + out: + mutex_unlock(&io_op->backend->mutex); return retval; } -static struct omnibook_feature wifi_feature; +static struct omnibook_feature wifi_driver; static int __init omnibook_wifi_init(struct omnibook_operation *io_op) { @@ -72,11 +79,11 @@ static int __init omnibook_wifi_init(struct omnibook_operation *io_op) * Refuse enabling/disabling a non-existent device */ - if ((retval = io_op->backend->aerial_get(io_op, &state))) + if ((retval = backend_aerial_get(io_op, &state))) return retval; if (!(state & WIFI_EX)) - wifi_feature.write = NULL; + wifi_driver.write = NULL; return retval; }