···1-config PCC_ACPI2- tristate "Panasonic ACPI Hotkey support"3- depends on ACPI4- default n5- ---help---6- This driver provides support for Panasonic hotkeys through the7- ACPI interface. This works for the Panasonic R1 (N variant),8- R2, R3, T2, W2, and Y2 laptops.9-10- To compile this driver as a module, choose M here. The module11- will be called pcc-acpi.
···1-TODO:2- - Lindent fixes3- - checkpatch.pl fixes4- - verify that the acpi interface is correct5- - remove /proc dependancy if needed (not sure yet.)6-7-Please send any patches for this driver to Greg Kroah-Hartman <greg@kroah.com>
···0000000
-1111
drivers/staging/pcc-acpi/pcc-acpi.c
···1-/*2- * Panasonic HotKey and lcd brightness control Extra driver3- * (C) 2004 Hiroshi Miura <miura@da-cha.org>4- * (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/5- * (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>6- * (C) 2004 David Bronaugh <dbronaugh>7- *8- * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte9- *10- * This program is free software; you can redistribute it and/or modify11- * it under the terms of the GNU General Public License version 2 as12- * publicshed by the Free Software Foundation.13- *14- * This program is distributed in the hope that it will be useful,15- * but WITHOUT ANY WARRANTY; without even the implied warranty of16- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17- * GNU General Public License for more details.18- *19- * You should have received a copy of the GNU General Public License20- * along with this program; if not, write to the Free Software21- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA22- *23- *---------------------------------------------------------------------------24- *25- * ChangeLog:26- *27- * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org>28- * -v0.9 remove warning about section reference.29- * remove acpi_os_free30- * add /proc/acpi/pcc/brightness interface to31- * allow HAL to access.32- * merge dbronaugh's enhancement33- * Aug.17, 2004 David Bronaugh (dbronaugh)34- * - Added screen brightness setting interface35- * Thanks to the FreeBSD crew36- * (acpi_panasonic.c authors)37- * for the ideas I needed to accomplish it38- *39- * May.29, 2006 Hiroshi Miura <miura@da-cha.org>40- * -v0.8.4 follow to change keyinput structure41- * thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,42- * Jacob Bower <jacob.bower@ic.ac.uk> and43- * Hiroshi Yokota for providing solutions.44- *45- * Oct.02, 2004 Hiroshi Miura <miura@da-cha.org>46- * -v0.8.2 merge code of YOKOTA Hiroshi47- * <yokota@netlab.is.tsukuba.ac.jp>.48- * Add sticky key mode interface.49- * Refactoring acpi_pcc_generete_keyinput().50- *51- * Sep.15, 2004 Hiroshi Miura <miura@da-cha.org>52- * -v0.8 Generate key input event on input subsystem.53- * This is based on yet another driver54- * written by Ryuta Nakanishi.55- *56- * Sep.10, 2004 Hiroshi Miura <miura@da-cha.org>57- * -v0.7 Change proc interface functions using seq_file58- * facility as same as other ACPI drivers.59- *60- * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org>61- * -v0.6.4 Fix a silly error with status checking62- *63- * Aug.25, 2004 Hiroshi Miura <miura@da-cha.org>64- * -v0.6.3 replace read_acpi_int by standard65- * function acpi_evaluate_integer66- * some clean up and make smart copyright notice.67- * fix return value of pcc_acpi_get_key()68- * fix checking return value of acpi_bus_register_driver()69- *70- * Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org>71- * -v0.6.2 Add check on ACPI data (num_sifr)72- * Coding style cleanups, better error messages/handling73- * Fixed an off-by-one error in memory allocation74- *75- * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org>76- * -v0.6.1 Fix a silly error with status checking77- *78- * Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org>79- * - v0.6 Correct brightness controls to reflect reality80- * based on information gleaned by Hiroshi Miura81- * and discussions with Hiroshi Miura82- *83- * Aug.10, 2004 Hiroshi Miura <miura@da-cha.org>84- * - v0.5 support LCD brightness control85- * based on the disclosed information by MEI.86- *87- * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org>88- * - v0.4 first post version89- * add function to retrive SIFR90- *91- * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org>92- * - v0.3 get proper status of hotkey93- *94- * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org>95- * - v0.2 add HotKey handler96- *97- * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org>98- * - v0.1 start from toshiba_acpi driver written by John Belmonte99- *100- */101-102-#define ACPI_PCC_VERSION "0.9+hy"103-104-#include <linux/kernel.h>105-#include <linux/module.h>106-#include <linux/types.h>107-#include <linux/ctype.h>108-#include <linux/init.h>109-#include <linux/input.h>110-#include <linux/proc_fs.h>111-#include <linux/seq_file.h>112-#include <linux/slab.h>113-#include <linux/uaccess.h>114-#include <acpi/acpi_bus.h>115-#include <acpi/acpi_drivers.h>116-117-118-/*************************************************************************119- * "seq" file template definition.120- */121-/* "seq" initializer */122-#define SEQ_OPEN_FS(_open_func_name_, _show_func_name_) \123-static int _open_func_name_(struct inode *inode, struct file *file) \124-{ \125- return single_open(file, _show_func_name_, PDE(inode)->data); \126-}127-128-/*-------------------------------------------------------------------------129- * "seq" fops template for read-only files.130- */131-#define SEQ_FILEOPS_R(_open_func_name_) \132-{ \133- .open = _open_func_name_, \134- .read = seq_read, \135- .llseek = seq_lseek, \136- .release = single_release, \137-}138-139-/*------------------------------------------------------------------------140- * "seq" fops template for read-write files.141- */142-#define SEQ_FILEOPS_RW(_open_func_name_, _write_func_name_) \143-{ \144- .open = _open_func_name_ , \145- .read = seq_read, \146- .write = _write_func_name_, \147- .llseek = seq_lseek, \148- .release = single_release, \149-}150-151-/*152- * "seq" file template definition ended.153- ***************************************************************************154- */155-#ifndef ACPI_HOTKEY_COMPONENT156-#define ACPI_HOTKEY_COMPONENT 0x10000000157-#endif158-159-#define _COMPONENT ACPI_HOTKEY_COMPONENT160-ACPI_MODULE_NAME("pcc_acpi");161-162-MODULE_AUTHOR("Hiroshi Miura, Hiroshi Yokota");163-MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");164-MODULE_LICENSE("GPL");165-166-#define LOGPREFIX "pcc_acpi: "167-168-/****************************************************169- * Define ACPI PATHs170- ****************************************************/171-/* Lets note hotkeys */172-#define METHOD_HKEY_QUERY "HINF"173-#define METHOD_HKEY_SQTY "SQTY"174-#define METHOD_HKEY_SINF "SINF"175-#define METHOD_HKEY_SSET "SSET"176-#define HKEY_NOTIFY 0x80177-178-/* for brightness control */179-#define LCD_MAX_BRIGHTNESS 255180-/* This may be magical -- beware */181-#define LCD_BRIGHTNESS_INCREMENT 17182-/* Registers of SINF */183-#define SINF_LCD_BRIGHTNESS 4184-185-/*******************************************************************186- *187- * definitions for /proc/ interface188- *189- *******************************************************************/190-#define ACPI_PCC_DRIVER_NAME "pcc_acpi"191-#define ACPI_PCC_DEVICE_NAME "PCCExtra"192-#define ACPI_PCC_CLASS "pcc"193-#define PROC_PCC ACPI_PCC_CLASS194-195-#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"196-197-/* This is transitional definition */198-#ifndef KEY_BATT199-# define KEY_BATT 227200-#endif201-202-#define PROC_STR_MAX_LEN 8203-204-#define BUS_PCC_HOTKEY BUS_I8042 /*0x1a*/ /* FIXME: BUS_I8042? */205-206-/* Fn+F4/F5 confricts with Shift+F1/F2 */207-/* This hack avoids key number confrict */208-#define PCC_KEYINPUT_MODE (0)209-210-/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent211- ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00212-*/213-enum SINF_BITS { SINF_NUM_BATTERIES = 0,214- SINF_LCD_TYPE,215- SINF_AC_MAX_BRIGHT,216- SINF_AC_MIN_BRIGHT,217- SINF_AC_CUR_BRIGHT,218- /* 4 = R1 only handle SINF_AC_CUR_BRIGHT219- * as SINF_CUR_BRIGHT and don't know AC state */220- SINF_DC_MAX_BRIGHT,221- SINF_DC_MIN_BRIGHT,222- SINF_DC_CUR_BRIGHT,223- SINF_MUTE,224- SINF_RESERVED,225- SINF_ENV_STATE, /* 10 */226- SINF_STICKY_KEY = 0x80,227-};228-229-static struct acpi_device_id pcc_device_ids[] = {230- {"MAT0012", 0},231- {"MAT0013", 0},232- {"MAT0018", 0},233- {"MAT0019", 0},234- {"", 0},235-};236-MODULE_DEVICE_TABLE(acpi, pcc_device_ids);237-238-239-static int __devinit acpi_pcc_hotkey_add(struct acpi_device *device);240-static int __devexit acpi_pcc_hotkey_remove(struct acpi_device *device,241- int type);242-static int acpi_pcc_hotkey_resume(struct acpi_device *device);243-244-245-static struct acpi_driver acpi_pcc_driver = {246- .name = ACPI_PCC_DRIVER_NAME,247- .class = ACPI_PCC_CLASS,248- .ids = pcc_device_ids,249- .ops = {250- .add = acpi_pcc_hotkey_add,251- .remove = __devexit_p(acpi_pcc_hotkey_remove),252-#ifdef CONFIG_PM253- /*.suspend = acpi_pcc_hotkey_suspend,*/254- .resume = acpi_pcc_hotkey_resume,255-#endif256- },257-};258-259-struct acpi_hotkey {260- acpi_handle handle;261- struct acpi_device *device;262- struct proc_dir_entry *proc_dir_entry;263- unsigned long num_sifr;264- unsigned long status;265- struct input_dev *input_dev;266- int sticky_mode;267-};268-269-struct pcc_keyinput {270- struct acpi_hotkey *hotkey;271- int key_mode;272-};273-274-/* *************************************************************************275- Hotkey driver core276- ************************************************************************* */277-/* -------------------------------------------------------------------------278- method access functions279- ------------------------------------------------------------------------- */280-static int acpi_pcc_write_sset(struct acpi_hotkey *hotkey, int func, int val)281-{282- union acpi_object in_objs[] = {283- { .integer.type = ACPI_TYPE_INTEGER,284- .integer.value = func, },285- { .integer.type = ACPI_TYPE_INTEGER,286- .integer.value = val, },287- };288- struct acpi_object_list params = {289- .count = ARRAY_SIZE(in_objs),290- .pointer = in_objs,291- };292- acpi_status status;293-294- ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");295-296- status = acpi_evaluate_object(hotkey->handle, METHOD_HKEY_SSET,297- ¶ms, NULL);298-299- return_VALUE(status == AE_OK ? AE_OK : AE_ERROR);300-}301-302-static inline int acpi_pcc_get_sqty(struct acpi_device *device)303-{304- unsigned long s;305- acpi_status status;306-307- ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");308-309- status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,310- NULL, &s);311- if (ACPI_SUCCESS(status)) {312- return_VALUE(s);313- } else {314- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,315- "evaluation error HKEY.SQTY\n"));316- return_VALUE(-EINVAL);317- }318-}319-320-static int acpi_pcc_retrieve_biosdata(struct acpi_hotkey *hotkey, u32 *sinf)321-{322- acpi_status status;323- struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};324- union acpi_object *hkey = NULL;325- int i;326-327- ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");328-329- status = acpi_evaluate_object(hotkey->handle, METHOD_HKEY_SINF, 0,330- &buffer);331- if (ACPI_FAILURE(status)) {332- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,333- "evaluation error HKEY.SINF\n"));334- status = AE_ERROR;335- return_VALUE(status);336- }337-338- hkey = buffer.pointer;339- if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {340- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));341- goto free_buffer;342- }343-344- if (hotkey->num_sifr < hkey->package.count) {345- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,346- "SQTY reports bad SINF length\n"));347- status = AE_ERROR;348- goto free_buffer;349- }350-351- for (i = 0; i < hkey->package.count; i++) {352- union acpi_object *element = &(hkey->package.elements[i]);353- if (likely(element->type == ACPI_TYPE_INTEGER)) {354- sinf[i] = element->integer.value;355- } else {356- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,357- "Invalid HKEY.SINF data\n"));358- status = AE_ERROR;359- break;360- }361- }362- sinf[hkey->package.count] = -1;363-364- free_buffer:365- kfree(buffer.pointer);366- return_VALUE(status == AE_OK ? AE_OK : AE_ERROR);367-}368-369-static int acpi_pcc_read_sinf_field(struct seq_file *seq, int field)370-{371- struct acpi_hotkey *hotkey = (struct acpi_hotkey *) seq->private;372- u32 sinf[hotkey->num_sifr + 1];373-374- ACPI_FUNCTION_TRACE("acpi_pcc_read_sinf_field");375-376- if (ACPI_SUCCESS(acpi_pcc_retrieve_biosdata(hotkey, sinf)))377- seq_printf(seq, "%u\n", sinf[field]);378- else379- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,380- "Couldn't retrieve BIOS data\n"));381-382- return_VALUE(AE_OK);383-}384-385-/* -------------------------------------------------------------------------386- user interface functions387- ------------------------------------------------------------------------- */388-/* read methods */389-/* Sinf read methods */390-#define PCC_SINF_READ_F(_name_, FUNC) \391-static int _name_(struct seq_file *seq, void *offset) \392-{ \393- return_VALUE(ACPI_SUCCESS(acpi_pcc_read_sinf_field(seq, \394- (FUNC))) \395- ? 0 : -EINVAL); \396-}397-398-PCC_SINF_READ_F(acpi_pcc_numbatteries_show, SINF_NUM_BATTERIES);399-PCC_SINF_READ_F(acpi_pcc_lcdtype_show, SINF_LCD_TYPE);400-PCC_SINF_READ_F(acpi_pcc_ac_brightness_max_show, SINF_AC_MAX_BRIGHT);401-PCC_SINF_READ_F(acpi_pcc_ac_brightness_min_show, SINF_AC_MIN_BRIGHT);402-PCC_SINF_READ_F(acpi_pcc_ac_brightness_show, SINF_AC_CUR_BRIGHT);403-PCC_SINF_READ_F(acpi_pcc_dc_brightness_max_show, SINF_DC_MAX_BRIGHT);404-PCC_SINF_READ_F(acpi_pcc_dc_brightness_min_show, SINF_DC_MIN_BRIGHT);405-PCC_SINF_READ_F(acpi_pcc_dc_brightness_show, SINF_DC_CUR_BRIGHT);406-PCC_SINF_READ_F(acpi_pcc_brightness_show, SINF_AC_CUR_BRIGHT);407-PCC_SINF_READ_F(acpi_pcc_mute_show, SINF_MUTE);408-409-static int acpi_pcc_sticky_key_show(struct seq_file *seq, void *offset)410-{411- struct acpi_hotkey *hotkey = seq->private;412-413- ACPI_FUNCTION_TRACE("acpi_pcc_sticky_key_show");414-415- if (!hotkey || !hotkey->device)416- return_VALUE(-EINVAL);417-418- seq_printf(seq, "%d\n", hotkey->sticky_mode);419-420- return_VALUE(0);421-}422-423-static int acpi_pcc_keyinput_show(struct seq_file *seq, void *offset)424-{425- struct acpi_hotkey *hotkey = seq->private;426- struct input_dev *hotk_input_dev = hotkey->input_dev;427- struct pcc_keyinput *keyinput = input_get_drvdata(hotk_input_dev);428-429- ACPI_FUNCTION_TRACE("acpi_pcc_keyinput_show");430-431- seq_printf(seq, "%d\n", keyinput->key_mode);432-433- return_VALUE(0);434-}435-436-static int acpi_pcc_version_show(struct seq_file *seq, void *offset)437-{438- struct acpi_hotkey *hotkey = seq->private;439-440- ACPI_FUNCTION_TRACE("acpi_pcc_version_show");441-442- if (!hotkey || !hotkey->device)443- return_VALUE(-EINVAL);444-445- seq_printf(seq, "%s version %s\n", ACPI_PCC_DRIVER_NAME,446- ACPI_PCC_VERSION);447- seq_printf(seq, "%li functions\n", hotkey->num_sifr);448-449- return_VALUE(0);450-}451-452-/* write methods */453-static ssize_t acpi_pcc_write_single_flag(struct file *file,454- const char __user *buffer,455- size_t count,456- int sinf_func)457-{458- struct seq_file *seq = file->private_data;459- struct acpi_hotkey *hotkey = seq->private;460- char write_string[PROC_STR_MAX_LEN];461- u32 val;462-463- ACPI_FUNCTION_TRACE("acpi_pcc_write_single_flag");464-465- if (!hotkey || (count > sizeof(write_string) - 1))466- return_VALUE(-EINVAL);467-468- if (copy_from_user(write_string, buffer, count))469- return_VALUE(-EFAULT);470-471- write_string[count] = '\0';472-473- if ((sscanf(write_string, "%3i", &val) == 1) &&474- (val == 0 || val == 1))475- acpi_pcc_write_sset(hotkey, sinf_func, val);476-477- return_VALUE(count);478-}479-480-static unsigned long acpi_pcc_write_brightness(struct file *file,481- const char __user *buffer,482- size_t count,483- int min_index, int max_index,484- int cur_index)485-{486- struct seq_file *seq = (struct seq_file *)file->private_data;487- struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private;488- char write_string[PROC_STR_MAX_LEN];489- u32 bright;490- u32 sinf[hotkey->num_sifr + 1];491-492- ACPI_FUNCTION_TRACE("acpi_pcc_write_brightness");493-494- if (!hotkey || (count > sizeof(write_string) - 1))495- return_VALUE(-EINVAL);496-497- if (copy_from_user(write_string, buffer, count))498- return_VALUE(-EFAULT);499-500- write_string[count] = '\0';501-502- if (ACPI_FAILURE(acpi_pcc_retrieve_biosdata(hotkey, sinf))) {503- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,504- "Couldn't retrieve BIOS data\n"));505- goto end;506- }507-508- if ((sscanf(write_string, "%4i", &bright) == 1) &&509- (bright >= sinf[min_index]) &&510- (bright <= sinf[max_index]))511- acpi_pcc_write_sset(hotkey, cur_index, bright);512-513-end:514- return_VALUE(count);515-}516-517-static ssize_t acpi_pcc_write_ac_brightness(struct file *file,518- const char __user *buffer,519- size_t count, loff_t *ppos)520-{521- return_VALUE(acpi_pcc_write_brightness(file, buffer, count,522- SINF_AC_MIN_BRIGHT,523- SINF_AC_MAX_BRIGHT,524- SINF_AC_CUR_BRIGHT));525-}526-527-static ssize_t acpi_pcc_write_dc_brightness(struct file *file,528- const char __user *buffer,529- size_t count, loff_t *ppos)530-{531- return_VALUE(acpi_pcc_write_brightness(file, buffer, count,532- SINF_DC_MIN_BRIGHT,533- SINF_DC_MAX_BRIGHT,534- SINF_DC_CUR_BRIGHT));535-}536-537-static ssize_t acpi_pcc_write_no_brightness(struct file *file,538- const char __user *buffer,539- size_t count, loff_t *ppos)540-{541- return acpi_pcc_write_brightness(file, buffer, count,542- SINF_AC_MIN_BRIGHT,543- SINF_AC_MAX_BRIGHT,544- SINF_AC_CUR_BRIGHT);545-}546-547-static ssize_t acpi_pcc_write_mute(struct file *file,548- const char __user *buffer,549- size_t count, loff_t *ppos)550-{551- return_VALUE(acpi_pcc_write_single_flag(file, buffer, count,552- SINF_MUTE));553-}554-555-static ssize_t acpi_pcc_write_sticky_key(struct file *file,556- const char __user *buffer,557- size_t count, loff_t *ppos)558-{559- struct seq_file *seq = (struct seq_file *)file->private_data;560- struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private;561- char write_string[PROC_STR_MAX_LEN];562- int mode;563-564- ACPI_FUNCTION_TRACE("acpi_pcc_write_sticky_key");565-566- if (!hotkey || (count > sizeof(write_string) - 1))567- return_VALUE(-EINVAL);568-569- if (copy_from_user(write_string, buffer, count))570- return_VALUE(-EFAULT);571-572- write_string[count] = '\0';573-574- if ((sscanf(write_string, "%3i", &mode) == 1) &&575- (mode == 0 || mode == 1)) {576- acpi_pcc_write_sset(hotkey, SINF_STICKY_KEY, mode);577- hotkey->sticky_mode = mode;578- }579-580- return_VALUE(count);581-}582-583-static ssize_t acpi_pcc_write_keyinput(struct file *file,584- const char __user *buffer,585- size_t count, loff_t *ppos)586-{587- struct seq_file *seq = (struct seq_file *)file->private_data;588- struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private;589- struct pcc_keyinput *keyinput;590- char write_string[PROC_STR_MAX_LEN];591- int key_mode;592-593- ACPI_FUNCTION_TRACE("acpi_pcc_write_keyinput");594-595- if (!hotkey || (count > (sizeof(write_string) - 1)))596- return_VALUE(-EINVAL);597-598- if (copy_from_user(write_string, buffer, count))599- return_VALUE(-EFAULT);600-601- write_string[count] = '\0';602-603- if ((sscanf(write_string, "%4i", &key_mode) == 1) &&604- (key_mode == 0 || key_mode == 1)) {605- keyinput = input_get_drvdata(hotkey->input_dev);606- keyinput->key_mode = key_mode;607- }608-609- return_VALUE(count);610-}611-612-/* -------------------------------------------------------------------------613- hotkey driver614- ------------------------------------------------------------------------- */615-static void acpi_pcc_generete_keyinput(struct acpi_hotkey *hotkey)616-{617- struct input_dev *hotk_input_dev = hotkey->input_dev;618- struct pcc_keyinput *keyinput = input_get_drvdata(hotk_input_dev);619- int hinf = hotkey->status;620- int key_code, hkey_num;621- const int key_map[] = {622- /* 0 */ -1,623- /* 1 */ KEY_BRIGHTNESSDOWN,624- /* 2 */ KEY_BRIGHTNESSUP,625- /* 3 */ -1, /* vga/lcd switch event is not occur on626- hotkey driver. */627- /* 4 */ KEY_MUTE,628- /* 5 */ KEY_VOLUMEDOWN,629- /* 6 */ KEY_VOLUMEUP,630- /* 7 */ KEY_SLEEP,631- /* 8 */ -1, /* Change CPU boost: do nothing */632- /* 9 */ KEY_BATT,633- /* 10 */ KEY_SUSPEND,634- };635-636- ACPI_FUNCTION_TRACE("acpi_pcc_generete_keyinput");637-638- if (keyinput->key_mode == 0)639- return_VOID;640-641- hkey_num = hinf & 0xf;642-643- if ((0 > hkey_num) ||644- (hkey_num > ARRAY_SIZE(key_map))) {645- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,646- "hotkey number out of range: %d\n",647- hkey_num));648- return_VOID;649- }650-651- key_code = key_map[hkey_num];652-653- if (key_code != -1) {654- int pushed = (hinf & 0x80) ? TRUE : FALSE;655-656- input_report_key(hotk_input_dev, key_code, pushed);657- input_sync(hotk_input_dev);658- }659-}660-661-static int acpi_pcc_hotkey_get_key(struct acpi_hotkey *hotkey)662-{663- unsigned long result;664- acpi_status status = AE_OK;665-666- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_get_key");667-668- status = acpi_evaluate_integer(hotkey->handle, METHOD_HKEY_QUERY,669- NULL, &result);670- if (likely(ACPI_SUCCESS(status)))671- hotkey->status = result;672- else673- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,674- "error getting hotkey status\n"));675-676- return_VALUE(status == AE_OK);677-}678-679-void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)680-{681- struct acpi_hotkey *hotkey = (struct acpi_hotkey *) data;682-683- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");684-685- switch (event) {686- case HKEY_NOTIFY:687- if (acpi_pcc_hotkey_get_key(hotkey)) {688- /* generate event like '"pcc HKEY 00000080 00000084"'689- * when Fn+F4 pressed */690- acpi_bus_generate_proc_event(hotkey->device, event,691- hotkey->status);692- }693- acpi_pcc_generete_keyinput(hotkey);694- break;695- default:696- /* nothing to do */697- break;698- }699- return_VOID;700-}701-702-/* *************************************************************************703- FS Interface (/proc)704- ************************************************************************* */705-/* oepn proc file fs*/706-SEQ_OPEN_FS(acpi_pcc_dc_brightness_open_fs, acpi_pcc_dc_brightness_show);707-SEQ_OPEN_FS(acpi_pcc_numbatteries_open_fs, acpi_pcc_numbatteries_show);708-SEQ_OPEN_FS(acpi_pcc_lcdtype_open_fs, acpi_pcc_lcdtype_show);709-SEQ_OPEN_FS(acpi_pcc_ac_brightness_max_open_fs,710- acpi_pcc_ac_brightness_max_show);711-SEQ_OPEN_FS(acpi_pcc_ac_brightness_min_open_fs,712- acpi_pcc_ac_brightness_min_show);713-SEQ_OPEN_FS(acpi_pcc_ac_brightness_open_fs, acpi_pcc_ac_brightness_show);714-SEQ_OPEN_FS(acpi_pcc_dc_brightness_max_open_fs,715- acpi_pcc_dc_brightness_max_show);716-SEQ_OPEN_FS(acpi_pcc_dc_brightness_min_open_fs,717- acpi_pcc_dc_brightness_min_show);718-SEQ_OPEN_FS(acpi_pcc_brightness_open_fs, acpi_pcc_brightness_show);719-SEQ_OPEN_FS(acpi_pcc_mute_open_fs, acpi_pcc_mute_show);720-SEQ_OPEN_FS(acpi_pcc_version_open_fs, acpi_pcc_version_show);721-SEQ_OPEN_FS(acpi_pcc_keyinput_open_fs, acpi_pcc_keyinput_show);722-SEQ_OPEN_FS(acpi_pcc_sticky_key_open_fs, acpi_pcc_sticky_key_show);723-724-static struct file_operations acpi_pcc_numbatteries_fops =725- SEQ_FILEOPS_R(acpi_pcc_numbatteries_open_fs);726-static struct file_operations acpi_pcc_lcdtype_fops =727- SEQ_FILEOPS_R(acpi_pcc_lcdtype_open_fs);728-static struct file_operations acpi_pcc_mute_fops =729- SEQ_FILEOPS_RW(acpi_pcc_mute_open_fs, acpi_pcc_write_mute);730-static struct file_operations acpi_pcc_ac_brightness_fops =731- SEQ_FILEOPS_RW(acpi_pcc_ac_brightness_open_fs,732- acpi_pcc_write_ac_brightness);733-static struct file_operations acpi_pcc_ac_brightness_max_fops =734- SEQ_FILEOPS_R(acpi_pcc_ac_brightness_max_open_fs);735-static struct file_operations acpi_pcc_ac_brightness_min_fops =736- SEQ_FILEOPS_R(acpi_pcc_ac_brightness_min_open_fs);737-static struct file_operations acpi_pcc_dc_brightness_fops =738- SEQ_FILEOPS_RW(acpi_pcc_dc_brightness_open_fs,739- acpi_pcc_write_dc_brightness);740-static struct file_operations acpi_pcc_dc_brightness_max_fops =741- SEQ_FILEOPS_R(acpi_pcc_dc_brightness_max_open_fs);742-static struct file_operations acpi_pcc_dc_brightness_min_fops =743- SEQ_FILEOPS_R(acpi_pcc_dc_brightness_min_open_fs);744-static struct file_operations acpi_pcc_brightness_fops =745- SEQ_FILEOPS_RW(acpi_pcc_brightness_open_fs,746- acpi_pcc_write_no_brightness);747-static struct file_operations acpi_pcc_sticky_key_fops =748- SEQ_FILEOPS_RW(acpi_pcc_sticky_key_open_fs, acpi_pcc_write_sticky_key);749-static struct file_operations acpi_pcc_keyinput_fops =750- SEQ_FILEOPS_RW(acpi_pcc_keyinput_open_fs, acpi_pcc_write_keyinput);751-static struct file_operations acpi_pcc_version_fops =752- SEQ_FILEOPS_R(acpi_pcc_version_open_fs);753-754-struct proc_item {755- const char *name;756- struct file_operations *fops;757- mode_t flag;758-};759-760-/* Note: These functions map *exactly* to the SINF/SSET functions */761-struct proc_item acpi_pcc_proc_items_sifr[] = {762- { "num_batteries", &acpi_pcc_numbatteries_fops, S_IRUGO },763- { "lcd_type", &acpi_pcc_lcdtype_fops, S_IRUGO },764- { "ac_brightness_max", &acpi_pcc_ac_brightness_max_fops, S_IRUGO },765- { "ac_brightness_min", &acpi_pcc_ac_brightness_min_fops, S_IRUGO },766- { "ac_brightness", &acpi_pcc_ac_brightness_fops,767- S_IFREG | S_IRUGO | S_IWUSR },768- { "dc_brightness_max", &acpi_pcc_dc_brightness_max_fops, S_IRUGO },769- { "dc_brightness_min", &acpi_pcc_dc_brightness_min_fops, S_IRUGO },770- { "dc_brightness", &acpi_pcc_dc_brightness_fops,771- S_IFREG | S_IRUGO | S_IWUSR },772- { "brightness", &acpi_pcc_brightness_fops, S_IFREG | S_IRUGO | S_IWUSR},773- { "mute", &acpi_pcc_mute_fops, S_IFREG | S_IRUGO | S_IWUSR },774- { NULL, NULL, 0 },775-};776-777-struct proc_item acpi_pcc_proc_items[] = {778- { "sticky_key", &acpi_pcc_sticky_key_fops, S_IFREG | S_IRUGO | S_IWUSR},779- { "keyinput", &acpi_pcc_keyinput_fops, S_IFREG | S_IRUGO | S_IWUSR },780- { "version", &acpi_pcc_version_fops, S_IRUGO },781- { NULL, NULL, 0 },782-};783-784-static int __devinit acpi_pcc_add_device(struct acpi_device *device,785- struct proc_item *proc_items,786- int num)787-{788- struct acpi_hotkey *hotkey = acpi_driver_data(device);789- struct proc_dir_entry *proc;790- struct proc_item *item;791- int i;792-793- for (item = proc_items, i = 0; item->name && i < num; ++item, ++i) {794- proc = create_proc_entry(item->name, item->flag,795- hotkey->proc_dir_entry);796- if (likely(proc)) {797- proc->proc_fops = item->fops;798- proc->data = hotkey;799- proc->owner = THIS_MODULE;800- } else {801- while (i-- > 0) {802- item--;803- remove_proc_entry(item->name,804- hotkey->proc_dir_entry);805- }806- return_VALUE(-ENODEV);807- }808- }809- return_VALUE(0);810-}811-812-static int __devinit acpi_pcc_proc_init(struct acpi_device *device)813-{814- struct proc_dir_entry *acpi_pcc_dir;815- struct acpi_hotkey *hotkey = acpi_driver_data(device);816- acpi_status status;817-818- ACPI_FUNCTION_TRACE("acpi_pcc_proc_init");819-820- acpi_pcc_dir = proc_mkdir(PROC_PCC, acpi_root_dir);821-822- if (unlikely(!acpi_pcc_dir)) {823- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,824- "Couldn't create dir in /proc\n"));825- return_VALUE(-ENODEV);826- }827-828- acpi_pcc_dir->owner = THIS_MODULE;829- hotkey->proc_dir_entry = acpi_pcc_dir;830-831- status = acpi_pcc_add_device(device, acpi_pcc_proc_items_sifr,832- hotkey->num_sifr);833- status |= acpi_pcc_add_device(device, acpi_pcc_proc_items,834- ARRAY_SIZE(acpi_pcc_proc_items));835- if (unlikely(status)) {836- remove_proc_entry(PROC_PCC, acpi_root_dir);837- hotkey->proc_dir_entry = NULL;838- return_VALUE(-ENODEV);839- }840-841- return_VALUE(status);842-}843-844-static void __devexit acpi_pcc_remove_device(struct acpi_device *device,845- struct proc_item *proc_items,846- int num)847-{848- struct acpi_hotkey *hotkey = acpi_driver_data(device);849- struct proc_item *item;850- int i;851-852- for (item = proc_items, i = 0;853- item->name != NULL && i < num;854- ++item, ++i) {855- remove_proc_entry(item->name, hotkey->proc_dir_entry);856- }857-858- return_VOID;859-}860-861-/* *************************************************************************862- Power Management863- ************************************************************************* */864-#ifdef CONFIG_PM865-static int acpi_pcc_hotkey_resume(struct acpi_device *device)866-{867- struct acpi_hotkey *hotkey = acpi_driver_data(device);868- acpi_status status = AE_OK;869-870- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");871-872- if (device == NULL || hotkey == NULL)873- return_VALUE(-EINVAL);874-875- if (hotkey->num_sifr != 0) {876- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Sticky mode restore: %d\n",877- hotkey->sticky_mode));878-879- status = acpi_pcc_write_sset(hotkey, SINF_STICKY_KEY,880- hotkey->sticky_mode);881- }882- if (status != AE_OK)883- return_VALUE(-EINVAL);884-885- return_VALUE(0);886-}887-#endif888-889-/* *************************************************************************890- Module init/remove891- ************************************************************************* */892-/* -------------------------------------------------------------------------893- input894- ------------------------------------------------------------------------- */895-static int __devinit acpi_pcc_init_input(struct acpi_hotkey *hotkey)896-{897- struct input_dev *hotk_input_dev;898- struct pcc_keyinput *pcc_keyinput;899- int error;900-901- ACPI_FUNCTION_TRACE("acpi_pcc_init_input");902-903- hotk_input_dev = input_allocate_device();904- if (hotk_input_dev == NULL) {905- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,906- "Couldn't allocate input device for hotkey"));907- goto err_input;908- }909-910- pcc_keyinput = kcalloc(1, sizeof(struct pcc_keyinput), GFP_KERNEL);911-912- if (pcc_keyinput == NULL) {913- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,914- "Couldn't allocate mem for private data"));915- goto err_pcc;916- }917-918- hotk_input_dev->evbit[0] = BIT(EV_KEY);919-920- set_bit(KEY_BRIGHTNESSDOWN, hotk_input_dev->keybit);921- set_bit(KEY_BRIGHTNESSUP, hotk_input_dev->keybit);922- set_bit(KEY_MUTE, hotk_input_dev->keybit);923- set_bit(KEY_VOLUMEDOWN, hotk_input_dev->keybit);924- set_bit(KEY_VOLUMEUP, hotk_input_dev->keybit);925- set_bit(KEY_SLEEP, hotk_input_dev->keybit);926- set_bit(KEY_BATT, hotk_input_dev->keybit);927- set_bit(KEY_SUSPEND, hotk_input_dev->keybit);928-929- hotk_input_dev->name = ACPI_PCC_DRIVER_NAME;930- hotk_input_dev->phys = ACPI_PCC_INPUT_PHYS;931- hotk_input_dev->id.bustype = BUS_PCC_HOTKEY;932- hotk_input_dev->id.vendor = 0x0001;933- hotk_input_dev->id.product = 0x0001;934- hotk_input_dev->id.version = 0x0100;935-936- pcc_keyinput->key_mode = PCC_KEYINPUT_MODE;937- pcc_keyinput->hotkey = hotkey;938-939- input_set_drvdata(hotk_input_dev, pcc_keyinput);940-941- hotkey->input_dev = hotk_input_dev;942-943- error = input_register_device(hotk_input_dev);944-945- if (error)946- goto err_pcc;947-948- return_VALUE(0);949-950- err_pcc:951- input_unregister_device(hotk_input_dev);952- err_input:953- return_VALUE(-ENOMEM);954-}955-956-static void __devexit acpi_pcc_remove_input(struct acpi_hotkey *hotkey)957-{958- struct input_dev *hotk_input_dev;959- struct pcc_keyinput *pcc_keyinput;960-961- ACPI_FUNCTION_TRACE("acpi_pcc_remove_input");962-963- if (hotkey == NULL) {964- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Can't free memory"));965- return_VOID;966- }967-968- hotk_input_dev = hotkey->input_dev;969- pcc_keyinput = input_get_drvdata(hotk_input_dev);970-971- input_unregister_device(hotk_input_dev);972-973- kfree(pcc_keyinput);974-}975-976-/* -------------------------------------------------------------------------977- ACPI978- ------------------------------------------------------------------------- */979-static int __devinit acpi_pcc_hotkey_add(struct acpi_device *device)980-{981- acpi_status status = AE_OK;982- struct acpi_hotkey *hotkey = NULL;983- int sifr_status, num_sifr, result;984-985- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");986-987- if (device == NULL)988- return_VALUE(-EINVAL);989-990- sifr_status = acpi_pcc_get_sqty(device);991-992- if (sifr_status > 255) {993- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));994- return_VALUE(-ENODEV);995- }996-997- if (sifr_status < 0) {998- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "not support SQTY"));999- num_sifr = 0;1000- } else {1001- num_sifr = sifr_status;1002- }1003-1004- hotkey = kcalloc(1, sizeof(struct acpi_hotkey), GFP_KERNEL);1005- if (hotkey == NULL) {1006- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,1007- "Couldn't allocate mem for hotkey"));1008- return_VALUE(-ENOMEM);1009- }1010-1011- hotkey->device = device;1012- hotkey->handle = device->handle;1013- hotkey->num_sifr = num_sifr;1014- device->driver_data = hotkey;1015- strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);1016- strcpy(acpi_device_class(device), ACPI_PCC_CLASS);1017-1018- status = acpi_install_notify_handler(hotkey->handle,1019- ACPI_DEVICE_NOTIFY,1020- acpi_pcc_hotkey_notify,1021- hotkey);1022-1023- if (ACPI_FAILURE(status)) {1024- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,1025- "Error installing notify handler\n"));1026- kfree(hotkey);1027- return_VALUE(-ENODEV);1028- }1029-1030- result = acpi_pcc_init_input(hotkey);1031- if (result != 0) {1032- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,1033- "Error installing keyinput handler\n"));1034- kfree(hotkey);1035- return_VALUE(result);1036- }1037-1038- return_VALUE(acpi_pcc_proc_init(device));1039-}1040-1041-static int __devexit acpi_pcc_hotkey_remove(struct acpi_device *device,1042- int type)1043-{1044- acpi_status status = AE_OK;1045- struct acpi_hotkey *hotkey = acpi_driver_data(device);1046-1047- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");1048-1049- if (!device || !hotkey)1050- return_VALUE(-EINVAL);1051-1052- if (hotkey->proc_dir_entry) {1053- acpi_pcc_remove_device(device, acpi_pcc_proc_items_sifr,1054- hotkey->num_sifr);1055- acpi_pcc_remove_device(device, acpi_pcc_proc_items,1056- ARRAY_SIZE(acpi_pcc_proc_items));1057- remove_proc_entry(PROC_PCC, acpi_root_dir);1058- }1059-1060- status = acpi_remove_notify_handler(hotkey->handle,1061- ACPI_DEVICE_NOTIFY, acpi_pcc_hotkey_notify);1062-1063- if (ACPI_FAILURE(status)) {1064- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,1065- "Error removing notify handler\n"));1066- }1067-1068- acpi_pcc_remove_input(hotkey);1069- kfree(hotkey);1070- return_VALUE(status == AE_OK);1071-}1072-1073-/* *********************************************************************1074- Module entry point1075- ********************************************************************* */1076-static int __init acpi_pcc_init(void)1077-{1078- int result;1079-1080- ACPI_FUNCTION_TRACE("acpi_pcc_init");1081-1082- printk(KERN_INFO LOGPREFIX "loading...\n");1083-1084- if (acpi_disabled) {1085- printk(KERN_INFO LOGPREFIX "ACPI disabled.\n");1086- return_VALUE(-ENODEV);1087- }1088-1089- result = acpi_bus_register_driver(&acpi_pcc_driver);1090- if (result < 0) {1091- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,1092- "Error registering hotkey driver\n"));1093- return_VALUE(-ENODEV);1094- }1095-1096- return_VALUE(result);1097-}1098-1099-static void __exit acpi_pcc_exit(void)1100-{1101- ACPI_FUNCTION_TRACE("acpi_pcc_exit");1102-1103- printk(KERN_INFO LOGPREFIX "unloading...\n");1104-1105- acpi_bus_unregister_driver(&acpi_pcc_driver);1106-1107- return_VOID;1108-}1109-1110-module_init(acpi_pcc_init);1111-module_exit(acpi_pcc_exit);