[RFC][PATCH] Generic Hotkey driver interface

Yu, Luming
2004-11-09T15:25:20Z

[-- Attachment #1: Type: text/plain, Size: 33753 bytes --]

To consolidate the hotkey implementation in ACPI subsystem,
I tried to implement a generic hotkey driver under ACPI.
I hope you can give it a review. Thanks in advances for any
comments.

The basic idea is to supply configurable interface
to userspace, thus user can configure the generic
hotkey driver to work for their specific laptop model.

Please note, the code is trying to prove the concept
is feasible. So there are large room to improve, including
bugs need to be fixed, redundant code need to be clean up,
critical section need to be protected by lock or semaphore,
memory leak need to be fix up, interfaces need to be revised.

The code is inspired by Karol Kozimor, Borislav Deianov,
Hiroshi Miura, Julien Lerouge, Alex Williamson, and Len.

Thanks,
Luming

diff -BruN orig/Documentation/acpi-hotkey.txt
2.6/Documentation/acpi-hotkey.txt
--- orig/Documentation/acpi-hotkey.txt 1970-01-01 08:00:00.000000000
+0800
+++ 2.6/Documentation/acpi-hotkey.txt 2004-11-09 23:07:25.000000000
+0800
@@ -0,0 +1,35 @@
+The enclosed hotkey.c implement:
+1. /proc/acpi/hotkey/event_config
+(event based hotkey or event config interface):
+a. add a event based hotkey(event) :
+echo "0:bus::action:method:num:num" > event_config
+
+b. delete a event based hotkey(event):
+echo "1:::::num:num" > event_config
+
+c. modify a event based hotkey(event):
+echo "2:bus::action:method:num:num" > event_config
+
+2. /proc/acpi/hotkey/pl_config
+(polling based hotkey or event config interface):
+a.add a polling based hotkey(event) :
+echo "0:bus:method:action:method:num" > polling_config
+this adding command will create a proc file
+/proc/acpi/hotkey/method, which is used to get
+result of polling.
+
+b.delete a polling based hotkey(event):
+echo "1:::::num" > ev_config
+
+c.modify a polling based hotkey(event):
+echo "2:bus:method:action:method:num" > pl_config
+
+3./proc/acpi/hotkey/action
+(interface to call aml method associated with a
+specific hotkey(event))
+echo "event_num:event_type:event_argument" >
+ /proc/acpi/hotkey/action.
+The result of the execution of this aml method is
+attached to /proc/acpi/hotkey/polling_method.
+You can use command "cat /proc/acpi/hotkey/polling_method"
+to get it.
diff -BruN orig/drivers/acpi/hotkey.c 2.6/drivers/acpi/hotkey.c
--- orig/drivers/acpi/hotkey.c 1970-01-01 08:00:00.000000000 +0800
+++ 2.6/drivers/acpi/hotkey.c 2004-11-09 23:18:40.000000000 +0800
@@ -0,0 +1,1034 @@
+/*
+ * hotkey.c - ACPI Hotkey Driver ($Revision:$)
+ *
+ * Copyright (C) 2004 Luming Yu <luming.yu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ *
+ *
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~
+ *
+ * This program is free software; you can redistribute it and/or
modify
+ * it under the terms of the GNU General Public License as published
by
+ * the Free Software Foundation; either version 2 of the License, or
(at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
along
+ * with this program; if not, write to the Free Software Foundation,
Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/kmod.h>
+#include <linux/seq_file.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
+
+#define HOTKEY_ACPI_VERSION "0.1"
+
+#define HOTKEY_PROC "hotkey"
+#define HOTKEY_EV_CONFIG "ev_config"
+#define HOTKEY_PL_CONFIG "pl_config"
+#define HOTKEY_ACTION "action"
+#define HOTKEY_INFO "info"
+
+#define ACPI_HOTK_NAME "Generic Hotkey Driver"
+#define ACPI_HOTK_CLASS "Hotkey"
+#define ACPI_HOTK_DEVICE_NAME "Hotkey"
+#define ACPI_HOTK_HID "Unknown?"
+#define ACPI_HOTKEY_COMPONENT 0x20000000
+
+#define ACPI_HOTKEY_EVENT 0x1
+#define ACPI_HOTKEY_POLLING 0x2
+#define ACPI_UNDEFINED_EVENT 0xf
+
+#define MAX_CONFIG_RECORD_LEN 80
+#define MAX_NAME_PATH_LEN 80
+#define MAX_CALL_PARM 80
+
+#define IS_EV_EVENT(e) 0xff /* ((e) & 0x40000000)
*/
+#define IS_PL_EVENT(e) 0xff /* (~((e) &
0x40000000)) */
+
+#define _COMPONENT ACPI_HOTKEY_COMPONENT
+ACPI_MODULE_NAME("acpi_hotkey")
+
+MODULE_AUTHOR("luming.yu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org");
+MODULE_DESCRIPTION(ACPI_HOTK_NAME);
+MODULE_LICENSE("GPL");
+
+/* standardized internal hotkey number/event */
+enum
+{
+ /* Video Extension event */
+ HK_EVENT_CYCLE_OUTPUT_DEVICE = 0x80,
+ HK_EVENT_OUTPUT_DEVICE_STATUS_CHANGE,
+ HK_EVENT_CYCLE_DISPLAY_OUTPUT,
+ HK_EVENT_NEXT_DISPLAY_OUTPUT,
+ HK_EVENT_PREVIOUS_DISPLAY_OUTPUT,
+ HK_EVENT_CYCLE_BRIGHTNESS,
+ HK_EVENT_INCREASE_BRIGHTNESS,
+ HK_EVENT_DECREASE_BRIGHTNESS,
+ HK_EVENT_ZERO_BRIGHTNESS,
+ HK_EVENT_DISPLAY_DEVICE_OFF,
+
+ /* Snd Card event */
+ HK_EVENT_VOLUME_MUTE,
+ HK_EVENT_VOLUME_INCLREASE,
+ HK_EVENT_VOLUME_DECREASE,
+
+ /* running state control */
+ HK_EVENT_ENTERRING_S3,
+ HK_EVENT_ENTERRING_S4,
+ HK_EVENT_ENTERRING_S5,
+};
+
+/* procdir we use */
+static struct proc_dir_entry *hotkey_proc_dir;
+static struct proc_dir_entry *hotkey_config;
+static struct proc_dir_entry *hotkey_pl_config;
+static struct proc_dir_entry *hotkey_action;
+static struct proc_dir_entry *hotkey_info;
+
+/* linkage for all type of hotkey */
+struct acpi_hotkey_link
+{
+ struct list_head entries;
+ int hk_type; /* event or polling
based hotkey */
+ int hk_std_num; /* standardized
hotkey(event) number */
+};
+
+/* event based hotkey */
+struct acpi_event_hotkey
+{
+ struct acpi_hotkey_link hk_lnk;
+ int flag;
+ acpi_handle bus_handle; /* bus to install
notify handler */
+ int ext_hk_num; /* external
hotkey/event number */
+ acpi_handle act_handle; /* acpi handle
attached aml action method */
+ char *act_method; /* action method */
+};
+
+/*
+ * There are two ways to poll status
+ * 1. directy call read_xxx method, without any arguments passed in
+ * 2. call write_xxx method, with arguments passed in, you need
+ * the result is saved in acpi_polling_hotkey.pl_result.
+ * anthoer read command through polling interface.
+ *
+ */
+
+/* polling based hotkey */
+struct acpi_polling_hotkey
+{
+ struct acpi_hotkey_link hk_lnk;
+ int flag;
+ acpi_handle poll_handle; /* acpi handle
attached polling method */
+ char *poll_method; /* poll method */
+ acpi_handle act_handle; /* acpi handle
attached action method */
+ char *act_method; /* action method */
+ void *pl_result; /* polling_result */
+ struct proc_dir_entry *proc;
+};
+
+/* hotkey object union */
+union acpi_hotkey
+{
+ struct list_head entries;
+ struct acpi_hotkey_link lnk;
+ struct acpi_event_hotkey ev_hk;
+ struct acpi_polling_hotkey pl_hk;
+};
+
+/* hotkey object list */
+struct acpi_hotkey_list
+{
+ struct list_head *entries;
+ int count;
+};
+
+static int auto_hotkey_add(struct acpi_device *device);
+static int auto_hotkey_remove(struct acpi_device *device, int type);
+
+static struct acpi_driver hotkey_driver =
+{
+ .name = ACPI_HOTK_NAME,
+ .class = ACPI_HOTK_CLASS,
+ .ids = ACPI_HOTK_HID,
+ .ops =
+ {
+ .add = auto_hotkey_add,
+ .remove = auto_hotkey_remove,
+ },
+};
+
+static int hotkey_open_config(struct inode *inode, struct file *file);
+static ssize_t hotkey_write_config(struct file *file,
+const char __user * buffer, size_t count,
+loff_t * data);
+static ssize_t hotkey_write_pl_config(struct file *file,
+const char __user * buffer, size_t count,
+loff_t * data);
+static int hotkey_info_open_fs(struct inode *inode, struct file *file);
+static int hotkey_action_open_fs(struct inode *inode, struct file
*file);
+static ssize_t hotkey_execute_aml_method(struct file *file,
+const char __user * buffer,
+size_t count, loff_t * data);
+static int hotkey_config_seq_show(struct seq_file *seq, void *offset);
+static int hotkey_polling_open_fs(struct inode *inode, struct file
*file);
+
+/* event based config */
+static struct file_operations hotkey_config_fops =
+{
+ .open = hotkey_open_config,
+ .read = seq_read,
+ .write = hotkey_write_config,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* polling based config */
+static struct file_operations hotkey_pl_config_fops =
+{
+ .open = hotkey_open_config,
+ .read = seq_read,
+ .write = hotkey_write_pl_config,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* hotkey driver info */
+static struct file_operations hotkey_info_fops =
+{
+ .open = hotkey_info_open_fs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* action */
+static struct file_operations hotkey_action_fops =
+{
+ .open = hotkey_action_open_fs,
+ .read = seq_read,
+ .write = hotkey_execute_aml_method,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* polling results */
+static struct file_operations hotkey_polling_fops =
+{
+ .open = hotkey_polling_open_fs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+struct acpi_hotkey_list hotkey_list; /* link all ev or pl
hotkey */
+struct list_head hotkey_entries; /* head of the list
of hotkey_list */
+
+static int hotkey_info_seq_show(struct seq_file *seq, void *offset)
+{
+ ACPI_FUNCTION_TRACE("hotkey_info_seq_show");
+
+ seq_printf(seq, "Hotkey generic driver ver: %s",
HOTKEY_ACPI_VERSION);
+
+ return_VALUE(0);
+}
+
+
+static int hotkey_info_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, hotkey_info_seq_show, PDE(inode)->data);
+}
+
+
+static char *format_result(union acpi_object *object)
+{
+ char *buf = (char *)kmalloc(sizeof(union acpi_object), GFP_KERNEL);
+
+ memset(buf, 0, sizeof(union acpi_object));
+
+ /* Now, just support integer type */
+ if (object->type == ACPI_TYPE_INTEGER)
+ sprintf(buf, "%d", object->integer.value);
+
+ return buf;
+}
+
+
+static int hotkey_polling_seq_show(struct seq_file *seq, void *offset)
+{
+ struct acpi_polling_hotkey *pl_hk =
+ (struct acpi_polling_hotkey *)seq->private;
+
+ ACPI_FUNCTION_TRACE("hotkey_polling_seq_show");
+
+ if (pl_hk->pl_result)
+ seq_printf(seq, "%s", format_result(pl_hk->pl_result));
+
+ return_VALUE(0);
+}
+
+
+static int hotkey_polling_open_fs(struct inode *inode, struct file
*file)
+{
+ return single_open(file, hotkey_polling_seq_show,
PDE(inode)->data);
+}
+
+
+static int hotkey_action_open_fs(struct inode *inode, struct file
*file)
+{
+ return single_open(file, hotkey_info_seq_show, PDE(inode)->data);
+}
+
+
+/* Mapping external hotkey number to standardized hotkey event num */
+static int hotkey_get_internal_event(int event, struct acpi_hotkey_list
*list)
+{
+ struct list_head *entries, *next;
+ int val = 0;
+
+ ACPI_FUNCTION_TRACE("hotkey_get_internal_event");
+
+ list_for_each_safe(entries, next, list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_type == ACPI_HOTKEY_EVENT
+ && key->ev_hk.ext_hk_num == event)
+ val = key->lnk.hk_std_num;
+ else
+ val = -1;
+ }
+
+ return_VALUE(val);
+}
+
+
+static void
+acpi_hotkey_notify_handler(acpi_handle handle, u32 event, void *data)
+{
+ /* union acpi_hotkey *key = (union acpi_hotkey *) data; */
+ struct acpi_device *device = NULL;
+ u32 internal_event;
+
+ ACPI_FUNCTION_TRACE("acpi_hotkey_notify_handler");
+
+ if (acpi_bus_get_device(handle, &device))
+ return_VOID;
+
+ internal_event = hotkey_get_internal_event(event, &hotkey_list);
+ acpi_bus_generate_event(device, internal_event, 0);
+
+ return_VOID;
+}
+
+
+/* Need to invent automatically hotkey add method */
+static int auto_hotkey_add(struct acpi_device *device)
+{
+ /* Implement me */
+ return 0;
+}
+
+
+/* Need to invent automatically hotkey remove method */
+static int auto_hotkey_remove(struct acpi_device *device, int type)
+{
+ /* Implement me */
+ return 0;
+}
+
+
+/* Create a proc file for each polling method */
+static int create_polling_proc(union acpi_hotkey *device)
+{
+ struct proc_dir_entry *proc;
+
+ mode_t mode = S_IFREG | S_IRUGO | S_IWUGO;
+
+ proc = create_proc_entry(device->pl_hk.act_method,
+ mode, hotkey_proc_dir);
+
+ if (!proc)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ device->pl_hk.poll_method);
+ return (-ENODEV);
+ }
+ else
+ {
+ proc->proc_fops = &hotkey_polling_fops;
+ proc->owner = THIS_MODULE;
+ proc->data = device;
+ proc->uid = 0;
+ proc->gid = 0;
+ device->pl_hk.proc = proc;
+ }
+}
+
+
+static int is_valid_acpi_path(const char *pathname)
+{
+ acpi_handle handle;
+ acpi_status status;
+
+ status = acpi_get_handle(NULL, (char *)pathname, &handle);
+ return !ACPI_FAILURE(status);
+}
+
+
+static int is_valid_hotkey(union acpi_hotkey *device)
+{
+ /* Implement valid check */
+ return TRUE;
+}
+
+
+static int hotkey_add(union acpi_hotkey *device)
+{
+ int status = 0;
+
+ if (device->lnk.hk_type == ACPI_HOTKEY_EVENT)
+ status = acpi_install_notify_handler(device->ev_hk.bus_handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_hotkey_notify_handler,
+ device);
+ else /* Add polling hotkey
*/
+ create_polling_proc(device);
+
+ hotkey_list.count++;
+
+ printk("hotkey_list.count++ = %d\n", hotkey_list.count);
+ list_add_tail(&device->lnk.entries, hotkey_list.entries);
+
+ return status;
+}
+
+
+static int hotkey_remove(union acpi_hotkey *device)
+{
+ struct list_head *entries, *next;
+
+ ACPI_FUNCTION_TRACE("hotkey_update");
+
+ list_for_each_safe(entries, next, hotkey_list.entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_std_num == device->lnk.hk_std_num)
+ {
+ list_del(&key->lnk.entries);
+ remove_proc_entry(key->pl_hk.act_method,
+ hotkey_proc_dir);
+ hotkey_list.count--;
+ break;
+ }
+ }
+ return 0;
+}
+
+
+static void hotkey_update(union acpi_hotkey *hk)
+{
+ struct list_head *entries, *next;
+
+ ACPI_FUNCTION_TRACE("hotkey_update");
+
+ list_for_each_safe(entries, next, hotkey_list.entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_std_num == hk->lnk.hk_std_num)
+ {
+ key->ev_hk.bus_handle = hk->ev_hk.bus_handle;
+ key->ev_hk.ext_hk_num = hk->ev_hk.ext_hk_num;
+ key->ev_hk.act_handle = hk->ev_hk.act_handle;
+ key->ev_hk.act_method = hk->ev_hk.act_method;
+ break;
+ }
+ }
+
+ return_VOID;
+}
+
+
+static void free_hotkey_device(union acpi_hotkey *key)
+{
+}
+
+
+static int
+init_hotkey_device(union acpi_hotkey *key,
+char *bus_str,
+char *action_str, char *method, int std_num, int ext_num)
+{
+ key->lnk.hk_type = ACPI_HOTKEY_EVENT;
+ key->lnk.hk_std_num = std_num;
+ key->ev_hk.flag = 0;
+ if (is_valid_acpi_path(bus_str))
+ acpi_get_handle((acpi_handle) 0,
+ bus_str, &(key->ev_hk.bus_handle));
+ key->ev_hk.ext_hk_num = ext_num;
+ if (is_valid_acpi_path(action_str))
+ acpi_get_handle((acpi_handle) 0,
+ action_str, &(key->ev_hk.act_handle));
+ key->ev_hk.act_method = method;
+ return (is_valid_hotkey(key));
+}
+
+
+static int
+init_pl_hotkey_device(union acpi_hotkey *key,
+char *poll_str,
+char *poll_mtd,
+char *action_str, char *action_mtd, int std_num)
+{
+ key->lnk.hk_type = ACPI_HOTKEY_POLLING;
+ key->lnk.hk_std_num = std_num;
+ key->pl_hk.flag = 0;
+ if (is_valid_acpi_path(poll_str))
+ acpi_get_handle((acpi_handle) 0,
+ poll_str, &(key->pl_hk.poll_handle));
+ key->pl_hk.poll_method = poll_mtd;
+ if (is_valid_acpi_path(action_str))
+ acpi_get_handle((acpi_handle) 0,
+ action_str, &(key->pl_hk.act_handle));
+ key->pl_hk.act_method = action_mtd;
+ key->pl_hk.pl_result =
+ (union acpi_object *)kmalloc(sizeof(union acpi_object),
GFP_KERNEL);
+ return (is_valid_hotkey(key));
+}
+
+
+static int check_hotkey_valid(union acpi_hotkey *key,
+struct acpi_hotkey_list *list)
+{
+ return 1;
+}
+
+
+static int hotkey_open_config(struct inode *inode, struct file *file)
+{
+ return single_open(file, hotkey_config_seq_show, PDE(inode)->data);
+}
+
+
+static int hotkey_config_seq_show(struct seq_file *seq, void *offset)
+{
+ /* (struct
acpi_hotkey_list *)seq->private; */
+ struct acpi_hotkey_list *hk_list = &hotkey_list;
+ struct list_head *entries, *next;
+ char bus_name[ACPI_PATHNAME_MAX] = { 0 };
+ char act_name[ACPI_PATHNAME_MAX] = { 0 };
+ struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name };
+ struct acpi_buffer act = { ACPI_PATHNAME_MAX, act_name };
+
+ ACPI_FUNCTION_TRACE(("hotkey_config_seq_show"));
+
+ if (!hk_list)
+ goto end;
+
+ list_for_each_safe(entries, next, hk_list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_type == ACPI_HOTKEY_EVENT)
+ {
+ acpi_get_name(key->ev_hk.bus_handle, ACPI_NAME_TYPE_MAX,
+ &bus);
+ acpi_get_name(key->ev_hk.act_handle, ACPI_NAME_TYPE_MAX,
+ &act);
+ seq_printf(seq, "%s:%s:%s:%d:%d", bus_name, act_name,
+ key->ev_hk.act_method, key->lnk.hk_std_num,
+ key->ev_hk.ext_hk_num);
+ } /*
ACPI_HOTKEY_POLLING */
+ else
+ {
+ acpi_get_name(key->pl_hk.poll_handle,
+ ACPI_NAME_TYPE_MAX, &bus);
+ acpi_get_name(key->pl_hk.act_handle, ACPI_NAME_TYPE_MAX,
+ &act);
+ seq_printf(seq, "%s:%s:%s:%s:%d", bus_name,
+ key->pl_hk.poll_method, act_name,
+ key->pl_hk.act_method, key->lnk.hk_std_num);
+ }
+ }
+ seq_puts(seq, "\n");
+ end:
+ return_VALUE(0);
+}
+
+
+static int
+get_parms(char *config_record,
+int *cmd,
+char *bus_handle,
+char *bus_method,
+char *action_handle,
+char *method, int *internal_event_num, int *external_event_num)
+{
+ char *tmp, *tmp1;
+ sscanf(config_record, "%d", cmd);
+
+ tmp = strchr(config_record, ':');
+ tmp++;
+ tmp1 = strchr(tmp, ':');
+ strncpy(bus_handle, tmp, tmp1 - tmp);
+ bus_handle[tmp1 - tmp] = 0;
+
+ tmp = tmp1;
+ tmp++;
+ tmp1 = strchr(tmp, ':');
+ strncpy(bus_method, tmp, tmp1 - tmp);
+ bus_method[tmp1 - tmp] = 0;
+
+ tmp = tmp1;
+ tmp++;
+ tmp1 = strchr(tmp, ':');
+ strncpy(action_handle, tmp, tmp1 - tmp);
+ action_handle[tmp1 - tmp] = 0;
+
+ tmp = tmp1;
+ tmp++;
+ tmp1 = strchr(tmp, ':');
+ strncpy(method, tmp, tmp1 - tmp);
+ method[tmp1 - tmp] = 0;
+
+ sscanf(tmp1 + 1, "%d:%d", internal_event_num, external_event_num);
+ return 6;
+}
+
+
+/* count is length for one input record */
+static ssize_t hotkey_write_config(struct file *file,
+const char __user * buffer, size_t count,
+loff_t * data)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ /* (struct
acpi_hotkey_list *)seq->private; */
+ struct acpi_hotkey_list *hk_list = &hotkey_list;
+ char config_record[MAX_CONFIG_RECORD_LEN];
+ char bus_handle[MAX_NAME_PATH_LEN];
+ char bus_method[MAX_NAME_PATH_LEN];
+ char action_handle[MAX_NAME_PATH_LEN];
+ char method[20];
+ int cmd, internal_event_num, external_event_num;
+ int ret = 0;
+ union acpi_hotkey *key = NULL;
+
+ ACPI_FUNCTION_TRACE("hotkey_write_config");
+
+ if (!hk_list || count > MAX_CONFIG_RECORD_LEN)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid arguments\n"));
+ return_VALUE(-EINVAL);
+ }
+
+ if (copy_from_user(config_record, buffer, count))
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data \n"));
+ return_VALUE(-EINVAL);
+ }
+ config_record[count] = '\0';
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "config_str: %s", config_record));
+
+ ret = get_parms(config_record,
+ /*
+ ret = sscanf(config_record,
+ "%d:%s:%s:%s:%d:%d",
+ */
+ &cmd,
+ bus_handle,
+ bus_method,
+ action_handle,
+ method, &internal_event_num, &external_event_num);
+ if (ret != 6)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format
ret=%d\n",
+ ret));
+ return_VALUE(-EINVAL);
+ }
+
+ key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
+ ret = init_hotkey_device(key, bus_handle, action_handle, method,
+ internal_event_num, external_event_num);
+
+ if (ret || check_hotkey_valid(key, &hotkey_list))
+ {
+ free_hotkey_device(key);
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid hotkey \n"));
+ return_VALUE(-EINVAL);
+ }
+ switch (cmd)
+ {
+ case 0:
+ hotkey_add(key);
+ break;
+ case 1:
+ hotkey_remove(key);
+ break;
+ case 2:
+ hotkey_update(key);
+ break;
+ default:
+ break;
+ }
+ return_VALUE(count);
+}
+
+
+/* count is length for one input record */
+static ssize_t hotkey_write_pl_config(struct file *file,
+const char __user * buffer, size_t count,
+loff_t * data)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ struct acpi_hotkey_list *hk_list =
+ (struct acpi_hotkey_list *)m->private;
+
+ char config_record[MAX_CONFIG_RECORD_LEN];
+ char polling_handle[MAX_NAME_PATH_LEN];
+ char action_handle[MAX_NAME_PATH_LEN];
+ char poll_method[20], action_method[20];
+ int ret, internal_event_num, cmd, external_event_num;
+ union acpi_hotkey *key = NULL;
+
+ ACPI_FUNCTION_TRACE("hotkey_write_config");
+
+ if (!hk_list || count > MAX_CONFIG_RECORD_LEN)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid arguments\n"));
+ return_VALUE(-EINVAL);
+ }
+
+ if (copy_from_user(config_record, buffer, count))
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data \n"));
+ return_VALUE(-EINVAL);
+ }
+ config_record[count] = '\0';
+
+ ret = get_parms(config_record,
+ &cmd,
+ polling_handle,
+ poll_method,
+ action_handle,
+ action_method,
+ &internal_event_num, &external_event_num);
+
+ if (ret != 6)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n"));
+ return_VALUE(-EINVAL);
+ }
+
+ key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
+ ret = init_pl_hotkey_device(key, polling_handle, poll_method,
+ action_handle, action_method,
+ internal_event_num);
+ if (ret || check_hotkey_valid(key, &hotkey_list))
+ {
+ free_hotkey_device(key);
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid hotkey \n"));
+ return_VALUE(-EINVAL);
+ }
+ switch (cmd)
+ {
+ case 0:
+ hotkey_add(key);
+ break;
+ case 1:
+ hotkey_remove(key);
+ break;
+ case 2:
+ hotkey_update(key);
+ break;
+ default:
+ break;
+ }
+ return_VALUE(count);
+}
+
+
+/*
+ * This function evaluates an ACPI method, given an int as parameter,
the
+ * method is searched within the scope of the handle, can be NULL. The
output
+ * of the method is written is output, which can also be NULL
+ *
+ * returns 1 if write is successful, 0 else.
+ */
+static int write_acpi_int(acpi_handle handle, const char *method, int
val,
+struct acpi_buffer *output)
+{
+ struct acpi_object_list params; /* list of input
parameters (an int here) */
+ union acpi_object in_obj; /* the only param we
use */
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE("write_acpi_int");
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = val;
+
+ status = acpi_evaluate_object(handle, (char *)method, &params,
output);
+
+ return_VALUE(status == AE_OK);
+}
+
+
+static int read_acpi_int(acpi_handle handle, const char *method, int
*val)
+{
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ status = acpi_evaluate_object(handle, (char *)method, NULL,
&output);
+ *val = out_obj.integer.value;
+ return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
+}
+
+
+static acpi_handle
+get_handle_from_hotkeylist(struct acpi_hotkey_list *hk_list, int
event_num)
+{
+ struct list_head *entries, *next;
+
+ list_for_each_safe(entries, next, hk_list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_type == ACPI_HOTKEY_EVENT
+ && key->lnk.hk_std_num == event_num)
+ {
+ return (key->ev_hk.act_handle);
+ }
+ }
+ return NULL;
+}
+
+
+static
+char *get_method_from_hotkeylist(struct acpi_hotkey_list *hk_list,
+int event_num)
+{
+ struct list_head *entries, *next;
+
+ list_for_each_safe(entries, next, hk_list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+
+ if (key->lnk.hk_type == ACPI_HOTKEY_EVENT &&
+ key->lnk.hk_std_num == event_num)
+ {
+ return (key->ev_hk.act_method);
+ }
+ }
+ return NULL;
+}
+
+
+static
+struct acpi_polling_hotkey *get_hotkey_by_evt(struct acpi_hotkey_list
*hk_list,
+int evt)
+{
+ struct list_head *entries, *next;
+
+ list_for_each_safe(entries, next, hk_list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_type == ACPI_HOTKEY_POLLING
+ && key->lnk.hk_std_num == evt)
+ {
+ return (&key->pl_hk);
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * user call AML method interface:
+ * Call convention:
+ * echo "event_num: arg type : value"
+ * example: echo "1:1:30" > /proc/acpi/action
+ * Just support 1 integer arg passing to AML method
+ */
+
+static ssize_t hotkey_execute_aml_method(struct file *file,
+const char __user * buffer,
+size_t count, loff_t * data)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ /* (struct
acpi_hotkey_list *) m->private; */
+ struct acpi_hotkey_list *hk_list = &hotkey_list;
+ char arg[MAX_CALL_PARM];
+ int evt, type, value;
+
+ char *method;
+ acpi_handle handle;
+
+ ACPI_FUNCTION_TRACE("hotkey_execte_action");
+
+ if (!hk_list || count > MAX_CALL_PARM)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 1"));
+ return_VALUE(-EINVAL);
+ }
+
+ if (copy_from_user(arg, buffer, count))
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 2"));
+ return_VALUE(-EINVAL);
+ }
+
+ arg[count] = '\0';
+
+ if (sscanf(arg, "%d:%d:%d", &evt, &type, &value) != 3)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 3"));
+ return_VALUE(-EINVAL);
+ }
+
+ if (type == ACPI_TYPE_INTEGER)
+ {
+ handle = get_handle_from_hotkeylist(hk_list, evt);
+ method = get_method_from_hotkeylist(hk_list, evt);
+ if (IS_EV_EVENT(evt))
+ write_acpi_int(handle, method, value, NULL);
+ else if (IS_PL_EVENT(evt))
+ {
+ struct acpi_polling_hotkey *hk;
+ hk = (struct acpi_polling_hotkey *)
+ get_hotkey_by_evt(&hotkey_list, evt);
+ read_acpi_int(handle, method, hk->pl_result);
+ }
+ }
+ else
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Not supported"));
+ return_VALUE(-EINVAL);
+ }
+
+ return_VALUE(count);
+}
+
+
+static int __init hotkey_init(void)
+{
+ int result;
+ mode_t mode = S_IFREG | S_IRUGO | S_IWUGO;
+
+ hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir);
+ if (!hotkey_proc_dir)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_PROC);
+ return (-ENODEV);
+ }
+ hotkey_proc_dir->owner = THIS_MODULE;
+
+ hotkey_config =
+ create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir);
+ if (!hotkey_config)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_EV_CONFIG);
+ return (-ENODEV);
+ }
+ else
+ {
+ hotkey_config->proc_fops = &hotkey_config_fops;
+ hotkey_config->data = &hotkey_list;
+ hotkey_config->owner = THIS_MODULE;
+ hotkey_config->uid = 0;
+ hotkey_config->gid = 0;
+ }
+
+ hotkey_pl_config =
+ create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir);
+ if (!hotkey_pl_config)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_EV_CONFIG);
+ return (-ENODEV);
+ }
+ else
+ {
+ hotkey_pl_config->proc_fops = &hotkey_pl_config_fops;
+ hotkey_pl_config->data = &hotkey_list;
+ hotkey_pl_config->owner = THIS_MODULE;
+ hotkey_pl_config->uid = 0;
+ hotkey_pl_config->gid = 0;
+ }
+
+ hotkey_action = create_proc_entry(HOTKEY_ACTION, mode,
hotkey_proc_dir);
+ if (!hotkey_action)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_ACTION);
+ return (-ENODEV);
+ }
+ else
+ {
+ hotkey_action->proc_fops = &hotkey_action_fops;
+ hotkey_action->owner = THIS_MODULE;
+ hotkey_action->uid = 0;
+ hotkey_action->gid = 0;
+ }
+
+ hotkey_info = create_proc_entry(HOTKEY_INFO, mode,
hotkey_proc_dir);
+ if (!hotkey_info)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_INFO);
+ return (-ENODEV);
+ }
+ else
+ {
+ hotkey_info->proc_fops = &hotkey_info_fops;
+ hotkey_info->owner = THIS_MODULE;
+ hotkey_info->uid = 0;
+ hotkey_info->gid = 0;
+ }
+
+ result = acpi_bus_register_driver(&hotkey_driver);
+ if (result < 0)
+ {
+ remove_proc_entry(HOTKEY_PROC, acpi_root_dir);
+ return (-ENODEV);
+ }
+ hotkey_list.count = 0;
+ hotkey_list.entries = &hotkey_entries;
+
+ INIT_LIST_HEAD(&hotkey_entries);
+
+ return (0);
+}
+
+
+static void __exit hotkey_exit(void)
+{
+ acpi_bus_unregister_driver(&hotkey_driver);
+ remove_proc_entry(HOTKEY_PROC, acpi_root_dir);
+ return;
+}
+
+
+module_init(hotkey_init);
+module_exit(hotkey_exit);
diff -BruN orig/drivers/acpi/Makefile 2.6/drivers/acpi/Makefile
--- orig/drivers/acpi/Makefile 2004-11-09 23:10:07.000000000 +0800
+++ 2.6/drivers/acpi/Makefile 2004-11-09 23:08:01.000000000 +0800
@@ -38,7 +38,7 @@
obj-$(CONFIG_ACPI_BUTTON) += button.o
obj-$(CONFIG_ACPI_EC) += ec.o
obj-$(CONFIG_ACPI_FAN) += fan.o
-obj-$(CONFIG_ACPI_VIDEO) += video.o
+obj-$(CONFIG_ACPI_VIDEO) += video.o hotkey.o
obj-$(CONFIG_ACPI_PCI) += pci_root.o pci_link.o pci_irq.o
pci_bind.o
obj-$(CONFIG_ACPI_POWER) += power.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o

Thanks
Luming

[-- Attachment #2: hotkey.patch --]
[-- Type: application/octet-stream, Size: 32821 bytes --]

diff -BruN orig/Documentation/acpi-hotkey.txt 2.6/Documentation/acpi-hotkey.txt
--- orig/Documentation/acpi-hotkey.txt 1970-01-01 08:00:00.000000000 +0800
+++ 2.6/Documentation/acpi-hotkey.txt 2004-11-09 23:07:25.000000000 +0800
@@ -0,0 +1,35 @@
+The enclosed hotkey.c implement:
+1. /proc/acpi/hotkey/event_config
+(event based hotkey or event config interface):
+a. add a event based hotkey(event) :
+echo "0:bus::action:method:num:num" > event_config
+
+b. delete a event based hotkey(event):
+echo "1:::::num:num" > event_config
+
+c. modify a event based hotkey(event):
+echo "2:bus::action:method:num:num" > event_config
+
+2. /proc/acpi/hotkey/pl_config
+(polling based hotkey or event config interface):
+a.add a polling based hotkey(event) :
+echo "0:bus:method:action:method:num" > polling_config
+this adding command will create a proc file
+/proc/acpi/hotkey/method, which is used to get
+result of polling.
+
+b.delete a polling based hotkey(event):
+echo "1:::::num" > ev_config
+
+c.modify a polling based hotkey(event):
+echo "2:bus:method:action:method:num" > pl_config
+
+3./proc/acpi/hotkey/action
+(interface to call aml method associated with a
+specific hotkey(event))
+echo "event_num:event_type:event_argument" >
+ /proc/acpi/hotkey/action.
+The result of the execution of this aml method is
+attached to /proc/acpi/hotkey/polling_method.
+You can use command "cat /proc/acpi/hotkey/polling_method"
+to get it.
diff -BruN orig/drivers/acpi/hotkey.c 2.6/drivers/acpi/hotkey.c
--- orig/drivers/acpi/hotkey.c 1970-01-01 08:00:00.000000000 +0800
+++ 2.6/drivers/acpi/hotkey.c 2004-11-09 23:18:40.000000000 +0800
@@ -0,0 +1,1034 @@
+/*
+ * hotkey.c - ACPI Hotkey Driver ($Revision:$)
+ *
+ * Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/kmod.h>
+#include <linux/seq_file.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
+
+#define HOTKEY_ACPI_VERSION "0.1"
+
+#define HOTKEY_PROC "hotkey"
+#define HOTKEY_EV_CONFIG "ev_config"
+#define HOTKEY_PL_CONFIG "pl_config"
+#define HOTKEY_ACTION "action"
+#define HOTKEY_INFO "info"
+
+#define ACPI_HOTK_NAME "Generic Hotkey Driver"
+#define ACPI_HOTK_CLASS "Hotkey"
+#define ACPI_HOTK_DEVICE_NAME "Hotkey"
+#define ACPI_HOTK_HID "Unknown?"
+#define ACPI_HOTKEY_COMPONENT 0x20000000
+
+#define ACPI_HOTKEY_EVENT 0x1
+#define ACPI_HOTKEY_POLLING 0x2
+#define ACPI_UNDEFINED_EVENT 0xf
+
+#define MAX_CONFIG_RECORD_LEN 80
+#define MAX_NAME_PATH_LEN 80
+#define MAX_CALL_PARM 80
+
+#define IS_EV_EVENT(e) 0xff /* ((e) & 0x40000000) */
+#define IS_PL_EVENT(e) 0xff /* (~((e) & 0x40000000)) */
+
+#define _COMPONENT ACPI_HOTKEY_COMPONENT
+ACPI_MODULE_NAME("acpi_hotkey")
+
+MODULE_AUTHOR("luming.yu@intel.com");
+MODULE_DESCRIPTION(ACPI_HOTK_NAME);
+MODULE_LICENSE("GPL");
+
+/* standardized internal hotkey number/event */
+enum
+{
+ /* Video Extension event */
+ HK_EVENT_CYCLE_OUTPUT_DEVICE = 0x80,
+ HK_EVENT_OUTPUT_DEVICE_STATUS_CHANGE,
+ HK_EVENT_CYCLE_DISPLAY_OUTPUT,
+ HK_EVENT_NEXT_DISPLAY_OUTPUT,
+ HK_EVENT_PREVIOUS_DISPLAY_OUTPUT,
+ HK_EVENT_CYCLE_BRIGHTNESS,
+ HK_EVENT_INCREASE_BRIGHTNESS,
+ HK_EVENT_DECREASE_BRIGHTNESS,
+ HK_EVENT_ZERO_BRIGHTNESS,
+ HK_EVENT_DISPLAY_DEVICE_OFF,
+
+ /* Snd Card event */
+ HK_EVENT_VOLUME_MUTE,
+ HK_EVENT_VOLUME_INCLREASE,
+ HK_EVENT_VOLUME_DECREASE,
+
+ /* running state control */
+ HK_EVENT_ENTERRING_S3,
+ HK_EVENT_ENTERRING_S4,
+ HK_EVENT_ENTERRING_S5,
+};
+
+/* procdir we use */
+static struct proc_dir_entry *hotkey_proc_dir;
+static struct proc_dir_entry *hotkey_config;
+static struct proc_dir_entry *hotkey_pl_config;
+static struct proc_dir_entry *hotkey_action;
+static struct proc_dir_entry *hotkey_info;
+
+/* linkage for all type of hotkey */
+struct acpi_hotkey_link
+{
+ struct list_head entries;
+ int hk_type; /* event or polling based hotkey */
+ int hk_std_num; /* standardized hotkey(event) number */
+};
+
+/* event based hotkey */
+struct acpi_event_hotkey
+{
+ struct acpi_hotkey_link hk_lnk;
+ int flag;
+ acpi_handle bus_handle; /* bus to install notify handler */
+ int ext_hk_num; /* external hotkey/event number */
+ acpi_handle act_handle; /* acpi handle attached aml action method */
+ char *act_method; /* action method */
+};
+
+/*
+ * There are two ways to poll status
+ * 1. directy call read_xxx method, without any arguments passed in
+ * 2. call write_xxx method, with arguments passed in, you need
+ * the result is saved in acpi_polling_hotkey.pl_result.
+ * anthoer read command through polling interface.
+ *
+ */
+
+/* polling based hotkey */
+struct acpi_polling_hotkey
+{
+ struct acpi_hotkey_link hk_lnk;
+ int flag;
+ acpi_handle poll_handle; /* acpi handle attached polling method */
+ char *poll_method; /* poll method */
+ acpi_handle act_handle; /* acpi handle attached action method */
+ char *act_method; /* action method */
+ void *pl_result; /* polling_result */
+ struct proc_dir_entry *proc;
+};
+
+/* hotkey object union */
+union acpi_hotkey
+{
+ struct list_head entries;
+ struct acpi_hotkey_link lnk;
+ struct acpi_event_hotkey ev_hk;
+ struct acpi_polling_hotkey pl_hk;
+};
+
+/* hotkey object list */
+struct acpi_hotkey_list
+{
+ struct list_head *entries;
+ int count;
+};
+
+static int auto_hotkey_add(struct acpi_device *device);
+static int auto_hotkey_remove(struct acpi_device *device, int type);
+
+static struct acpi_driver hotkey_driver =
+{
+ .name = ACPI_HOTK_NAME,
+ .class = ACPI_HOTK_CLASS,
+ .ids = ACPI_HOTK_HID,
+ .ops =
+ {
+ .add = auto_hotkey_add,
+ .remove = auto_hotkey_remove,
+ },
+};
+
+static int hotkey_open_config(struct inode *inode, struct file *file);
+static ssize_t hotkey_write_config(struct file *file,
+const char __user * buffer, size_t count,
+loff_t * data);
+static ssize_t hotkey_write_pl_config(struct file *file,
+const char __user * buffer, size_t count,
+loff_t * data);
+static int hotkey_info_open_fs(struct inode *inode, struct file *file);
+static int hotkey_action_open_fs(struct inode *inode, struct file *file);
+static ssize_t hotkey_execute_aml_method(struct file *file,
+const char __user * buffer,
+size_t count, loff_t * data);
+static int hotkey_config_seq_show(struct seq_file *seq, void *offset);
+static int hotkey_polling_open_fs(struct inode *inode, struct file *file);
+
+/* event based config */
+static struct file_operations hotkey_config_fops =
+{
+ .open = hotkey_open_config,
+ .read = seq_read,
+ .write = hotkey_write_config,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* polling based config */
+static struct file_operations hotkey_pl_config_fops =
+{
+ .open = hotkey_open_config,
+ .read = seq_read,
+ .write = hotkey_write_pl_config,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* hotkey driver info */
+static struct file_operations hotkey_info_fops =
+{
+ .open = hotkey_info_open_fs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* action */
+static struct file_operations hotkey_action_fops =
+{
+ .open = hotkey_action_open_fs,
+ .read = seq_read,
+ .write = hotkey_execute_aml_method,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* polling results */
+static struct file_operations hotkey_polling_fops =
+{
+ .open = hotkey_polling_open_fs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+struct acpi_hotkey_list hotkey_list; /* link all ev or pl hotkey */
+struct list_head hotkey_entries; /* head of the list of hotkey_list */
+
+static int hotkey_info_seq_show(struct seq_file *seq, void *offset)
+{
+ ACPI_FUNCTION_TRACE("hotkey_info_seq_show");
+
+ seq_printf(seq, "Hotkey generic driver ver: %s", HOTKEY_ACPI_VERSION);
+
+ return_VALUE(0);
+}
+
+
+static int hotkey_info_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, hotkey_info_seq_show, PDE(inode)->data);
+}
+
+
+static char *format_result(union acpi_object *object)
+{
+ char *buf = (char *)kmalloc(sizeof(union acpi_object), GFP_KERNEL);
+
+ memset(buf, 0, sizeof(union acpi_object));
+
+ /* Now, just support integer type */
+ if (object->type == ACPI_TYPE_INTEGER)
+ sprintf(buf, "%d", object->integer.value);
+
+ return buf;
+}
+
+
+static int hotkey_polling_seq_show(struct seq_file *seq, void *offset)
+{
+ struct acpi_polling_hotkey *pl_hk =
+ (struct acpi_polling_hotkey *)seq->private;
+
+ ACPI_FUNCTION_TRACE("hotkey_polling_seq_show");
+
+ if (pl_hk->pl_result)
+ seq_printf(seq, "%s", format_result(pl_hk->pl_result));
+
+ return_VALUE(0);
+}
+
+
+static int hotkey_polling_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, hotkey_polling_seq_show, PDE(inode)->data);
+}
+
+
+static int hotkey_action_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, hotkey_info_seq_show, PDE(inode)->data);
+}
+
+
+/* Mapping external hotkey number to standardized hotkey event num */
+static int hotkey_get_internal_event(int event, struct acpi_hotkey_list *list)
+{
+ struct list_head *entries, *next;
+ int val = 0;
+
+ ACPI_FUNCTION_TRACE("hotkey_get_internal_event");
+
+ list_for_each_safe(entries, next, list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_type == ACPI_HOTKEY_EVENT
+ && key->ev_hk.ext_hk_num == event)
+ val = key->lnk.hk_std_num;
+ else
+ val = -1;
+ }
+
+ return_VALUE(val);
+}
+
+
+static void
+acpi_hotkey_notify_handler(acpi_handle handle, u32 event, void *data)
+{
+ /* union acpi_hotkey *key = (union acpi_hotkey *) data; */
+ struct acpi_device *device = NULL;
+ u32 internal_event;
+
+ ACPI_FUNCTION_TRACE("acpi_hotkey_notify_handler");
+
+ if (acpi_bus_get_device(handle, &device))
+ return_VOID;
+
+ internal_event = hotkey_get_internal_event(event, &hotkey_list);
+ acpi_bus_generate_event(device, internal_event, 0);
+
+ return_VOID;
+}
+
+
+/* Need to invent automatically hotkey add method */
+static int auto_hotkey_add(struct acpi_device *device)
+{
+ /* Implement me */
+ return 0;
+}
+
+
+/* Need to invent automatically hotkey remove method */
+static int auto_hotkey_remove(struct acpi_device *device, int type)
+{
+ /* Implement me */
+ return 0;
+}
+
+
+/* Create a proc file for each polling method */
+static int create_polling_proc(union acpi_hotkey *device)
+{
+ struct proc_dir_entry *proc;
+
+ mode_t mode = S_IFREG | S_IRUGO | S_IWUGO;
+
+ proc = create_proc_entry(device->pl_hk.act_method,
+ mode, hotkey_proc_dir);
+
+ if (!proc)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ device->pl_hk.poll_method);
+ return (-ENODEV);
+ }
+ else
+ {
+ proc->proc_fops = &hotkey_polling_fops;
+ proc->owner = THIS_MODULE;
+ proc->data = device;
+ proc->uid = 0;
+ proc->gid = 0;
+ device->pl_hk.proc = proc;
+ }
+}
+
+
+static int is_valid_acpi_path(const char *pathname)
+{
+ acpi_handle handle;
+ acpi_status status;
+
+ status = acpi_get_handle(NULL, (char *)pathname, &handle);
+ return !ACPI_FAILURE(status);
+}
+
+
+static int is_valid_hotkey(union acpi_hotkey *device)
+{
+ /* Implement valid check */
+ return TRUE;
+}
+
+
+static int hotkey_add(union acpi_hotkey *device)
+{
+ int status = 0;
+
+ if (device->lnk.hk_type == ACPI_HOTKEY_EVENT)
+ status = acpi_install_notify_handler(device->ev_hk.bus_handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_hotkey_notify_handler,
+ device);
+ else /* Add polling hotkey */
+ create_polling_proc(device);
+
+ hotkey_list.count++;
+
+ printk("hotkey_list.count++ = %d\n", hotkey_list.count);
+ list_add_tail(&device->lnk.entries, hotkey_list.entries);
+
+ return status;
+}
+
+
+static int hotkey_remove(union acpi_hotkey *device)
+{
+ struct list_head *entries, *next;
+
+ ACPI_FUNCTION_TRACE("hotkey_update");
+
+ list_for_each_safe(entries, next, hotkey_list.entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_std_num == device->lnk.hk_std_num)
+ {
+ list_del(&key->lnk.entries);
+ remove_proc_entry(key->pl_hk.act_method,
+ hotkey_proc_dir);
+ hotkey_list.count--;
+ break;
+ }
+ }
+ return 0;
+}
+
+
+static void hotkey_update(union acpi_hotkey *hk)
+{
+ struct list_head *entries, *next;
+
+ ACPI_FUNCTION_TRACE("hotkey_update");
+
+ list_for_each_safe(entries, next, hotkey_list.entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_std_num == hk->lnk.hk_std_num)
+ {
+ key->ev_hk.bus_handle = hk->ev_hk.bus_handle;
+ key->ev_hk.ext_hk_num = hk->ev_hk.ext_hk_num;
+ key->ev_hk.act_handle = hk->ev_hk.act_handle;
+ key->ev_hk.act_method = hk->ev_hk.act_method;
+ break;
+ }
+ }
+
+ return_VOID;
+}
+
+
+static void free_hotkey_device(union acpi_hotkey *key)
+{
+}
+
+
+static int
+init_hotkey_device(union acpi_hotkey *key,
+char *bus_str,
+char *action_str, char *method, int std_num, int ext_num)
+{
+ key->lnk.hk_type = ACPI_HOTKEY_EVENT;
+ key->lnk.hk_std_num = std_num;
+ key->ev_hk.flag = 0;
+ if (is_valid_acpi_path(bus_str))
+ acpi_get_handle((acpi_handle) 0,
+ bus_str, &(key->ev_hk.bus_handle));
+ key->ev_hk.ext_hk_num = ext_num;
+ if (is_valid_acpi_path(action_str))
+ acpi_get_handle((acpi_handle) 0,
+ action_str, &(key->ev_hk.act_handle));
+ key->ev_hk.act_method = method;
+ return (is_valid_hotkey(key));
+}
+
+
+static int
+init_pl_hotkey_device(union acpi_hotkey *key,
+char *poll_str,
+char *poll_mtd,
+char *action_str, char *action_mtd, int std_num)
+{
+ key->lnk.hk_type = ACPI_HOTKEY_POLLING;
+ key->lnk.hk_std_num = std_num;
+ key->pl_hk.flag = 0;
+ if (is_valid_acpi_path(poll_str))
+ acpi_get_handle((acpi_handle) 0,
+ poll_str, &(key->pl_hk.poll_handle));
+ key->pl_hk.poll_method = poll_mtd;
+ if (is_valid_acpi_path(action_str))
+ acpi_get_handle((acpi_handle) 0,
+ action_str, &(key->pl_hk.act_handle));
+ key->pl_hk.act_method = action_mtd;
+ key->pl_hk.pl_result =
+ (union acpi_object *)kmalloc(sizeof(union acpi_object), GFP_KERNEL);
+ return (is_valid_hotkey(key));
+}
+
+
+static int check_hotkey_valid(union acpi_hotkey *key,
+struct acpi_hotkey_list *list)
+{
+ return 1;
+}
+
+
+static int hotkey_open_config(struct inode *inode, struct file *file)
+{
+ return single_open(file, hotkey_config_seq_show, PDE(inode)->data);
+}
+
+
+static int hotkey_config_seq_show(struct seq_file *seq, void *offset)
+{
+ /* (struct acpi_hotkey_list *)seq->private; */
+ struct acpi_hotkey_list *hk_list = &hotkey_list;
+ struct list_head *entries, *next;
+ char bus_name[ACPI_PATHNAME_MAX] = { 0 };
+ char act_name[ACPI_PATHNAME_MAX] = { 0 };
+ struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name };
+ struct acpi_buffer act = { ACPI_PATHNAME_MAX, act_name };
+
+ ACPI_FUNCTION_TRACE(("hotkey_config_seq_show"));
+
+ if (!hk_list)
+ goto end;
+
+ list_for_each_safe(entries, next, hk_list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_type == ACPI_HOTKEY_EVENT)
+ {
+ acpi_get_name(key->ev_hk.bus_handle, ACPI_NAME_TYPE_MAX,
+ &bus);
+ acpi_get_name(key->ev_hk.act_handle, ACPI_NAME_TYPE_MAX,
+ &act);
+ seq_printf(seq, "%s:%s:%s:%d:%d", bus_name, act_name,
+ key->ev_hk.act_method, key->lnk.hk_std_num,
+ key->ev_hk.ext_hk_num);
+ } /* ACPI_HOTKEY_POLLING */
+ else
+ {
+ acpi_get_name(key->pl_hk.poll_handle,
+ ACPI_NAME_TYPE_MAX, &bus);
+ acpi_get_name(key->pl_hk.act_handle, ACPI_NAME_TYPE_MAX,
+ &act);
+ seq_printf(seq, "%s:%s:%s:%s:%d", bus_name,
+ key->pl_hk.poll_method, act_name,
+ key->pl_hk.act_method, key->lnk.hk_std_num);
+ }
+ }
+ seq_puts(seq, "\n");
+ end:
+ return_VALUE(0);
+}
+
+
+static int
+get_parms(char *config_record,
+int *cmd,
+char *bus_handle,
+char *bus_method,
+char *action_handle,
+char *method, int *internal_event_num, int *external_event_num)
+{
+ char *tmp, *tmp1;
+ sscanf(config_record, "%d", cmd);
+
+ tmp = strchr(config_record, ':');
+ tmp++;
+ tmp1 = strchr(tmp, ':');
+ strncpy(bus_handle, tmp, tmp1 - tmp);
+ bus_handle[tmp1 - tmp] = 0;
+
+ tmp = tmp1;
+ tmp++;
+ tmp1 = strchr(tmp, ':');
+ strncpy(bus_method, tmp, tmp1 - tmp);
+ bus_method[tmp1 - tmp] = 0;
+
+ tmp = tmp1;
+ tmp++;
+ tmp1 = strchr(tmp, ':');
+ strncpy(action_handle, tmp, tmp1 - tmp);
+ action_handle[tmp1 - tmp] = 0;
+
+ tmp = tmp1;
+ tmp++;
+ tmp1 = strchr(tmp, ':');
+ strncpy(method, tmp, tmp1 - tmp);
+ method[tmp1 - tmp] = 0;
+
+ sscanf(tmp1 + 1, "%d:%d", internal_event_num, external_event_num);
+ return 6;
+}
+
+
+/* count is length for one input record */
+static ssize_t hotkey_write_config(struct file *file,
+const char __user * buffer, size_t count,
+loff_t * data)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ /* (struct acpi_hotkey_list *)seq->private; */
+ struct acpi_hotkey_list *hk_list = &hotkey_list;
+ char config_record[MAX_CONFIG_RECORD_LEN];
+ char bus_handle[MAX_NAME_PATH_LEN];
+ char bus_method[MAX_NAME_PATH_LEN];
+ char action_handle[MAX_NAME_PATH_LEN];
+ char method[20];
+ int cmd, internal_event_num, external_event_num;
+ int ret = 0;
+ union acpi_hotkey *key = NULL;
+
+ ACPI_FUNCTION_TRACE("hotkey_write_config");
+
+ if (!hk_list || count > MAX_CONFIG_RECORD_LEN)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid arguments\n"));
+ return_VALUE(-EINVAL);
+ }
+
+ if (copy_from_user(config_record, buffer, count))
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data \n"));
+ return_VALUE(-EINVAL);
+ }
+ config_record[count] = '\0';
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "config_str: %s", config_record));
+
+ ret = get_parms(config_record,
+ /*
+ ret = sscanf(config_record,
+ "%d:%s:%s:%s:%d:%d",
+ */
+ &cmd,
+ bus_handle,
+ bus_method,
+ action_handle,
+ method, &internal_event_num, &external_event_num);
+ if (ret != 6)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format ret=%d\n",
+ ret));
+ return_VALUE(-EINVAL);
+ }
+
+ key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
+ ret = init_hotkey_device(key, bus_handle, action_handle, method,
+ internal_event_num, external_event_num);
+
+ if (ret || check_hotkey_valid(key, &hotkey_list))
+ {
+ free_hotkey_device(key);
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid hotkey \n"));
+ return_VALUE(-EINVAL);
+ }
+ switch (cmd)
+ {
+ case 0:
+ hotkey_add(key);
+ break;
+ case 1:
+ hotkey_remove(key);
+ break;
+ case 2:
+ hotkey_update(key);
+ break;
+ default:
+ break;
+ }
+ return_VALUE(count);
+}
+
+
+/* count is length for one input record */
+static ssize_t hotkey_write_pl_config(struct file *file,
+const char __user * buffer, size_t count,
+loff_t * data)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ struct acpi_hotkey_list *hk_list =
+ (struct acpi_hotkey_list *)m->private;
+
+ char config_record[MAX_CONFIG_RECORD_LEN];
+ char polling_handle[MAX_NAME_PATH_LEN];
+ char action_handle[MAX_NAME_PATH_LEN];
+ char poll_method[20], action_method[20];
+ int ret, internal_event_num, cmd, external_event_num;
+ union acpi_hotkey *key = NULL;
+
+ ACPI_FUNCTION_TRACE("hotkey_write_config");
+
+ if (!hk_list || count > MAX_CONFIG_RECORD_LEN)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid arguments\n"));
+ return_VALUE(-EINVAL);
+ }
+
+ if (copy_from_user(config_record, buffer, count))
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data \n"));
+ return_VALUE(-EINVAL);
+ }
+ config_record[count] = '\0';
+
+ ret = get_parms(config_record,
+ &cmd,
+ polling_handle,
+ poll_method,
+ action_handle,
+ action_method,
+ &internal_event_num, &external_event_num);
+
+ if (ret != 6)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n"));
+ return_VALUE(-EINVAL);
+ }
+
+ key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
+ ret = init_pl_hotkey_device(key, polling_handle, poll_method,
+ action_handle, action_method,
+ internal_event_num);
+ if (ret || check_hotkey_valid(key, &hotkey_list))
+ {
+ free_hotkey_device(key);
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid hotkey \n"));
+ return_VALUE(-EINVAL);
+ }
+ switch (cmd)
+ {
+ case 0:
+ hotkey_add(key);
+ break;
+ case 1:
+ hotkey_remove(key);
+ break;
+ case 2:
+ hotkey_update(key);
+ break;
+ default:
+ break;
+ }
+ return_VALUE(count);
+}
+
+
+/*
+ * This function evaluates an ACPI method, given an int as parameter, the
+ * method is searched within the scope of the handle, can be NULL. The output
+ * of the method is written is output, which can also be NULL
+ *
+ * returns 1 if write is successful, 0 else.
+ */
+static int write_acpi_int(acpi_handle handle, const char *method, int val,
+struct acpi_buffer *output)
+{
+ struct acpi_object_list params; /* list of input parameters (an int here) */
+ union acpi_object in_obj; /* the only param we use */
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE("write_acpi_int");
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = val;
+
+ status = acpi_evaluate_object(handle, (char *)method, &params, output);
+
+ return_VALUE(status == AE_OK);
+}
+
+
+static int read_acpi_int(acpi_handle handle, const char *method, int *val)
+{
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+
+ status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
+ *val = out_obj.integer.value;
+ return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
+}
+
+
+static acpi_handle
+get_handle_from_hotkeylist(struct acpi_hotkey_list *hk_list, int event_num)
+{
+ struct list_head *entries, *next;
+
+ list_for_each_safe(entries, next, hk_list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_type == ACPI_HOTKEY_EVENT
+ && key->lnk.hk_std_num == event_num)
+ {
+ return (key->ev_hk.act_handle);
+ }
+ }
+ return NULL;
+}
+
+
+static
+char *get_method_from_hotkeylist(struct acpi_hotkey_list *hk_list,
+int event_num)
+{
+ struct list_head *entries, *next;
+
+ list_for_each_safe(entries, next, hk_list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+
+ if (key->lnk.hk_type == ACPI_HOTKEY_EVENT &&
+ key->lnk.hk_std_num == event_num)
+ {
+ return (key->ev_hk.act_method);
+ }
+ }
+ return NULL;
+}
+
+
+static
+struct acpi_polling_hotkey *get_hotkey_by_evt(struct acpi_hotkey_list *hk_list,
+int evt)
+{
+ struct list_head *entries, *next;
+
+ list_for_each_safe(entries, next, hk_list->entries)
+ {
+ union acpi_hotkey *key =
+ container_of(entries, union acpi_hotkey, entries);
+ if (key->lnk.hk_type == ACPI_HOTKEY_POLLING
+ && key->lnk.hk_std_num == evt)
+ {
+ return (&key->pl_hk);
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * user call AML method interface:
+ * Call convention:
+ * echo "event_num: arg type : value"
+ * example: echo "1:1:30" > /proc/acpi/action
+ * Just support 1 integer arg passing to AML method
+ */
+
+static ssize_t hotkey_execute_aml_method(struct file *file,
+const char __user * buffer,
+size_t count, loff_t * data)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ /* (struct acpi_hotkey_list *) m->private; */
+ struct acpi_hotkey_list *hk_list = &hotkey_list;
+ char arg[MAX_CALL_PARM];
+ int evt, type, value;
+
+ char *method;
+ acpi_handle handle;
+
+ ACPI_FUNCTION_TRACE("hotkey_execte_action");
+
+ if (!hk_list || count > MAX_CALL_PARM)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 1"));
+ return_VALUE(-EINVAL);
+ }
+
+ if (copy_from_user(arg, buffer, count))
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 2"));
+ return_VALUE(-EINVAL);
+ }
+
+ arg[count] = '\0';
+
+ if (sscanf(arg, "%d:%d:%d", &evt, &type, &value) != 3)
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 3"));
+ return_VALUE(-EINVAL);
+ }
+
+ if (type == ACPI_TYPE_INTEGER)
+ {
+ handle = get_handle_from_hotkeylist(hk_list, evt);
+ method = get_method_from_hotkeylist(hk_list, evt);
+ if (IS_EV_EVENT(evt))
+ write_acpi_int(handle, method, value, NULL);
+ else if (IS_PL_EVENT(evt))
+ {
+ struct acpi_polling_hotkey *hk;
+ hk = (struct acpi_polling_hotkey *)
+ get_hotkey_by_evt(&hotkey_list, evt);
+ read_acpi_int(handle, method, hk->pl_result);
+ }
+ }
+ else
+ {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Not supported"));
+ return_VALUE(-EINVAL);
+ }
+
+ return_VALUE(count);
+}
+
+
+static int __init hotkey_init(void)
+{
+ int result;
+ mode_t mode = S_IFREG | S_IRUGO | S_IWUGO;
+
+ hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir);
+ if (!hotkey_proc_dir)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_PROC);
+ return (-ENODEV);
+ }
+ hotkey_proc_dir->owner = THIS_MODULE;
+
+ hotkey_config =
+ create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir);
+ if (!hotkey_config)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_EV_CONFIG);
+ return (-ENODEV);
+ }
+ else
+ {
+ hotkey_config->proc_fops = &hotkey_config_fops;
+ hotkey_config->data = &hotkey_list;
+ hotkey_config->owner = THIS_MODULE;
+ hotkey_config->uid = 0;
+ hotkey_config->gid = 0;
+ }
+
+ hotkey_pl_config =
+ create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir);
+ if (!hotkey_pl_config)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_EV_CONFIG);
+ return (-ENODEV);
+ }
+ else
+ {
+ hotkey_pl_config->proc_fops = &hotkey_pl_config_fops;
+ hotkey_pl_config->data = &hotkey_list;
+ hotkey_pl_config->owner = THIS_MODULE;
+ hotkey_pl_config->uid = 0;
+ hotkey_pl_config->gid = 0;
+ }
+
+ hotkey_action = create_proc_entry(HOTKEY_ACTION, mode, hotkey_proc_dir);
+ if (!hotkey_action)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_ACTION);
+ return (-ENODEV);
+ }
+ else
+ {
+ hotkey_action->proc_fops = &hotkey_action_fops;
+ hotkey_action->owner = THIS_MODULE;
+ hotkey_action->uid = 0;
+ hotkey_action->gid = 0;
+ }
+
+ hotkey_info = create_proc_entry(HOTKEY_INFO, mode, hotkey_proc_dir);
+ if (!hotkey_info)
+ {
+ printk(KERN_ERR "Hotkey: Unable to create %s entry\n",
+ HOTKEY_INFO);
+ return (-ENODEV);
+ }
+ else
+ {
+ hotkey_info->proc_fops = &hotkey_info_fops;
+ hotkey_info->owner = THIS_MODULE;
+ hotkey_info->uid = 0;
+ hotkey_info->gid = 0;
+ }
+
+ result = acpi_bus_register_driver(&hotkey_driver);
+ if (result < 0)
+ {
+ remove_proc_entry(HOTKEY_PROC, acpi_root_dir);
+ return (-ENODEV);
+ }
+ hotkey_list.count = 0;
+ hotkey_list.entries = &hotkey_entries;
+
+ INIT_LIST_HEAD(&hotkey_entries);
+
+ return (0);
+}
+
+
+static void __exit hotkey_exit(void)
+{
+ acpi_bus_unregister_driver(&hotkey_driver);
+ remove_proc_entry(HOTKEY_PROC, acpi_root_dir);
+ return;
+}
+
+
+module_init(hotkey_init);
+module_exit(hotkey_exit);
diff -BruN orig/drivers/acpi/Makefile 2.6/drivers/acpi/Makefile
--- orig/drivers/acpi/Makefile 2004-11-09 23:10:07.000000000 +0800
+++ 2.6/drivers/acpi/Makefile 2004-11-09 23:08:01.000000000 +0800
@@ -38,7 +38,7 @@
obj-$(CONFIG_ACPI_BUTTON) += button.o
obj-$(CONFIG_ACPI_EC) += ec.o
obj-$(CONFIG_ACPI_FAN) += fan.o
-obj-$(CONFIG_ACPI_VIDEO) += video.o
+obj-$(CONFIG_ACPI_VIDEO) += video.o hotkey.o
obj-$(CONFIG_ACPI_PCI) += pci_root.o pci_link.o pci_irq.o pci_bind.o
obj-$(CONFIG_ACPI_POWER) += power.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o