Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
f1e6d317
Commit
f1e6d317
authored
Oct 22, 2008
by
Len Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'panasonic' into test
parents
1b79b27d
7ba427c2
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
784 additions
and
0 deletions
+784
-0
MAINTAINERS
MAINTAINERS
+5
-0
drivers/misc/Kconfig
drivers/misc/Kconfig
+11
-0
drivers/misc/Makefile
drivers/misc/Makefile
+1
-0
drivers/misc/panasonic-laptop.c
drivers/misc/panasonic-laptop.c
+767
-0
No files found.
MAINTAINERS
View file @
f1e6d317
...
...
@@ -3153,6 +3153,11 @@ M: olof@lixom.net
L: i2c@lm-sensors.org
S: Maintained
PANASONIC LAPTOP ACPI EXTRAS DRIVER
P: Harald Welte
M: laforge@gnumonks.org
S: Maintained
PARALLEL PORT SUPPORT
L: linux-parport@lists.infradead.org (subscribers-only)
S: Orphan
...
...
drivers/misc/Kconfig
View file @
f1e6d317
...
...
@@ -246,6 +246,17 @@ config MSI_LAPTOP
If you have an MSI S270 laptop, say Y or M here.
config PANASONIC_LAPTOP
tristate "Panasonic Laptop Extras"
depends on X86 && INPUT && ACPI
depends on BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to backlight control and hotkeys
on Panasonic Let's Note laptops.
If you have a Panasonic Let's note laptop (such as the R1(N variant),
R2, R3, R5, T2, W2 and Y2 series), say Y.
config COMPAL_LAPTOP
tristate "Compal Laptop Extras"
depends on X86
...
...
drivers/misc/Makefile
View file @
f1e6d317
...
...
@@ -23,6 +23,7 @@ obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_SONY_LAPTOP)
+=
sony-laptop.o
obj-$(CONFIG_THINKPAD_ACPI)
+=
thinkpad_acpi.o
obj-$(CONFIG_FUJITSU_LAPTOP)
+=
fujitsu-laptop.o
obj-$(CONFIG_PANASONIC_LAPTOP)
+=
panasonic-laptop.o
obj-$(CONFIG_EEPROM_93CX6)
+=
eeprom_93cx6.o
obj-$(CONFIG_INTEL_MENLOW)
+=
intel_menlow.o
obj-$(CONFIG_ENCLOSURE_SERVICES)
+=
enclosure.o
...
...
drivers/misc/panasonic-laptop.c
0 → 100644
View file @
f1e6d317
/*
* Panasonic HotKey and LCD brightness control driver
* (C) 2004 Hiroshi Miura <miura@da-cha.org>
* (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
* (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
* (C) 2004 David Bronaugh <dbronaugh>
* (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
*
* derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* publicshed by the Free Software Foundation.
*
* 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
*
*---------------------------------------------------------------------------
*
* ChangeLog:
* Sep.23, 2008 Harald Welte <laforge@gnumonks.org>
* -v0.95 rename driver from drivers/acpi/pcc_acpi.c to
* drivers/misc/panasonic-laptop.c
*
* Jul.04, 2008 Harald Welte <laforge@gnumonks.org>
* -v0.94 replace /proc interface with device attributes
* support {set,get}keycode on th input device
*
* Jun.27, 2008 Harald Welte <laforge@gnumonks.org>
* -v0.92 merge with 2.6.26-rc6 input API changes
* remove broken <= 2.6.15 kernel support
* resolve all compiler warnings
* various coding style fixes (checkpatch.pl)
* add support for backlight api
* major code restructuring
*
* Dac.28, 2007 Harald Welte <laforge@gnumonks.org>
* -v0.91 merge with 2.6.24-rc6 ACPI changes
*
* Nov.04, 2006 Hiroshi Miura <miura@da-cha.org>
* -v0.9 remove warning about section reference.
* remove acpi_os_free
* add /proc/acpi/pcc/brightness interface for HAL access
* merge dbronaugh's enhancement
* Aug.17, 2004 David Bronaugh (dbronaugh)
* - Added screen brightness setting interface
* Thanks to FreeBSD crew (acpi_panasonic.c)
* for the ideas I needed to accomplish it
*
* May.29, 2006 Hiroshi Miura <miura@da-cha.org>
* -v0.8.4 follow to change keyinput structure
* thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
* Jacob Bower <jacob.bower@ic.ac.uk> and
* Hiroshi Yokota for providing solutions.
*
* Oct.02, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.8.2 merge code of YOKOTA Hiroshi
* <yokota@netlab.is.tsukuba.ac.jp>.
* Add sticky key mode interface.
* Refactoring acpi_pcc_generate_keyinput().
*
* Sep.15, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.8 Generate key input event on input subsystem.
* This is based on yet another driver written by
* Ryuta Nakanishi.
*
* Sep.10, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.7 Change proc interface functions using seq_file
* facility as same as other ACPI drivers.
*
* Aug.28, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.6.4 Fix a silly error with status checking
*
* Aug.25, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.6.3 replace read_acpi_int by standard function
* acpi_evaluate_integer
* some clean up and make smart copyright notice.
* fix return value of pcc_acpi_get_key()
* fix checking return value of acpi_bus_register_driver()
*
* Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
* -v0.6.2 Add check on ACPI data (num_sifr)
* Coding style cleanups, better error messages/handling
* Fixed an off-by-one error in memory allocation
*
* Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
* -v0.6.1 Fix a silly error with status checking
*
* Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
* - v0.6 Correct brightness controls to reflect reality
* based on information gleaned by Hiroshi Miura
* and discussions with Hiroshi Miura
*
* Aug.10, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.5 support LCD brightness control
* based on the disclosed information by MEI.
*
* Jul.25, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.4 first post version
* add function to retrive SIFR
*
* Jul.24, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.3 get proper status of hotkey
*
* Jul.22, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.2 add HotKey handler
*
* Jul.17, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.1 start from toshiba_acpi driver written by John Belmonte
*
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/backlight.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include <linux/input.h>
#ifndef ACPI_HOTKEY_COMPONENT
#define ACPI_HOTKEY_COMPONENT 0x10000000
#endif
#define _COMPONENT ACPI_HOTKEY_COMPONENT
MODULE_AUTHOR
(
"Hiroshi Miura, David Bronaugh and Harald Welte"
);
MODULE_DESCRIPTION
(
"ACPI HotKey driver for Panasonic Let's Note laptops"
);
MODULE_LICENSE
(
"GPL"
);
#define LOGPREFIX "pcc_acpi: "
/* Define ACPI PATHs */
/* Lets note hotkeys */
#define METHOD_HKEY_QUERY "HINF"
#define METHOD_HKEY_SQTY "SQTY"
#define METHOD_HKEY_SINF "SINF"
#define METHOD_HKEY_SSET "SSET"
#define HKEY_NOTIFY 0x80
#define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support"
#define ACPI_PCC_DEVICE_NAME "Hotkey"
#define ACPI_PCC_CLASS "pcc"
#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"
/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
*/
enum
SINF_BITS
{
SINF_NUM_BATTERIES
=
0
,
SINF_LCD_TYPE
,
SINF_AC_MAX_BRIGHT
,
SINF_AC_MIN_BRIGHT
,
SINF_AC_CUR_BRIGHT
,
SINF_DC_MAX_BRIGHT
,
SINF_DC_MIN_BRIGHT
,
SINF_DC_CUR_BRIGHT
,
SINF_MUTE
,
SINF_RESERVED
,
SINF_ENV_STATE
,
SINF_STICKY_KEY
=
0x80
,
};
/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
static
int
acpi_pcc_hotkey_add
(
struct
acpi_device
*
device
);
static
int
acpi_pcc_hotkey_remove
(
struct
acpi_device
*
device
,
int
type
);
static
int
acpi_pcc_hotkey_resume
(
struct
acpi_device
*
device
);
static
const
struct
acpi_device_id
pcc_device_ids
[]
=
{
{
"MAT0012"
,
0
},
{
"MAT0013"
,
0
},
{
"MAT0018"
,
0
},
{
"MAT0019"
,
0
},
{
""
,
0
},
};
static
struct
acpi_driver
acpi_pcc_driver
=
{
.
name
=
ACPI_PCC_DRIVER_NAME
,
.
class
=
ACPI_PCC_CLASS
,
.
ids
=
pcc_device_ids
,
.
ops
=
{
.
add
=
acpi_pcc_hotkey_add
,
.
remove
=
acpi_pcc_hotkey_remove
,
.
resume
=
acpi_pcc_hotkey_resume
,
},
};
#define KEYMAP_SIZE 11
static
const
int
initial_keymap
[
KEYMAP_SIZE
]
=
{
/* 0 */
KEY_RESERVED
,
/* 1 */
KEY_BRIGHTNESSDOWN
,
/* 2 */
KEY_BRIGHTNESSUP
,
/* 3 */
KEY_DISPLAYTOGGLE
,
/* 4 */
KEY_MUTE
,
/* 5 */
KEY_VOLUMEDOWN
,
/* 6 */
KEY_VOLUMEUP
,
/* 7 */
KEY_SLEEP
,
/* 8 */
KEY_PROG1
,
/* Change CPU boost */
/* 9 */
KEY_BATTERY
,
/* 10 */
KEY_SUSPEND
,
};
struct
pcc_acpi
{
acpi_handle
handle
;
unsigned
long
num_sifr
;
int
sticky_mode
;
u32
*
sinf
;
struct
acpi_device
*
device
;
struct
input_dev
*
input_dev
;
struct
backlight_device
*
backlight
;
int
keymap
[
KEYMAP_SIZE
];
};
struct
pcc_keyinput
{
struct
acpi_hotkey
*
hotkey
;
};
/* method access functions */
static
int
acpi_pcc_write_sset
(
struct
pcc_acpi
*
pcc
,
int
func
,
int
val
)
{
union
acpi_object
in_objs
[]
=
{
{
.
integer
.
type
=
ACPI_TYPE_INTEGER
,
.
integer
.
value
=
func
,
},
{
.
integer
.
type
=
ACPI_TYPE_INTEGER
,
.
integer
.
value
=
val
,
},
};
struct
acpi_object_list
params
=
{
.
count
=
ARRAY_SIZE
(
in_objs
),
.
pointer
=
in_objs
,
};
acpi_status
status
=
AE_OK
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_write_sset"
);
status
=
acpi_evaluate_object
(
pcc
->
handle
,
METHOD_HKEY_SSET
,
&
params
,
NULL
);
return
status
==
AE_OK
;
}
static
inline
int
acpi_pcc_get_sqty
(
struct
acpi_device
*
device
)
{
unsigned
long
s
;
acpi_status
status
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_get_sqty"
);
status
=
acpi_evaluate_integer
(
device
->
handle
,
METHOD_HKEY_SQTY
,
NULL
,
&
s
);
if
(
ACPI_SUCCESS
(
status
))
return
s
;
else
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"evaluation error HKEY.SQTY
\n
"
));
return
-
EINVAL
;
}
}
static
int
acpi_pcc_retrieve_biosdata
(
struct
pcc_acpi
*
pcc
,
u32
*
sinf
)
{
acpi_status
status
;
struct
acpi_buffer
buffer
=
{
ACPI_ALLOCATE_BUFFER
,
NULL
};
union
acpi_object
*
hkey
=
NULL
;
int
i
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_retrieve_biosdata"
);
status
=
acpi_evaluate_object
(
pcc
->
handle
,
METHOD_HKEY_SINF
,
0
,
&
buffer
);
if
(
ACPI_FAILURE
(
status
))
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"evaluation error HKEY.SINF
\n
"
));
return
0
;
}
hkey
=
buffer
.
pointer
;
if
(
!
hkey
||
(
hkey
->
type
!=
ACPI_TYPE_PACKAGE
))
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Invalid HKEY.SINF
\n
"
));
goto
end
;
}
if
(
pcc
->
num_sifr
<
hkey
->
package
.
count
)
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"SQTY reports bad SINF length
\n
"
));
status
=
AE_ERROR
;
goto
end
;
}
for
(
i
=
0
;
i
<
hkey
->
package
.
count
;
i
++
)
{
union
acpi_object
*
element
=
&
(
hkey
->
package
.
elements
[
i
]);
if
(
likely
(
element
->
type
==
ACPI_TYPE_INTEGER
))
{
sinf
[
i
]
=
element
->
integer
.
value
;
}
else
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Invalid HKEY.SINF data
\n
"
));
}
sinf
[
hkey
->
package
.
count
]
=
-
1
;
end:
kfree
(
buffer
.
pointer
);
return
status
==
AE_OK
;
}
/* backlight API interface functions */
/* This driver currently treats AC and DC brightness identical,
* since we don't need to invent an interface to the core ACPI
* logic to receive events in case a power supply is plugged in
* or removed */
static
int
bl_get
(
struct
backlight_device
*
bd
)
{
struct
pcc_acpi
*
pcc
=
bl_get_data
(
bd
);
if
(
!
acpi_pcc_retrieve_biosdata
(
pcc
,
pcc
->
sinf
))
return
-
EIO
;
return
pcc
->
sinf
[
SINF_AC_CUR_BRIGHT
];
}
static
int
bl_set_status
(
struct
backlight_device
*
bd
)
{
struct
pcc_acpi
*
pcc
=
bl_get_data
(
bd
);
int
bright
=
bd
->
props
.
brightness
;
int
rc
;
if
(
!
acpi_pcc_retrieve_biosdata
(
pcc
,
pcc
->
sinf
))
return
-
EIO
;
if
(
bright
<
pcc
->
sinf
[
SINF_AC_MIN_BRIGHT
])
bright
=
pcc
->
sinf
[
SINF_AC_MIN_BRIGHT
];
if
(
bright
<
pcc
->
sinf
[
SINF_DC_MIN_BRIGHT
])
bright
=
pcc
->
sinf
[
SINF_DC_MIN_BRIGHT
];
if
(
bright
<
pcc
->
sinf
[
SINF_AC_MIN_BRIGHT
]
||
bright
>
pcc
->
sinf
[
SINF_AC_MAX_BRIGHT
])
return
-
EINVAL
;
rc
=
acpi_pcc_write_sset
(
pcc
,
SINF_AC_CUR_BRIGHT
,
bright
);
if
(
rc
<
0
)
return
rc
;
return
acpi_pcc_write_sset
(
pcc
,
SINF_DC_CUR_BRIGHT
,
bright
);
}
static
struct
backlight_ops
pcc_backlight_ops
=
{
.
get_brightness
=
bl_get
,
.
update_status
=
bl_set_status
,
};
/* sysfs user interface functions */
static
ssize_t
show_numbatt
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
acpi_device
*
acpi
=
to_acpi_device
(
dev
);
struct
pcc_acpi
*
pcc
=
acpi_driver_data
(
acpi
);
if
(
!
acpi_pcc_retrieve_biosdata
(
pcc
,
pcc
->
sinf
))
return
-
EIO
;
return
sprintf
(
buf
,
"%u
\n
"
,
pcc
->
sinf
[
SINF_NUM_BATTERIES
]);
}
static
ssize_t
show_lcdtype
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
acpi_device
*
acpi
=
to_acpi_device
(
dev
);
struct
pcc_acpi
*
pcc
=
acpi_driver_data
(
acpi
);
if
(
!
acpi_pcc_retrieve_biosdata
(
pcc
,
pcc
->
sinf
))
return
-
EIO
;
return
sprintf
(
buf
,
"%u
\n
"
,
pcc
->
sinf
[
SINF_LCD_TYPE
]);
}
static
ssize_t
show_mute
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
acpi_device
*
acpi
=
to_acpi_device
(
dev
);
struct
pcc_acpi
*
pcc
=
acpi_driver_data
(
acpi
);
if
(
!
acpi_pcc_retrieve_biosdata
(
pcc
,
pcc
->
sinf
))
return
-
EIO
;
return
sprintf
(
buf
,
"%u
\n
"
,
pcc
->
sinf
[
SINF_MUTE
]);
}
static
ssize_t
show_sticky
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
acpi_device
*
acpi
=
to_acpi_device
(
dev
);
struct
pcc_acpi
*
pcc
=
acpi_driver_data
(
acpi
);
if
(
!
acpi_pcc_retrieve_biosdata
(
pcc
,
pcc
->
sinf
))
return
-
EIO
;
return
sprintf
(
buf
,
"%u
\n
"
,
pcc
->
sinf
[
SINF_STICKY_KEY
]);
}
static
ssize_t
set_sticky
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
struct
acpi_device
*
acpi
=
to_acpi_device
(
dev
);
struct
pcc_acpi
*
pcc
=
acpi_driver_data
(
acpi
);
int
val
;
if
(
count
&&
sscanf
(
buf
,
"%i"
,
&
val
)
==
1
&&
(
val
==
0
||
val
==
1
))
{
acpi_pcc_write_sset
(
pcc
,
SINF_STICKY_KEY
,
val
);
pcc
->
sticky_mode
=
val
;
}
return
count
;
}
static
DEVICE_ATTR
(
numbatt
,
S_IRUGO
,
show_numbatt
,
NULL
);
static
DEVICE_ATTR
(
lcdtype
,
S_IRUGO
,
show_lcdtype
,
NULL
);
static
DEVICE_ATTR
(
mute
,
S_IRUGO
,
show_mute
,
NULL
);
static
DEVICE_ATTR
(
sticky_key
,
S_IRUGO
|
S_IWUSR
,
show_sticky
,
set_sticky
);
static
struct
attribute
*
pcc_sysfs_entries
[]
=
{
&
dev_attr_numbatt
.
attr
,
&
dev_attr_lcdtype
.
attr
,
&
dev_attr_mute
.
attr
,
&
dev_attr_sticky_key
.
attr
,
NULL
,
};
static
struct
attribute_group
pcc_attr_group
=
{
.
name
=
NULL
,
/* put in device directory */
.
attrs
=
pcc_sysfs_entries
,
};
/* hotkey input device driver */
static
int
pcc_getkeycode
(
struct
input_dev
*
dev
,
int
scancode
,
int
*
keycode
)
{
struct
pcc_acpi
*
pcc
=
input_get_drvdata
(
dev
);
if
(
scancode
>=
ARRAY_SIZE
(
pcc
->
keymap
))
return
-
EINVAL
;
*
keycode
=
pcc
->
keymap
[
scancode
];
return
0
;
}
static
int
keymap_get_by_keycode
(
struct
pcc_acpi
*
pcc
,
int
keycode
)
{
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
pcc
->
keymap
);
i
++
)
{
if
(
pcc
->
keymap
[
i
]
==
keycode
)
return
i
+
1
;
}
return
0
;
}
static
int
pcc_setkeycode
(
struct
input_dev
*
dev
,
int
scancode
,
int
keycode
)
{
struct
pcc_acpi
*
pcc
=
input_get_drvdata
(
dev
);
int
oldkeycode
;
if
(
scancode
>=
ARRAY_SIZE
(
pcc
->
keymap
))
return
-
EINVAL
;
if
(
keycode
<
0
||
keycode
>
KEY_MAX
)
return
-
EINVAL
;
oldkeycode
=
pcc
->
keymap
[
scancode
];
pcc
->
keymap
[
scancode
]
=
keycode
;
set_bit
(
keycode
,
dev
->
keybit
);
if
(
!
keymap_get_by_keycode
(
pcc
,
oldkeycode
))
clear_bit
(
oldkeycode
,
dev
->
keybit
);
return
0
;
}
static
void
acpi_pcc_generate_keyinput
(
struct
pcc_acpi
*
pcc
)
{
struct
input_dev
*
hotk_input_dev
=
pcc
->
input_dev
;
int
rc
;
int
key_code
,
hkey_num
;
unsigned
long
result
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_generate_keyinput"
);
rc
=
acpi_evaluate_integer
(
pcc
->
handle
,
METHOD_HKEY_QUERY
,
NULL
,
&
result
);
if
(
!
ACPI_SUCCESS
(
rc
))
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"error getting hotkey status
\n
"
));
return
;
}
acpi_bus_generate_proc_event
(
pcc
->
device
,
HKEY_NOTIFY
,
result
);
hkey_num
=
result
&
0xf
;
if
(
hkey_num
<
0
||
hkey_num
>
ARRAY_SIZE
(
pcc
->
keymap
))
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"hotkey number out of range: %d
\n
"
,
hkey_num
));
return
;
}
key_code
=
pcc
->
keymap
[
hkey_num
];
if
(
key_code
!=
KEY_RESERVED
)
{
int
pushed
=
(
result
&
0x80
)
?
TRUE
:
FALSE
;
input_report_key
(
hotk_input_dev
,
key_code
,
pushed
);
input_sync
(
hotk_input_dev
);
}
return
;
}
static
void
acpi_pcc_hotkey_notify
(
acpi_handle
handle
,
u32
event
,
void
*
data
)
{
struct
pcc_acpi
*
pcc
=
(
struct
pcc_acpi
*
)
data
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_hotkey_notify"
);
switch
(
event
)
{
case
HKEY_NOTIFY
:
acpi_pcc_generate_keyinput
(
pcc
);
break
;
default:
/* nothing to do */
break
;
}
}
static
int
acpi_pcc_init_input
(
struct
pcc_acpi
*
pcc
)
{
int
i
,
rc
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_init_input"
);
pcc
->
input_dev
=
input_allocate_device
();
if
(
!
pcc
->
input_dev
)
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Couldn't allocate input device for hotkey"
));
return
-
ENOMEM
;
}
pcc
->
input_dev
->
evbit
[
0
]
=
BIT
(
EV_KEY
);
pcc
->
input_dev
->
name
=
ACPI_PCC_DRIVER_NAME
;
pcc
->
input_dev
->
phys
=
ACPI_PCC_INPUT_PHYS
;
pcc
->
input_dev
->
id
.
bustype
=
BUS_HOST
;
pcc
->
input_dev
->
id
.
vendor
=
0x0001
;
pcc
->
input_dev
->
id
.
product
=
0x0001
;
pcc
->
input_dev
->
id
.
version
=
0x0100
;
pcc
->
input_dev
->
getkeycode
=
pcc_getkeycode
;
pcc
->
input_dev
->
setkeycode
=
pcc_setkeycode
;
/* load initial keymap */
memcpy
(
pcc
->
keymap
,
initial_keymap
,
sizeof
(
pcc
->
keymap
));
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
pcc
->
keymap
);
i
++
)
__set_bit
(
pcc
->
keymap
[
i
],
pcc
->
input_dev
->
keybit
);
__clear_bit
(
KEY_RESERVED
,
pcc
->
input_dev
->
keybit
);
input_set_drvdata
(
pcc
->
input_dev
,
pcc
);
rc
=
input_register_device
(
pcc
->
input_dev
);
if
(
rc
<
0
)
input_free_device
(
pcc
->
input_dev
);
return
rc
;
}
/* kernel module interface */
static
int
acpi_pcc_hotkey_resume
(
struct
acpi_device
*
device
)
{
struct
pcc_acpi
*
pcc
=
acpi_driver_data
(
device
);
acpi_status
status
=
AE_OK
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_hotkey_resume"
);
if
(
device
==
NULL
||
pcc
==
NULL
)
return
-
EINVAL
;
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Sticky mode restore: %d
\n
"
,
pcc
->
sticky_mode
));
status
=
acpi_pcc_write_sset
(
pcc
,
SINF_STICKY_KEY
,
pcc
->
sticky_mode
);
return
status
==
AE_OK
?
0
:
-
EINVAL
;
}
static
int
acpi_pcc_hotkey_add
(
struct
acpi_device
*
device
)
{
acpi_status
status
;
struct
pcc_acpi
*
pcc
;
int
num_sifr
,
result
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_hotkey_add"
);
if
(
!
device
)
return
-
EINVAL
;
num_sifr
=
acpi_pcc_get_sqty
(
device
);
if
(
num_sifr
>
255
)
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"num_sifr too large"
));
return
-
ENODEV
;
}
pcc
=
kzalloc
(
sizeof
(
struct
pcc_acpi
),
GFP_KERNEL
);
if
(
!
pcc
)
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Couldn't allocate mem for pcc"
));
return
-
ENOMEM
;
}
pcc
->
sinf
=
kzalloc
(
sizeof
(
u32
)
*
(
num_sifr
+
1
),
GFP_KERNEL
);
if
(
!
pcc
->
sinf
)
{
result
=
-
ENOMEM
;
goto
out_hotkey
;
}
pcc
->
device
=
device
;
pcc
->
handle
=
device
->
handle
;
pcc
->
num_sifr
=
num_sifr
;
acpi_driver_data
(
device
)
=
pcc
;
strcpy
(
acpi_device_name
(
device
),
ACPI_PCC_DEVICE_NAME
);
strcpy
(
acpi_device_class
(
device
),
ACPI_PCC_CLASS
);
result
=
acpi_pcc_init_input
(
pcc
);
if
(
result
)
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Error installing keyinput handler
\n
"
));
goto
out_sinf
;
}
/* initialize hotkey input device */
status
=
acpi_install_notify_handler
(
pcc
->
handle
,
ACPI_DEVICE_NOTIFY
,
acpi_pcc_hotkey_notify
,
pcc
);
if
(
ACPI_FAILURE
(
status
))
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Error installing notify handler
\n
"
));
result
=
-
ENODEV
;
goto
out_input
;
}
/* initialize backlight */
pcc
->
backlight
=
backlight_device_register
(
"panasonic"
,
NULL
,
pcc
,
&
pcc_backlight_ops
);
if
(
IS_ERR
(
pcc
->
backlight
))
goto
out_notify
;
if
(
!
acpi_pcc_retrieve_biosdata
(
pcc
,
pcc
->
sinf
))
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Couldn't retrieve BIOS data
\n
"
));
goto
out_backlight
;
}
/* read the initial brightness setting from the hardware */
pcc
->
backlight
->
props
.
max_brightness
=
pcc
->
sinf
[
SINF_AC_MAX_BRIGHT
];
pcc
->
backlight
->
props
.
brightness
=
pcc
->
sinf
[
SINF_AC_CUR_BRIGHT
];
/* read the initial sticky key mode from the hardware */
pcc
->
sticky_mode
=
pcc
->
sinf
[
SINF_STICKY_KEY
];
/* add sysfs attributes */
result
=
sysfs_create_group
(
&
device
->
dev
.
kobj
,
&
pcc_attr_group
);
if
(
result
)
goto
out_backlight
;
return
0
;
out_backlight:
backlight_device_unregister
(
pcc
->
backlight
);
out_notify:
acpi_remove_notify_handler
(
pcc
->
handle
,
ACPI_DEVICE_NOTIFY
,
acpi_pcc_hotkey_notify
);
out_input:
input_unregister_device
(
pcc
->
input_dev
);
/* no need to input_free_device() since core input API refcount and
* free()s the device */
out_sinf:
kfree
(
pcc
->
sinf
);
out_hotkey:
kfree
(
pcc
);
return
result
;
}
static
int
__init
acpi_pcc_init
(
void
)
{
int
result
=
0
;
ACPI_FUNCTION_TRACE
(
"acpi_pcc_init"
);
if
(
acpi_disabled
)
return
-
ENODEV
;
result
=
acpi_bus_register_driver
(
&
acpi_pcc_driver
);
if
(
result
<
0
)
{
ACPI_DEBUG_PRINT
((
ACPI_DB_ERROR
,
"Error registering hotkey driver
\n
"
));
return
-
ENODEV
;
}
return
0
;
}
static
int
acpi_pcc_hotkey_remove
(
struct
acpi_device
*
device
,
int
type
)
{
struct
pcc_acpi
*
pcc
=
acpi_driver_data
(
device
);
ACPI_FUNCTION_TRACE
(
"acpi_pcc_hotkey_remove"
);
if
(
!
device
||
!
pcc
)
return
-
EINVAL
;
sysfs_remove_group
(
&
device
->
dev
.
kobj
,
&
pcc_attr_group
);
backlight_device_unregister
(
pcc
->
backlight
);
acpi_remove_notify_handler
(
pcc
->
handle
,
ACPI_DEVICE_NOTIFY
,
acpi_pcc_hotkey_notify
);
input_unregister_device
(
pcc
->
input_dev
);
/* no need to input_free_device() since core input API refcount and
* free()s the device */
kfree
(
pcc
->
sinf
);
kfree
(
pcc
);
return
0
;
}
static
void
__exit
acpi_pcc_exit
(
void
)
{
ACPI_FUNCTION_TRACE
(
"acpi_pcc_exit"
);
acpi_bus_unregister_driver
(
&
acpi_pcc_driver
);
}
module_init
(
acpi_pcc_init
);
module_exit
(
acpi_pcc_exit
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment