···11-config PCC_ACPI22- tristate "Panasonic ACPI Hotkey support"33- depends on ACPI44- default n55- ---help---66- This driver provides support for Panasonic hotkeys through the77- ACPI interface. This works for the Panasonic R1 (N variant),88- R2, R3, T2, W2, and Y2 laptops.99-1010- To compile this driver as a module, choose M here. The module1111- will be called pcc-acpi.
···11-TODO:22- - Lindent fixes33- - checkpatch.pl fixes44- - verify that the acpi interface is correct55- - remove /proc dependancy if needed (not sure yet.)66-77-Please send any patches for this driver to Greg Kroah-Hartman <greg@kroah.com>
-1111
drivers/staging/pcc-acpi/pcc-acpi.c
···11-/*22- * Panasonic HotKey and lcd brightness control Extra driver33- * (C) 2004 Hiroshi Miura <miura@da-cha.org>44- * (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/55- * (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>66- * (C) 2004 David Bronaugh <dbronaugh>77- *88- * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte99- *1010- * This program is free software; you can redistribute it and/or modify1111- * it under the terms of the GNU General Public License version 2 as1212- * publicshed by the Free Software Foundation.1313- *1414- * This program is distributed in the hope that it will be useful,1515- * but WITHOUT ANY WARRANTY; without even the implied warranty of1616- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1717- * GNU General Public License for more details.1818- *1919- * You should have received a copy of the GNU General Public License2020- * along with this program; if not, write to the Free Software2121- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA2222- *2323- *---------------------------------------------------------------------------2424- *2525- * ChangeLog:2626- *2727- * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org>2828- * -v0.9 remove warning about section reference.2929- * remove acpi_os_free3030- * add /proc/acpi/pcc/brightness interface to3131- * allow HAL to access.3232- * merge dbronaugh's enhancement3333- * Aug.17, 2004 David Bronaugh (dbronaugh)3434- * - Added screen brightness setting interface3535- * Thanks to the FreeBSD crew3636- * (acpi_panasonic.c authors)3737- * for the ideas I needed to accomplish it3838- *3939- * May.29, 2006 Hiroshi Miura <miura@da-cha.org>4040- * -v0.8.4 follow to change keyinput structure4141- * thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,4242- * Jacob Bower <jacob.bower@ic.ac.uk> and4343- * Hiroshi Yokota for providing solutions.4444- *4545- * Oct.02, 2004 Hiroshi Miura <miura@da-cha.org>4646- * -v0.8.2 merge code of YOKOTA Hiroshi4747- * <yokota@netlab.is.tsukuba.ac.jp>.4848- * Add sticky key mode interface.4949- * Refactoring acpi_pcc_generete_keyinput().5050- *5151- * Sep.15, 2004 Hiroshi Miura <miura@da-cha.org>5252- * -v0.8 Generate key input event on input subsystem.5353- * This is based on yet another driver5454- * written by Ryuta Nakanishi.5555- *5656- * Sep.10, 2004 Hiroshi Miura <miura@da-cha.org>5757- * -v0.7 Change proc interface functions using seq_file5858- * facility as same as other ACPI drivers.5959- *6060- * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org>6161- * -v0.6.4 Fix a silly error with status checking6262- *6363- * Aug.25, 2004 Hiroshi Miura <miura@da-cha.org>6464- * -v0.6.3 replace read_acpi_int by standard6565- * function acpi_evaluate_integer6666- * some clean up and make smart copyright notice.6767- * fix return value of pcc_acpi_get_key()6868- * fix checking return value of acpi_bus_register_driver()6969- *7070- * Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org>7171- * -v0.6.2 Add check on ACPI data (num_sifr)7272- * Coding style cleanups, better error messages/handling7373- * Fixed an off-by-one error in memory allocation7474- *7575- * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org>7676- * -v0.6.1 Fix a silly error with status checking7777- *7878- * Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org>7979- * - v0.6 Correct brightness controls to reflect reality8080- * based on information gleaned by Hiroshi Miura8181- * and discussions with Hiroshi Miura8282- *8383- * Aug.10, 2004 Hiroshi Miura <miura@da-cha.org>8484- * - v0.5 support LCD brightness control8585- * based on the disclosed information by MEI.8686- *8787- * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org>8888- * - v0.4 first post version8989- * add function to retrive SIFR9090- *9191- * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org>9292- * - v0.3 get proper status of hotkey9393- *9494- * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org>9595- * - v0.2 add HotKey handler9696- *9797- * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org>9898- * - v0.1 start from toshiba_acpi driver written by John Belmonte9999- *100100- */101101-102102-#define ACPI_PCC_VERSION "0.9+hy"103103-104104-#include <linux/kernel.h>105105-#include <linux/module.h>106106-#include <linux/types.h>107107-#include <linux/ctype.h>108108-#include <linux/init.h>109109-#include <linux/input.h>110110-#include <linux/proc_fs.h>111111-#include <linux/seq_file.h>112112-#include <linux/slab.h>113113-#include <linux/uaccess.h>114114-#include <acpi/acpi_bus.h>115115-#include <acpi/acpi_drivers.h>116116-117117-118118-/*************************************************************************119119- * "seq" file template definition.120120- */121121-/* "seq" initializer */122122-#define SEQ_OPEN_FS(_open_func_name_, _show_func_name_) \123123-static int _open_func_name_(struct inode *inode, struct file *file) \124124-{ \125125- return single_open(file, _show_func_name_, PDE(inode)->data); \126126-}127127-128128-/*-------------------------------------------------------------------------129129- * "seq" fops template for read-only files.130130- */131131-#define SEQ_FILEOPS_R(_open_func_name_) \132132-{ \133133- .open = _open_func_name_, \134134- .read = seq_read, \135135- .llseek = seq_lseek, \136136- .release = single_release, \137137-}138138-139139-/*------------------------------------------------------------------------140140- * "seq" fops template for read-write files.141141- */142142-#define SEQ_FILEOPS_RW(_open_func_name_, _write_func_name_) \143143-{ \144144- .open = _open_func_name_ , \145145- .read = seq_read, \146146- .write = _write_func_name_, \147147- .llseek = seq_lseek, \148148- .release = single_release, \149149-}150150-151151-/*152152- * "seq" file template definition ended.153153- ***************************************************************************154154- */155155-#ifndef ACPI_HOTKEY_COMPONENT156156-#define ACPI_HOTKEY_COMPONENT 0x10000000157157-#endif158158-159159-#define _COMPONENT ACPI_HOTKEY_COMPONENT160160-ACPI_MODULE_NAME("pcc_acpi");161161-162162-MODULE_AUTHOR("Hiroshi Miura, Hiroshi Yokota");163163-MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");164164-MODULE_LICENSE("GPL");165165-166166-#define LOGPREFIX "pcc_acpi: "167167-168168-/****************************************************169169- * Define ACPI PATHs170170- ****************************************************/171171-/* Lets note hotkeys */172172-#define METHOD_HKEY_QUERY "HINF"173173-#define METHOD_HKEY_SQTY "SQTY"174174-#define METHOD_HKEY_SINF "SINF"175175-#define METHOD_HKEY_SSET "SSET"176176-#define HKEY_NOTIFY 0x80177177-178178-/* for brightness control */179179-#define LCD_MAX_BRIGHTNESS 255180180-/* This may be magical -- beware */181181-#define LCD_BRIGHTNESS_INCREMENT 17182182-/* Registers of SINF */183183-#define SINF_LCD_BRIGHTNESS 4184184-185185-/*******************************************************************186186- *187187- * definitions for /proc/ interface188188- *189189- *******************************************************************/190190-#define ACPI_PCC_DRIVER_NAME "pcc_acpi"191191-#define ACPI_PCC_DEVICE_NAME "PCCExtra"192192-#define ACPI_PCC_CLASS "pcc"193193-#define PROC_PCC ACPI_PCC_CLASS194194-195195-#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"196196-197197-/* This is transitional definition */198198-#ifndef KEY_BATT199199-# define KEY_BATT 227200200-#endif201201-202202-#define PROC_STR_MAX_LEN 8203203-204204-#define BUS_PCC_HOTKEY BUS_I8042 /*0x1a*/ /* FIXME: BUS_I8042? */205205-206206-/* Fn+F4/F5 confricts with Shift+F1/F2 */207207-/* This hack avoids key number confrict */208208-#define PCC_KEYINPUT_MODE (0)209209-210210-/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent211211- ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00212212-*/213213-enum SINF_BITS { SINF_NUM_BATTERIES = 0,214214- SINF_LCD_TYPE,215215- SINF_AC_MAX_BRIGHT,216216- SINF_AC_MIN_BRIGHT,217217- SINF_AC_CUR_BRIGHT,218218- /* 4 = R1 only handle SINF_AC_CUR_BRIGHT219219- * as SINF_CUR_BRIGHT and don't know AC state */220220- SINF_DC_MAX_BRIGHT,221221- SINF_DC_MIN_BRIGHT,222222- SINF_DC_CUR_BRIGHT,223223- SINF_MUTE,224224- SINF_RESERVED,225225- SINF_ENV_STATE, /* 10 */226226- SINF_STICKY_KEY = 0x80,227227-};228228-229229-static struct acpi_device_id pcc_device_ids[] = {230230- {"MAT0012", 0},231231- {"MAT0013", 0},232232- {"MAT0018", 0},233233- {"MAT0019", 0},234234- {"", 0},235235-};236236-MODULE_DEVICE_TABLE(acpi, pcc_device_ids);237237-238238-239239-static int __devinit acpi_pcc_hotkey_add(struct acpi_device *device);240240-static int __devexit acpi_pcc_hotkey_remove(struct acpi_device *device,241241- int type);242242-static int acpi_pcc_hotkey_resume(struct acpi_device *device);243243-244244-245245-static struct acpi_driver acpi_pcc_driver = {246246- .name = ACPI_PCC_DRIVER_NAME,247247- .class = ACPI_PCC_CLASS,248248- .ids = pcc_device_ids,249249- .ops = {250250- .add = acpi_pcc_hotkey_add,251251- .remove = __devexit_p(acpi_pcc_hotkey_remove),252252-#ifdef CONFIG_PM253253- /*.suspend = acpi_pcc_hotkey_suspend,*/254254- .resume = acpi_pcc_hotkey_resume,255255-#endif256256- },257257-};258258-259259-struct acpi_hotkey {260260- acpi_handle handle;261261- struct acpi_device *device;262262- struct proc_dir_entry *proc_dir_entry;263263- unsigned long num_sifr;264264- unsigned long status;265265- struct input_dev *input_dev;266266- int sticky_mode;267267-};268268-269269-struct pcc_keyinput {270270- struct acpi_hotkey *hotkey;271271- int key_mode;272272-};273273-274274-/* *************************************************************************275275- Hotkey driver core276276- ************************************************************************* */277277-/* -------------------------------------------------------------------------278278- method access functions279279- ------------------------------------------------------------------------- */280280-static int acpi_pcc_write_sset(struct acpi_hotkey *hotkey, int func, int val)281281-{282282- union acpi_object in_objs[] = {283283- { .integer.type = ACPI_TYPE_INTEGER,284284- .integer.value = func, },285285- { .integer.type = ACPI_TYPE_INTEGER,286286- .integer.value = val, },287287- };288288- struct acpi_object_list params = {289289- .count = ARRAY_SIZE(in_objs),290290- .pointer = in_objs,291291- };292292- acpi_status status;293293-294294- ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");295295-296296- status = acpi_evaluate_object(hotkey->handle, METHOD_HKEY_SSET,297297- ¶ms, NULL);298298-299299- return_VALUE(status == AE_OK ? AE_OK : AE_ERROR);300300-}301301-302302-static inline int acpi_pcc_get_sqty(struct acpi_device *device)303303-{304304- unsigned long s;305305- acpi_status status;306306-307307- ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");308308-309309- status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,310310- NULL, &s);311311- if (ACPI_SUCCESS(status)) {312312- return_VALUE(s);313313- } else {314314- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,315315- "evaluation error HKEY.SQTY\n"));316316- return_VALUE(-EINVAL);317317- }318318-}319319-320320-static int acpi_pcc_retrieve_biosdata(struct acpi_hotkey *hotkey, u32 *sinf)321321-{322322- acpi_status status;323323- struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};324324- union acpi_object *hkey = NULL;325325- int i;326326-327327- ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");328328-329329- status = acpi_evaluate_object(hotkey->handle, METHOD_HKEY_SINF, 0,330330- &buffer);331331- if (ACPI_FAILURE(status)) {332332- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,333333- "evaluation error HKEY.SINF\n"));334334- status = AE_ERROR;335335- return_VALUE(status);336336- }337337-338338- hkey = buffer.pointer;339339- if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {340340- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));341341- goto free_buffer;342342- }343343-344344- if (hotkey->num_sifr < hkey->package.count) {345345- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,346346- "SQTY reports bad SINF length\n"));347347- status = AE_ERROR;348348- goto free_buffer;349349- }350350-351351- for (i = 0; i < hkey->package.count; i++) {352352- union acpi_object *element = &(hkey->package.elements[i]);353353- if (likely(element->type == ACPI_TYPE_INTEGER)) {354354- sinf[i] = element->integer.value;355355- } else {356356- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,357357- "Invalid HKEY.SINF data\n"));358358- status = AE_ERROR;359359- break;360360- }361361- }362362- sinf[hkey->package.count] = -1;363363-364364- free_buffer:365365- kfree(buffer.pointer);366366- return_VALUE(status == AE_OK ? AE_OK : AE_ERROR);367367-}368368-369369-static int acpi_pcc_read_sinf_field(struct seq_file *seq, int field)370370-{371371- struct acpi_hotkey *hotkey = (struct acpi_hotkey *) seq->private;372372- u32 sinf[hotkey->num_sifr + 1];373373-374374- ACPI_FUNCTION_TRACE("acpi_pcc_read_sinf_field");375375-376376- if (ACPI_SUCCESS(acpi_pcc_retrieve_biosdata(hotkey, sinf)))377377- seq_printf(seq, "%u\n", sinf[field]);378378- else379379- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,380380- "Couldn't retrieve BIOS data\n"));381381-382382- return_VALUE(AE_OK);383383-}384384-385385-/* -------------------------------------------------------------------------386386- user interface functions387387- ------------------------------------------------------------------------- */388388-/* read methods */389389-/* Sinf read methods */390390-#define PCC_SINF_READ_F(_name_, FUNC) \391391-static int _name_(struct seq_file *seq, void *offset) \392392-{ \393393- return_VALUE(ACPI_SUCCESS(acpi_pcc_read_sinf_field(seq, \394394- (FUNC))) \395395- ? 0 : -EINVAL); \396396-}397397-398398-PCC_SINF_READ_F(acpi_pcc_numbatteries_show, SINF_NUM_BATTERIES);399399-PCC_SINF_READ_F(acpi_pcc_lcdtype_show, SINF_LCD_TYPE);400400-PCC_SINF_READ_F(acpi_pcc_ac_brightness_max_show, SINF_AC_MAX_BRIGHT);401401-PCC_SINF_READ_F(acpi_pcc_ac_brightness_min_show, SINF_AC_MIN_BRIGHT);402402-PCC_SINF_READ_F(acpi_pcc_ac_brightness_show, SINF_AC_CUR_BRIGHT);403403-PCC_SINF_READ_F(acpi_pcc_dc_brightness_max_show, SINF_DC_MAX_BRIGHT);404404-PCC_SINF_READ_F(acpi_pcc_dc_brightness_min_show, SINF_DC_MIN_BRIGHT);405405-PCC_SINF_READ_F(acpi_pcc_dc_brightness_show, SINF_DC_CUR_BRIGHT);406406-PCC_SINF_READ_F(acpi_pcc_brightness_show, SINF_AC_CUR_BRIGHT);407407-PCC_SINF_READ_F(acpi_pcc_mute_show, SINF_MUTE);408408-409409-static int acpi_pcc_sticky_key_show(struct seq_file *seq, void *offset)410410-{411411- struct acpi_hotkey *hotkey = seq->private;412412-413413- ACPI_FUNCTION_TRACE("acpi_pcc_sticky_key_show");414414-415415- if (!hotkey || !hotkey->device)416416- return_VALUE(-EINVAL);417417-418418- seq_printf(seq, "%d\n", hotkey->sticky_mode);419419-420420- return_VALUE(0);421421-}422422-423423-static int acpi_pcc_keyinput_show(struct seq_file *seq, void *offset)424424-{425425- struct acpi_hotkey *hotkey = seq->private;426426- struct input_dev *hotk_input_dev = hotkey->input_dev;427427- struct pcc_keyinput *keyinput = input_get_drvdata(hotk_input_dev);428428-429429- ACPI_FUNCTION_TRACE("acpi_pcc_keyinput_show");430430-431431- seq_printf(seq, "%d\n", keyinput->key_mode);432432-433433- return_VALUE(0);434434-}435435-436436-static int acpi_pcc_version_show(struct seq_file *seq, void *offset)437437-{438438- struct acpi_hotkey *hotkey = seq->private;439439-440440- ACPI_FUNCTION_TRACE("acpi_pcc_version_show");441441-442442- if (!hotkey || !hotkey->device)443443- return_VALUE(-EINVAL);444444-445445- seq_printf(seq, "%s version %s\n", ACPI_PCC_DRIVER_NAME,446446- ACPI_PCC_VERSION);447447- seq_printf(seq, "%li functions\n", hotkey->num_sifr);448448-449449- return_VALUE(0);450450-}451451-452452-/* write methods */453453-static ssize_t acpi_pcc_write_single_flag(struct file *file,454454- const char __user *buffer,455455- size_t count,456456- int sinf_func)457457-{458458- struct seq_file *seq = file->private_data;459459- struct acpi_hotkey *hotkey = seq->private;460460- char write_string[PROC_STR_MAX_LEN];461461- u32 val;462462-463463- ACPI_FUNCTION_TRACE("acpi_pcc_write_single_flag");464464-465465- if (!hotkey || (count > sizeof(write_string) - 1))466466- return_VALUE(-EINVAL);467467-468468- if (copy_from_user(write_string, buffer, count))469469- return_VALUE(-EFAULT);470470-471471- write_string[count] = '\0';472472-473473- if ((sscanf(write_string, "%3i", &val) == 1) &&474474- (val == 0 || val == 1))475475- acpi_pcc_write_sset(hotkey, sinf_func, val);476476-477477- return_VALUE(count);478478-}479479-480480-static unsigned long acpi_pcc_write_brightness(struct file *file,481481- const char __user *buffer,482482- size_t count,483483- int min_index, int max_index,484484- int cur_index)485485-{486486- struct seq_file *seq = (struct seq_file *)file->private_data;487487- struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private;488488- char write_string[PROC_STR_MAX_LEN];489489- u32 bright;490490- u32 sinf[hotkey->num_sifr + 1];491491-492492- ACPI_FUNCTION_TRACE("acpi_pcc_write_brightness");493493-494494- if (!hotkey || (count > sizeof(write_string) - 1))495495- return_VALUE(-EINVAL);496496-497497- if (copy_from_user(write_string, buffer, count))498498- return_VALUE(-EFAULT);499499-500500- write_string[count] = '\0';501501-502502- if (ACPI_FAILURE(acpi_pcc_retrieve_biosdata(hotkey, sinf))) {503503- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,504504- "Couldn't retrieve BIOS data\n"));505505- goto end;506506- }507507-508508- if ((sscanf(write_string, "%4i", &bright) == 1) &&509509- (bright >= sinf[min_index]) &&510510- (bright <= sinf[max_index]))511511- acpi_pcc_write_sset(hotkey, cur_index, bright);512512-513513-end:514514- return_VALUE(count);515515-}516516-517517-static ssize_t acpi_pcc_write_ac_brightness(struct file *file,518518- const char __user *buffer,519519- size_t count, loff_t *ppos)520520-{521521- return_VALUE(acpi_pcc_write_brightness(file, buffer, count,522522- SINF_AC_MIN_BRIGHT,523523- SINF_AC_MAX_BRIGHT,524524- SINF_AC_CUR_BRIGHT));525525-}526526-527527-static ssize_t acpi_pcc_write_dc_brightness(struct file *file,528528- const char __user *buffer,529529- size_t count, loff_t *ppos)530530-{531531- return_VALUE(acpi_pcc_write_brightness(file, buffer, count,532532- SINF_DC_MIN_BRIGHT,533533- SINF_DC_MAX_BRIGHT,534534- SINF_DC_CUR_BRIGHT));535535-}536536-537537-static ssize_t acpi_pcc_write_no_brightness(struct file *file,538538- const char __user *buffer,539539- size_t count, loff_t *ppos)540540-{541541- return acpi_pcc_write_brightness(file, buffer, count,542542- SINF_AC_MIN_BRIGHT,543543- SINF_AC_MAX_BRIGHT,544544- SINF_AC_CUR_BRIGHT);545545-}546546-547547-static ssize_t acpi_pcc_write_mute(struct file *file,548548- const char __user *buffer,549549- size_t count, loff_t *ppos)550550-{551551- return_VALUE(acpi_pcc_write_single_flag(file, buffer, count,552552- SINF_MUTE));553553-}554554-555555-static ssize_t acpi_pcc_write_sticky_key(struct file *file,556556- const char __user *buffer,557557- size_t count, loff_t *ppos)558558-{559559- struct seq_file *seq = (struct seq_file *)file->private_data;560560- struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private;561561- char write_string[PROC_STR_MAX_LEN];562562- int mode;563563-564564- ACPI_FUNCTION_TRACE("acpi_pcc_write_sticky_key");565565-566566- if (!hotkey || (count > sizeof(write_string) - 1))567567- return_VALUE(-EINVAL);568568-569569- if (copy_from_user(write_string, buffer, count))570570- return_VALUE(-EFAULT);571571-572572- write_string[count] = '\0';573573-574574- if ((sscanf(write_string, "%3i", &mode) == 1) &&575575- (mode == 0 || mode == 1)) {576576- acpi_pcc_write_sset(hotkey, SINF_STICKY_KEY, mode);577577- hotkey->sticky_mode = mode;578578- }579579-580580- return_VALUE(count);581581-}582582-583583-static ssize_t acpi_pcc_write_keyinput(struct file *file,584584- const char __user *buffer,585585- size_t count, loff_t *ppos)586586-{587587- struct seq_file *seq = (struct seq_file *)file->private_data;588588- struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private;589589- struct pcc_keyinput *keyinput;590590- char write_string[PROC_STR_MAX_LEN];591591- int key_mode;592592-593593- ACPI_FUNCTION_TRACE("acpi_pcc_write_keyinput");594594-595595- if (!hotkey || (count > (sizeof(write_string) - 1)))596596- return_VALUE(-EINVAL);597597-598598- if (copy_from_user(write_string, buffer, count))599599- return_VALUE(-EFAULT);600600-601601- write_string[count] = '\0';602602-603603- if ((sscanf(write_string, "%4i", &key_mode) == 1) &&604604- (key_mode == 0 || key_mode == 1)) {605605- keyinput = input_get_drvdata(hotkey->input_dev);606606- keyinput->key_mode = key_mode;607607- }608608-609609- return_VALUE(count);610610-}611611-612612-/* -------------------------------------------------------------------------613613- hotkey driver614614- ------------------------------------------------------------------------- */615615-static void acpi_pcc_generete_keyinput(struct acpi_hotkey *hotkey)616616-{617617- struct input_dev *hotk_input_dev = hotkey->input_dev;618618- struct pcc_keyinput *keyinput = input_get_drvdata(hotk_input_dev);619619- int hinf = hotkey->status;620620- int key_code, hkey_num;621621- const int key_map[] = {622622- /* 0 */ -1,623623- /* 1 */ KEY_BRIGHTNESSDOWN,624624- /* 2 */ KEY_BRIGHTNESSUP,625625- /* 3 */ -1, /* vga/lcd switch event is not occur on626626- hotkey driver. */627627- /* 4 */ KEY_MUTE,628628- /* 5 */ KEY_VOLUMEDOWN,629629- /* 6 */ KEY_VOLUMEUP,630630- /* 7 */ KEY_SLEEP,631631- /* 8 */ -1, /* Change CPU boost: do nothing */632632- /* 9 */ KEY_BATT,633633- /* 10 */ KEY_SUSPEND,634634- };635635-636636- ACPI_FUNCTION_TRACE("acpi_pcc_generete_keyinput");637637-638638- if (keyinput->key_mode == 0)639639- return_VOID;640640-641641- hkey_num = hinf & 0xf;642642-643643- if ((0 > hkey_num) ||644644- (hkey_num > ARRAY_SIZE(key_map))) {645645- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,646646- "hotkey number out of range: %d\n",647647- hkey_num));648648- return_VOID;649649- }650650-651651- key_code = key_map[hkey_num];652652-653653- if (key_code != -1) {654654- int pushed = (hinf & 0x80) ? TRUE : FALSE;655655-656656- input_report_key(hotk_input_dev, key_code, pushed);657657- input_sync(hotk_input_dev);658658- }659659-}660660-661661-static int acpi_pcc_hotkey_get_key(struct acpi_hotkey *hotkey)662662-{663663- unsigned long result;664664- acpi_status status = AE_OK;665665-666666- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_get_key");667667-668668- status = acpi_evaluate_integer(hotkey->handle, METHOD_HKEY_QUERY,669669- NULL, &result);670670- if (likely(ACPI_SUCCESS(status)))671671- hotkey->status = result;672672- else673673- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,674674- "error getting hotkey status\n"));675675-676676- return_VALUE(status == AE_OK);677677-}678678-679679-void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)680680-{681681- struct acpi_hotkey *hotkey = (struct acpi_hotkey *) data;682682-683683- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");684684-685685- switch (event) {686686- case HKEY_NOTIFY:687687- if (acpi_pcc_hotkey_get_key(hotkey)) {688688- /* generate event like '"pcc HKEY 00000080 00000084"'689689- * when Fn+F4 pressed */690690- acpi_bus_generate_proc_event(hotkey->device, event,691691- hotkey->status);692692- }693693- acpi_pcc_generete_keyinput(hotkey);694694- break;695695- default:696696- /* nothing to do */697697- break;698698- }699699- return_VOID;700700-}701701-702702-/* *************************************************************************703703- FS Interface (/proc)704704- ************************************************************************* */705705-/* oepn proc file fs*/706706-SEQ_OPEN_FS(acpi_pcc_dc_brightness_open_fs, acpi_pcc_dc_brightness_show);707707-SEQ_OPEN_FS(acpi_pcc_numbatteries_open_fs, acpi_pcc_numbatteries_show);708708-SEQ_OPEN_FS(acpi_pcc_lcdtype_open_fs, acpi_pcc_lcdtype_show);709709-SEQ_OPEN_FS(acpi_pcc_ac_brightness_max_open_fs,710710- acpi_pcc_ac_brightness_max_show);711711-SEQ_OPEN_FS(acpi_pcc_ac_brightness_min_open_fs,712712- acpi_pcc_ac_brightness_min_show);713713-SEQ_OPEN_FS(acpi_pcc_ac_brightness_open_fs, acpi_pcc_ac_brightness_show);714714-SEQ_OPEN_FS(acpi_pcc_dc_brightness_max_open_fs,715715- acpi_pcc_dc_brightness_max_show);716716-SEQ_OPEN_FS(acpi_pcc_dc_brightness_min_open_fs,717717- acpi_pcc_dc_brightness_min_show);718718-SEQ_OPEN_FS(acpi_pcc_brightness_open_fs, acpi_pcc_brightness_show);719719-SEQ_OPEN_FS(acpi_pcc_mute_open_fs, acpi_pcc_mute_show);720720-SEQ_OPEN_FS(acpi_pcc_version_open_fs, acpi_pcc_version_show);721721-SEQ_OPEN_FS(acpi_pcc_keyinput_open_fs, acpi_pcc_keyinput_show);722722-SEQ_OPEN_FS(acpi_pcc_sticky_key_open_fs, acpi_pcc_sticky_key_show);723723-724724-static struct file_operations acpi_pcc_numbatteries_fops =725725- SEQ_FILEOPS_R(acpi_pcc_numbatteries_open_fs);726726-static struct file_operations acpi_pcc_lcdtype_fops =727727- SEQ_FILEOPS_R(acpi_pcc_lcdtype_open_fs);728728-static struct file_operations acpi_pcc_mute_fops =729729- SEQ_FILEOPS_RW(acpi_pcc_mute_open_fs, acpi_pcc_write_mute);730730-static struct file_operations acpi_pcc_ac_brightness_fops =731731- SEQ_FILEOPS_RW(acpi_pcc_ac_brightness_open_fs,732732- acpi_pcc_write_ac_brightness);733733-static struct file_operations acpi_pcc_ac_brightness_max_fops =734734- SEQ_FILEOPS_R(acpi_pcc_ac_brightness_max_open_fs);735735-static struct file_operations acpi_pcc_ac_brightness_min_fops =736736- SEQ_FILEOPS_R(acpi_pcc_ac_brightness_min_open_fs);737737-static struct file_operations acpi_pcc_dc_brightness_fops =738738- SEQ_FILEOPS_RW(acpi_pcc_dc_brightness_open_fs,739739- acpi_pcc_write_dc_brightness);740740-static struct file_operations acpi_pcc_dc_brightness_max_fops =741741- SEQ_FILEOPS_R(acpi_pcc_dc_brightness_max_open_fs);742742-static struct file_operations acpi_pcc_dc_brightness_min_fops =743743- SEQ_FILEOPS_R(acpi_pcc_dc_brightness_min_open_fs);744744-static struct file_operations acpi_pcc_brightness_fops =745745- SEQ_FILEOPS_RW(acpi_pcc_brightness_open_fs,746746- acpi_pcc_write_no_brightness);747747-static struct file_operations acpi_pcc_sticky_key_fops =748748- SEQ_FILEOPS_RW(acpi_pcc_sticky_key_open_fs, acpi_pcc_write_sticky_key);749749-static struct file_operations acpi_pcc_keyinput_fops =750750- SEQ_FILEOPS_RW(acpi_pcc_keyinput_open_fs, acpi_pcc_write_keyinput);751751-static struct file_operations acpi_pcc_version_fops =752752- SEQ_FILEOPS_R(acpi_pcc_version_open_fs);753753-754754-struct proc_item {755755- const char *name;756756- struct file_operations *fops;757757- mode_t flag;758758-};759759-760760-/* Note: These functions map *exactly* to the SINF/SSET functions */761761-struct proc_item acpi_pcc_proc_items_sifr[] = {762762- { "num_batteries", &acpi_pcc_numbatteries_fops, S_IRUGO },763763- { "lcd_type", &acpi_pcc_lcdtype_fops, S_IRUGO },764764- { "ac_brightness_max", &acpi_pcc_ac_brightness_max_fops, S_IRUGO },765765- { "ac_brightness_min", &acpi_pcc_ac_brightness_min_fops, S_IRUGO },766766- { "ac_brightness", &acpi_pcc_ac_brightness_fops,767767- S_IFREG | S_IRUGO | S_IWUSR },768768- { "dc_brightness_max", &acpi_pcc_dc_brightness_max_fops, S_IRUGO },769769- { "dc_brightness_min", &acpi_pcc_dc_brightness_min_fops, S_IRUGO },770770- { "dc_brightness", &acpi_pcc_dc_brightness_fops,771771- S_IFREG | S_IRUGO | S_IWUSR },772772- { "brightness", &acpi_pcc_brightness_fops, S_IFREG | S_IRUGO | S_IWUSR},773773- { "mute", &acpi_pcc_mute_fops, S_IFREG | S_IRUGO | S_IWUSR },774774- { NULL, NULL, 0 },775775-};776776-777777-struct proc_item acpi_pcc_proc_items[] = {778778- { "sticky_key", &acpi_pcc_sticky_key_fops, S_IFREG | S_IRUGO | S_IWUSR},779779- { "keyinput", &acpi_pcc_keyinput_fops, S_IFREG | S_IRUGO | S_IWUSR },780780- { "version", &acpi_pcc_version_fops, S_IRUGO },781781- { NULL, NULL, 0 },782782-};783783-784784-static int __devinit acpi_pcc_add_device(struct acpi_device *device,785785- struct proc_item *proc_items,786786- int num)787787-{788788- struct acpi_hotkey *hotkey = acpi_driver_data(device);789789- struct proc_dir_entry *proc;790790- struct proc_item *item;791791- int i;792792-793793- for (item = proc_items, i = 0; item->name && i < num; ++item, ++i) {794794- proc = create_proc_entry(item->name, item->flag,795795- hotkey->proc_dir_entry);796796- if (likely(proc)) {797797- proc->proc_fops = item->fops;798798- proc->data = hotkey;799799- proc->owner = THIS_MODULE;800800- } else {801801- while (i-- > 0) {802802- item--;803803- remove_proc_entry(item->name,804804- hotkey->proc_dir_entry);805805- }806806- return_VALUE(-ENODEV);807807- }808808- }809809- return_VALUE(0);810810-}811811-812812-static int __devinit acpi_pcc_proc_init(struct acpi_device *device)813813-{814814- struct proc_dir_entry *acpi_pcc_dir;815815- struct acpi_hotkey *hotkey = acpi_driver_data(device);816816- acpi_status status;817817-818818- ACPI_FUNCTION_TRACE("acpi_pcc_proc_init");819819-820820- acpi_pcc_dir = proc_mkdir(PROC_PCC, acpi_root_dir);821821-822822- if (unlikely(!acpi_pcc_dir)) {823823- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,824824- "Couldn't create dir in /proc\n"));825825- return_VALUE(-ENODEV);826826- }827827-828828- acpi_pcc_dir->owner = THIS_MODULE;829829- hotkey->proc_dir_entry = acpi_pcc_dir;830830-831831- status = acpi_pcc_add_device(device, acpi_pcc_proc_items_sifr,832832- hotkey->num_sifr);833833- status |= acpi_pcc_add_device(device, acpi_pcc_proc_items,834834- ARRAY_SIZE(acpi_pcc_proc_items));835835- if (unlikely(status)) {836836- remove_proc_entry(PROC_PCC, acpi_root_dir);837837- hotkey->proc_dir_entry = NULL;838838- return_VALUE(-ENODEV);839839- }840840-841841- return_VALUE(status);842842-}843843-844844-static void __devexit acpi_pcc_remove_device(struct acpi_device *device,845845- struct proc_item *proc_items,846846- int num)847847-{848848- struct acpi_hotkey *hotkey = acpi_driver_data(device);849849- struct proc_item *item;850850- int i;851851-852852- for (item = proc_items, i = 0;853853- item->name != NULL && i < num;854854- ++item, ++i) {855855- remove_proc_entry(item->name, hotkey->proc_dir_entry);856856- }857857-858858- return_VOID;859859-}860860-861861-/* *************************************************************************862862- Power Management863863- ************************************************************************* */864864-#ifdef CONFIG_PM865865-static int acpi_pcc_hotkey_resume(struct acpi_device *device)866866-{867867- struct acpi_hotkey *hotkey = acpi_driver_data(device);868868- acpi_status status = AE_OK;869869-870870- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");871871-872872- if (device == NULL || hotkey == NULL)873873- return_VALUE(-EINVAL);874874-875875- if (hotkey->num_sifr != 0) {876876- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Sticky mode restore: %d\n",877877- hotkey->sticky_mode));878878-879879- status = acpi_pcc_write_sset(hotkey, SINF_STICKY_KEY,880880- hotkey->sticky_mode);881881- }882882- if (status != AE_OK)883883- return_VALUE(-EINVAL);884884-885885- return_VALUE(0);886886-}887887-#endif888888-889889-/* *************************************************************************890890- Module init/remove891891- ************************************************************************* */892892-/* -------------------------------------------------------------------------893893- input894894- ------------------------------------------------------------------------- */895895-static int __devinit acpi_pcc_init_input(struct acpi_hotkey *hotkey)896896-{897897- struct input_dev *hotk_input_dev;898898- struct pcc_keyinput *pcc_keyinput;899899- int error;900900-901901- ACPI_FUNCTION_TRACE("acpi_pcc_init_input");902902-903903- hotk_input_dev = input_allocate_device();904904- if (hotk_input_dev == NULL) {905905- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,906906- "Couldn't allocate input device for hotkey"));907907- goto err_input;908908- }909909-910910- pcc_keyinput = kcalloc(1, sizeof(struct pcc_keyinput), GFP_KERNEL);911911-912912- if (pcc_keyinput == NULL) {913913- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,914914- "Couldn't allocate mem for private data"));915915- goto err_pcc;916916- }917917-918918- hotk_input_dev->evbit[0] = BIT(EV_KEY);919919-920920- set_bit(KEY_BRIGHTNESSDOWN, hotk_input_dev->keybit);921921- set_bit(KEY_BRIGHTNESSUP, hotk_input_dev->keybit);922922- set_bit(KEY_MUTE, hotk_input_dev->keybit);923923- set_bit(KEY_VOLUMEDOWN, hotk_input_dev->keybit);924924- set_bit(KEY_VOLUMEUP, hotk_input_dev->keybit);925925- set_bit(KEY_SLEEP, hotk_input_dev->keybit);926926- set_bit(KEY_BATT, hotk_input_dev->keybit);927927- set_bit(KEY_SUSPEND, hotk_input_dev->keybit);928928-929929- hotk_input_dev->name = ACPI_PCC_DRIVER_NAME;930930- hotk_input_dev->phys = ACPI_PCC_INPUT_PHYS;931931- hotk_input_dev->id.bustype = BUS_PCC_HOTKEY;932932- hotk_input_dev->id.vendor = 0x0001;933933- hotk_input_dev->id.product = 0x0001;934934- hotk_input_dev->id.version = 0x0100;935935-936936- pcc_keyinput->key_mode = PCC_KEYINPUT_MODE;937937- pcc_keyinput->hotkey = hotkey;938938-939939- input_set_drvdata(hotk_input_dev, pcc_keyinput);940940-941941- hotkey->input_dev = hotk_input_dev;942942-943943- error = input_register_device(hotk_input_dev);944944-945945- if (error)946946- goto err_pcc;947947-948948- return_VALUE(0);949949-950950- err_pcc:951951- input_unregister_device(hotk_input_dev);952952- err_input:953953- return_VALUE(-ENOMEM);954954-}955955-956956-static void __devexit acpi_pcc_remove_input(struct acpi_hotkey *hotkey)957957-{958958- struct input_dev *hotk_input_dev;959959- struct pcc_keyinput *pcc_keyinput;960960-961961- ACPI_FUNCTION_TRACE("acpi_pcc_remove_input");962962-963963- if (hotkey == NULL) {964964- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Can't free memory"));965965- return_VOID;966966- }967967-968968- hotk_input_dev = hotkey->input_dev;969969- pcc_keyinput = input_get_drvdata(hotk_input_dev);970970-971971- input_unregister_device(hotk_input_dev);972972-973973- kfree(pcc_keyinput);974974-}975975-976976-/* -------------------------------------------------------------------------977977- ACPI978978- ------------------------------------------------------------------------- */979979-static int __devinit acpi_pcc_hotkey_add(struct acpi_device *device)980980-{981981- acpi_status status = AE_OK;982982- struct acpi_hotkey *hotkey = NULL;983983- int sifr_status, num_sifr, result;984984-985985- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");986986-987987- if (device == NULL)988988- return_VALUE(-EINVAL);989989-990990- sifr_status = acpi_pcc_get_sqty(device);991991-992992- if (sifr_status > 255) {993993- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));994994- return_VALUE(-ENODEV);995995- }996996-997997- if (sifr_status < 0) {998998- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "not support SQTY"));999999- num_sifr = 0;10001000- } else {10011001- num_sifr = sifr_status;10021002- }10031003-10041004- hotkey = kcalloc(1, sizeof(struct acpi_hotkey), GFP_KERNEL);10051005- if (hotkey == NULL) {10061006- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,10071007- "Couldn't allocate mem for hotkey"));10081008- return_VALUE(-ENOMEM);10091009- }10101010-10111011- hotkey->device = device;10121012- hotkey->handle = device->handle;10131013- hotkey->num_sifr = num_sifr;10141014- device->driver_data = hotkey;10151015- strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);10161016- strcpy(acpi_device_class(device), ACPI_PCC_CLASS);10171017-10181018- status = acpi_install_notify_handler(hotkey->handle,10191019- ACPI_DEVICE_NOTIFY,10201020- acpi_pcc_hotkey_notify,10211021- hotkey);10221022-10231023- if (ACPI_FAILURE(status)) {10241024- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,10251025- "Error installing notify handler\n"));10261026- kfree(hotkey);10271027- return_VALUE(-ENODEV);10281028- }10291029-10301030- result = acpi_pcc_init_input(hotkey);10311031- if (result != 0) {10321032- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,10331033- "Error installing keyinput handler\n"));10341034- kfree(hotkey);10351035- return_VALUE(result);10361036- }10371037-10381038- return_VALUE(acpi_pcc_proc_init(device));10391039-}10401040-10411041-static int __devexit acpi_pcc_hotkey_remove(struct acpi_device *device,10421042- int type)10431043-{10441044- acpi_status status = AE_OK;10451045- struct acpi_hotkey *hotkey = acpi_driver_data(device);10461046-10471047- ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");10481048-10491049- if (!device || !hotkey)10501050- return_VALUE(-EINVAL);10511051-10521052- if (hotkey->proc_dir_entry) {10531053- acpi_pcc_remove_device(device, acpi_pcc_proc_items_sifr,10541054- hotkey->num_sifr);10551055- acpi_pcc_remove_device(device, acpi_pcc_proc_items,10561056- ARRAY_SIZE(acpi_pcc_proc_items));10571057- remove_proc_entry(PROC_PCC, acpi_root_dir);10581058- }10591059-10601060- status = acpi_remove_notify_handler(hotkey->handle,10611061- ACPI_DEVICE_NOTIFY, acpi_pcc_hotkey_notify);10621062-10631063- if (ACPI_FAILURE(status)) {10641064- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,10651065- "Error removing notify handler\n"));10661066- }10671067-10681068- acpi_pcc_remove_input(hotkey);10691069- kfree(hotkey);10701070- return_VALUE(status == AE_OK);10711071-}10721072-10731073-/* *********************************************************************10741074- Module entry point10751075- ********************************************************************* */10761076-static int __init acpi_pcc_init(void)10771077-{10781078- int result;10791079-10801080- ACPI_FUNCTION_TRACE("acpi_pcc_init");10811081-10821082- printk(KERN_INFO LOGPREFIX "loading...\n");10831083-10841084- if (acpi_disabled) {10851085- printk(KERN_INFO LOGPREFIX "ACPI disabled.\n");10861086- return_VALUE(-ENODEV);10871087- }10881088-10891089- result = acpi_bus_register_driver(&acpi_pcc_driver);10901090- if (result < 0) {10911091- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,10921092- "Error registering hotkey driver\n"));10931093- return_VALUE(-ENODEV);10941094- }10951095-10961096- return_VALUE(result);10971097-}10981098-10991099-static void __exit acpi_pcc_exit(void)11001100-{11011101- ACPI_FUNCTION_TRACE("acpi_pcc_exit");11021102-11031103- printk(KERN_INFO LOGPREFIX "unloading...\n");11041104-11051105- acpi_bus_unregister_driver(&acpi_pcc_driver);11061106-11071107- return_VOID;11081108-}11091109-11101110-module_init(acpi_pcc_init);11111111-module_exit(acpi_pcc_exit);