Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (81 commits) xo15-ebook: Remove device.wakeup_count ips: use interruptible waits in ips-monitor acer-wmi: does not poll device status when WMI event is available acer-wmi: does not set persistence state by rfkill_init_sw_state platform-drivers: x86: fix common misspellings acer-wmi: use pr_<level> for messages asus-wmi: potential NULL dereference in show_call() asus-wmi: signedness bug in read_brightness() platform-driver-x86: samsung-laptop: make dmi_check_cb to return 1 instead of 0 platform-driver-x86: fix wrong merge for compal-laptop.c msi-laptop: use pr_<level> for messages Platform: add Samsung Laptop platform driver acer-wmi: Fix WMI ID acer-wmi: deactive mail led when power off msi-laptop: send out touchpad on/off key acer-wmi: set the touchpad toggle key code to KEY_TOUCHPAD_TOGGLE platform-driver-x86: intel_mid_thermal: fix unterminated platform_device_id table sony-laptop: potential null dereference sony-laptop: handle allocation failures sony-laptop: return negative on failure in sony_nc_add() ...
This commit is contained in:
commit
baaca1a614
|
@ -0,0 +1,19 @@
|
||||||
|
What: /sys/devices/platform/samsung/performance_level
|
||||||
|
Date: January 1, 2010
|
||||||
|
KernelVersion: 2.6.33
|
||||||
|
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||||
|
Description: Some Samsung laptops have different "performance levels"
|
||||||
|
that are can be modified by a function key, and by this
|
||||||
|
sysfs file. These values don't always make a whole lot
|
||||||
|
of sense, but some users like to modify them to keep
|
||||||
|
their fans quiet at all costs. Reading from this file
|
||||||
|
will show the current performance level. Writing to the
|
||||||
|
file can change this value.
|
||||||
|
Valid options:
|
||||||
|
"silent"
|
||||||
|
"normal"
|
||||||
|
"overclock"
|
||||||
|
Note that not all laptops support all of these options.
|
||||||
|
Specifically, not all support the "overclock" option,
|
||||||
|
and it's still unknown if this value even changes
|
||||||
|
anything, other than making the user feel a bit better.
|
|
@ -0,0 +1,31 @@
|
||||||
|
What: /sys/devices/platform/<platform>/cpufv
|
||||||
|
Date: Oct 2010
|
||||||
|
KernelVersion: 2.6.37
|
||||||
|
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||||
|
Description:
|
||||||
|
Change CPU clock configuration (write-only).
|
||||||
|
There are three available clock configuration:
|
||||||
|
* 0 -> Super Performance Mode
|
||||||
|
* 1 -> High Performance Mode
|
||||||
|
* 2 -> Power Saving Mode
|
||||||
|
|
||||||
|
What: /sys/devices/platform/<platform>/camera
|
||||||
|
Date: Jan 2010
|
||||||
|
KernelVersion: 2.6.39
|
||||||
|
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||||
|
Description:
|
||||||
|
Control the camera. 1 means on, 0 means off.
|
||||||
|
|
||||||
|
What: /sys/devices/platform/<platform>/cardr
|
||||||
|
Date: Jan 2010
|
||||||
|
KernelVersion: 2.6.39
|
||||||
|
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||||
|
Description:
|
||||||
|
Control the card reader. 1 means on, 0 means off.
|
||||||
|
|
||||||
|
What: /sys/devices/platform/<platform>/touchpad
|
||||||
|
Date: Jan 2010
|
||||||
|
KernelVersion: 2.6.39
|
||||||
|
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||||
|
Description:
|
||||||
|
Control the card touchpad. 1 means on, 0 means off.
|
|
@ -1,10 +0,0 @@
|
||||||
What: /sys/devices/platform/eeepc-wmi/cpufv
|
|
||||||
Date: Oct 2010
|
|
||||||
KernelVersion: 2.6.37
|
|
||||||
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
|
||||||
Description:
|
|
||||||
Change CPU clock configuration (write-only).
|
|
||||||
There are three available clock configuration:
|
|
||||||
* 0 -> Super Performance Mode
|
|
||||||
* 1 -> High Performance Mode
|
|
||||||
* 2 -> Power Saving Mode
|
|
|
@ -14,7 +14,8 @@ Some models report hotkeys through the SNC or SPIC devices, such events are
|
||||||
reported both through the ACPI subsystem as acpi events and through the INPUT
|
reported both through the ACPI subsystem as acpi events and through the INPUT
|
||||||
subsystem. See the logs of acpid or /proc/acpi/event and
|
subsystem. See the logs of acpid or /proc/acpi/event and
|
||||||
/proc/bus/input/devices to find out what those events are and which input
|
/proc/bus/input/devices to find out what those events are and which input
|
||||||
devices are created by the driver.
|
devices are created by the driver. Additionally, loading the driver with the
|
||||||
|
debug option will report all events in the kernel log.
|
||||||
|
|
||||||
Backlight control:
|
Backlight control:
|
||||||
------------------
|
------------------
|
||||||
|
@ -64,6 +65,16 @@ powers off the sound card,
|
||||||
# echo "1" > /sys/devices/platform/sony-laptop/audiopower
|
# echo "1" > /sys/devices/platform/sony-laptop/audiopower
|
||||||
powers on the sound card.
|
powers on the sound card.
|
||||||
|
|
||||||
|
|
||||||
|
RFkill control:
|
||||||
|
---------------
|
||||||
|
More recent Vaio models expose a consistent set of ACPI methods to
|
||||||
|
control radio frequency emitting devices. If you are a lucky owner of
|
||||||
|
such a laptop you will find the necessary rfkill devices under
|
||||||
|
/sys/class/rfkill. Check those starting with sony-* in
|
||||||
|
# grep . /sys/class/rfkill/*/{state,name}
|
||||||
|
|
||||||
|
|
||||||
Development:
|
Development:
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -75,8 +86,21 @@ pass the option 'debug=1'.
|
||||||
REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
|
REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
|
||||||
|
|
||||||
In your kernel logs you will find the list of all ACPI methods
|
In your kernel logs you will find the list of all ACPI methods
|
||||||
the SNC device has on your laptop. You can see the GCDP/GCDP methods
|
the SNC device has on your laptop.
|
||||||
used to pwer on/off the CD drive, but there are others.
|
|
||||||
|
* For new models you will see a long list of meaningless method names,
|
||||||
|
reading the DSDT table source should reveal that:
|
||||||
|
(1) the SNC device uses an internal capability lookup table
|
||||||
|
(2) SN00 is used to find values in the lookup table
|
||||||
|
(3) SN06 and SN07 are used to call into the real methods based on
|
||||||
|
offsets you can obtain iterating the table using SN00
|
||||||
|
(4) SN02 used to enable events.
|
||||||
|
Some values in the capability lookup table are more or less known, see
|
||||||
|
the code for all sony_call_snc_handle calls, others are more obscure.
|
||||||
|
|
||||||
|
* For old models you can see the GCDP/GCDP methods used to pwer on/off
|
||||||
|
the CD drive, but there are others and they are usually different from
|
||||||
|
model to model.
|
||||||
|
|
||||||
I HAVE NO IDEA WHAT THOSE METHODS DO.
|
I HAVE NO IDEA WHAT THOSE METHODS DO.
|
||||||
|
|
||||||
|
@ -108,9 +132,8 @@ Bugs/Limitations:
|
||||||
laptop, including permanent damage.
|
laptop, including permanent damage.
|
||||||
|
|
||||||
* The sony-laptop and sonypi drivers do not interact at all. In the
|
* The sony-laptop and sonypi drivers do not interact at all. In the
|
||||||
future, sonypi could use sony-laptop to do (part of) its business.
|
future, sonypi will be removed and replaced by sony-laptop.
|
||||||
|
|
||||||
* spicctrl, which is the userspace tool used to communicate with the
|
* spicctrl, which is the userspace tool used to communicate with the
|
||||||
sonypi driver (through /dev/sonypi) does not try to use the
|
sonypi driver (through /dev/sonypi) is deprecated as well since all
|
||||||
sony-laptop driver. In the future, spicctrl could try sonypi first,
|
its features are now available under the sysfs tree via sony-laptop.
|
||||||
and if it isn't present, try sony-laptop instead.
|
|
||||||
|
|
30
MAINTAINERS
30
MAINTAINERS
|
@ -1157,14 +1157,14 @@ S: Maintained
|
||||||
F: Documentation/hwmon/asc7621
|
F: Documentation/hwmon/asc7621
|
||||||
F: drivers/hwmon/asc7621.c
|
F: drivers/hwmon/asc7621.c
|
||||||
|
|
||||||
ASUS ACPI EXTRAS DRIVER
|
ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
|
||||||
M: Corentin Chary <corentincj@iksaif.net>
|
M: Corentin Chary <corentincj@iksaif.net>
|
||||||
M: Karol Kozimor <sziwan@users.sourceforge.net>
|
|
||||||
L: acpi4asus-user@lists.sourceforge.net
|
L: acpi4asus-user@lists.sourceforge.net
|
||||||
L: platform-driver-x86@vger.kernel.org
|
L: platform-driver-x86@vger.kernel.org
|
||||||
W: http://acpi4asus.sf.net
|
W: http://acpi4asus.sf.net
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/platform/x86/asus_acpi.c
|
F: drivers/platform/x86/asus*.c
|
||||||
|
F: drivers/platform/x86/eeepc*.c
|
||||||
|
|
||||||
ASUS ASB100 HARDWARE MONITOR DRIVER
|
ASUS ASB100 HARDWARE MONITOR DRIVER
|
||||||
M: "Mark M. Hoffman" <mhoffman@lightlink.com>
|
M: "Mark M. Hoffman" <mhoffman@lightlink.com>
|
||||||
|
@ -1172,14 +1172,6 @@ L: lm-sensors@lm-sensors.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/hwmon/asb100.c
|
F: drivers/hwmon/asb100.c
|
||||||
|
|
||||||
ASUS LAPTOP EXTRAS DRIVER
|
|
||||||
M: Corentin Chary <corentincj@iksaif.net>
|
|
||||||
L: acpi4asus-user@lists.sourceforge.net
|
|
||||||
L: platform-driver-x86@vger.kernel.org
|
|
||||||
W: http://acpi4asus.sf.net
|
|
||||||
S: Maintained
|
|
||||||
F: drivers/platform/x86/asus-laptop.c
|
|
||||||
|
|
||||||
ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
|
ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
|
||||||
M: Dan Williams <dan.j.williams@intel.com>
|
M: Dan Williams <dan.j.williams@intel.com>
|
||||||
W: http://sourceforge.net/projects/xscaleiop
|
W: http://sourceforge.net/projects/xscaleiop
|
||||||
|
@ -2414,22 +2406,6 @@ T: git git://git.alsa-project.org/alsa-kernel.git
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: sound/usb/misc/ua101.c
|
F: sound/usb/misc/ua101.c
|
||||||
|
|
||||||
EEEPC LAPTOP EXTRAS DRIVER
|
|
||||||
M: Corentin Chary <corentincj@iksaif.net>
|
|
||||||
L: acpi4asus-user@lists.sourceforge.net
|
|
||||||
L: platform-driver-x86@vger.kernel.org
|
|
||||||
W: http://acpi4asus.sf.net
|
|
||||||
S: Maintained
|
|
||||||
F: drivers/platform/x86/eeepc-laptop.c
|
|
||||||
|
|
||||||
EEEPC WMI EXTRAS DRIVER
|
|
||||||
M: Corentin Chary <corentincj@iksaif.net>
|
|
||||||
L: acpi4asus-user@lists.sourceforge.net
|
|
||||||
L: platform-driver-x86@vger.kernel.org
|
|
||||||
W: http://acpi4asus.sf.net
|
|
||||||
S: Maintained
|
|
||||||
F: drivers/platform/x86/eeepc-wmi.c
|
|
||||||
|
|
||||||
EFIFB FRAMEBUFFER DRIVER
|
EFIFB FRAMEBUFFER DRIVER
|
||||||
L: linux-fbdev@vger.kernel.org
|
L: linux-fbdev@vger.kernel.org
|
||||||
M: Peter Jones <pjones@redhat.com>
|
M: Peter Jones <pjones@redhat.com>
|
||||||
|
|
|
@ -101,6 +101,19 @@ config DELL_WMI
|
||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called dell-wmi.
|
be called dell-wmi.
|
||||||
|
|
||||||
|
config DELL_WMI_AIO
|
||||||
|
tristate "WMI Hotkeys for Dell All-In-One series"
|
||||||
|
depends on ACPI_WMI
|
||||||
|
depends on INPUT
|
||||||
|
select INPUT_SPARSEKMAP
|
||||||
|
---help---
|
||||||
|
Say Y here if you want to support WMI-based hotkeys on Dell
|
||||||
|
All-In-One machines.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
be called dell-wmi.
|
||||||
|
|
||||||
|
|
||||||
config FUJITSU_LAPTOP
|
config FUJITSU_LAPTOP
|
||||||
tristate "Fujitsu Laptop Extras"
|
tristate "Fujitsu Laptop Extras"
|
||||||
depends on ACPI
|
depends on ACPI
|
||||||
|
@ -438,23 +451,53 @@ config EEEPC_LAPTOP
|
||||||
Bluetooth, backlight and allows powering on/off some other
|
Bluetooth, backlight and allows powering on/off some other
|
||||||
devices.
|
devices.
|
||||||
|
|
||||||
If you have an Eee PC laptop, say Y or M here.
|
If you have an Eee PC laptop, say Y or M here. If this driver
|
||||||
|
doesn't work on your Eee PC, try eeepc-wmi instead.
|
||||||
|
|
||||||
config EEEPC_WMI
|
config ASUS_WMI
|
||||||
tristate "Eee PC WMI Hotkey Driver (EXPERIMENTAL)"
|
tristate "ASUS WMI Driver (EXPERIMENTAL)"
|
||||||
depends on ACPI_WMI
|
depends on ACPI_WMI
|
||||||
depends on INPUT
|
depends on INPUT
|
||||||
|
depends on HWMON
|
||||||
depends on EXPERIMENTAL
|
depends on EXPERIMENTAL
|
||||||
depends on BACKLIGHT_CLASS_DEVICE
|
depends on BACKLIGHT_CLASS_DEVICE
|
||||||
depends on RFKILL || RFKILL = n
|
depends on RFKILL || RFKILL = n
|
||||||
|
depends on HOTPLUG_PCI
|
||||||
select INPUT_SPARSEKMAP
|
select INPUT_SPARSEKMAP
|
||||||
select LEDS_CLASS
|
select LEDS_CLASS
|
||||||
select NEW_LEDS
|
select NEW_LEDS
|
||||||
---help---
|
---help---
|
||||||
Say Y here if you want to support WMI-based hotkeys on Eee PC laptops.
|
Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new
|
||||||
|
Asus Notebooks).
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called eeepc-wmi.
|
be called asus-wmi.
|
||||||
|
|
||||||
|
config ASUS_NB_WMI
|
||||||
|
tristate "Asus Notebook WMI Driver (EXPERIMENTAL)"
|
||||||
|
depends on ASUS_WMI
|
||||||
|
---help---
|
||||||
|
This is a driver for newer Asus notebooks. It adds extra features
|
||||||
|
like wireless radio and bluetooth control, leds, hotkeys, backlight...
|
||||||
|
|
||||||
|
For more informations, see
|
||||||
|
<file:Documentation/ABI/testing/sysfs-platform-asus-wmi>
|
||||||
|
|
||||||
|
If you have an ACPI-WMI compatible Asus Notebook, say Y or M
|
||||||
|
here.
|
||||||
|
|
||||||
|
config EEEPC_WMI
|
||||||
|
tristate "Eee PC WMI Driver (EXPERIMENTAL)"
|
||||||
|
depends on ASUS_WMI
|
||||||
|
---help---
|
||||||
|
This is a driver for newer Eee PC laptops. It adds extra features
|
||||||
|
like wireless radio and bluetooth control, leds, hotkeys, backlight...
|
||||||
|
|
||||||
|
For more informations, see
|
||||||
|
<file:Documentation/ABI/testing/sysfs-platform-asus-wmi>
|
||||||
|
|
||||||
|
If you have an ACPI-WMI compatible Eee PC laptop (>= 1000), say Y or M
|
||||||
|
here.
|
||||||
|
|
||||||
config ACPI_WMI
|
config ACPI_WMI
|
||||||
tristate "WMI"
|
tristate "WMI"
|
||||||
|
@ -616,6 +659,21 @@ config GPIO_INTEL_PMIC
|
||||||
Say Y here to support GPIO via the SCU IPC interface
|
Say Y here to support GPIO via the SCU IPC interface
|
||||||
on Intel MID platforms.
|
on Intel MID platforms.
|
||||||
|
|
||||||
|
config INTEL_MID_POWER_BUTTON
|
||||||
|
tristate "power button driver for Intel MID platforms"
|
||||||
|
depends on INTEL_SCU_IPC && INPUT
|
||||||
|
help
|
||||||
|
This driver handles the power button on the Intel MID platforms.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
config INTEL_MFLD_THERMAL
|
||||||
|
tristate "Thermal driver for Intel Medfield platform"
|
||||||
|
depends on INTEL_SCU_IPC && THERMAL
|
||||||
|
help
|
||||||
|
Say Y here to enable thermal driver support for the Intel Medfield
|
||||||
|
platform.
|
||||||
|
|
||||||
config RAR_REGISTER
|
config RAR_REGISTER
|
||||||
bool "Restricted Access Region Register Driver"
|
bool "Restricted Access Region Register Driver"
|
||||||
depends on PCI && X86_MRST
|
depends on PCI && X86_MRST
|
||||||
|
@ -672,4 +730,26 @@ config XO1_RFKILL
|
||||||
Support for enabling/disabling the WLAN interface on the OLPC XO-1
|
Support for enabling/disabling the WLAN interface on the OLPC XO-1
|
||||||
laptop.
|
laptop.
|
||||||
|
|
||||||
|
config XO15_EBOOK
|
||||||
|
tristate "OLPC XO-1.5 ebook switch"
|
||||||
|
depends on ACPI && INPUT
|
||||||
|
---help---
|
||||||
|
Support for the ebook switch on the OLPC XO-1.5 laptop.
|
||||||
|
|
||||||
|
This switch is triggered as the screen is rotated and folded down to
|
||||||
|
convert the device into ebook form.
|
||||||
|
|
||||||
|
config SAMSUNG_LAPTOP
|
||||||
|
tristate "Samsung Laptop driver"
|
||||||
|
depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86
|
||||||
|
---help---
|
||||||
|
This module implements a driver for a wide range of different
|
||||||
|
Samsung laptops. It offers control over the different
|
||||||
|
function keys, wireless LED, LCD backlight level, and
|
||||||
|
sometimes provides a "performance_control" sysfs file to allow
|
||||||
|
the performance level of the laptop to be changed.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called samsung-laptop.
|
||||||
|
|
||||||
endif # X86_PLATFORM_DEVICES
|
endif # X86_PLATFORM_DEVICES
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# x86 Platform-Specific Drivers
|
# x86 Platform-Specific Drivers
|
||||||
#
|
#
|
||||||
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
|
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
|
||||||
|
obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
|
||||||
|
obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
|
||||||
obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
|
obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
|
||||||
obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
|
obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
|
||||||
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
|
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
|
||||||
|
@ -10,6 +12,7 @@ obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
|
||||||
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
|
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
|
||||||
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
||||||
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
|
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
|
||||||
|
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
|
||||||
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
|
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
|
||||||
obj-$(CONFIG_ACERHDF) += acerhdf.o
|
obj-$(CONFIG_ACERHDF) += acerhdf.o
|
||||||
obj-$(CONFIG_HP_ACCEL) += hp_accel.o
|
obj-$(CONFIG_HP_ACCEL) += hp_accel.o
|
||||||
|
@ -29,9 +32,13 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
|
||||||
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
|
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
|
||||||
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
|
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
|
||||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o
|
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
|
||||||
|
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||||
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
|
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
|
||||||
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
|
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
|
||||||
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
|
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
|
||||||
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
|
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
|
||||||
|
obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o
|
||||||
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
|
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
|
||||||
|
obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
|
||||||
|
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -46,12 +48,6 @@ MODULE_AUTHOR("Carlos Corbacho");
|
||||||
MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
|
MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
#define ACER_LOGPREFIX "acer-wmi: "
|
|
||||||
#define ACER_ERR KERN_ERR ACER_LOGPREFIX
|
|
||||||
#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
|
|
||||||
#define ACER_INFO KERN_INFO ACER_LOGPREFIX
|
|
||||||
#define ACER_WARNING KERN_WARNING ACER_LOGPREFIX
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Magic Number
|
* Magic Number
|
||||||
* Meaning is unknown - this number is required for writing to ACPI for AMW0
|
* Meaning is unknown - this number is required for writing to ACPI for AMW0
|
||||||
|
@ -84,7 +80,7 @@ MODULE_LICENSE("GPL");
|
||||||
#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
|
#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
|
||||||
#define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
|
#define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
|
||||||
#define WMID_GUID1 "6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3"
|
#define WMID_GUID1 "6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3"
|
||||||
#define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
|
#define WMID_GUID2 "95764E09-FB56-4E83-B31A-37761F60994A"
|
||||||
#define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531"
|
#define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -93,7 +89,7 @@ MODULE_LICENSE("GPL");
|
||||||
#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
|
#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
|
||||||
|
|
||||||
MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
|
MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
|
||||||
MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
|
MODULE_ALIAS("wmi:6AF4F258-B401-42Fd-BE91-3D4AC2D7C0D3");
|
||||||
MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
|
MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
|
||||||
|
|
||||||
enum acer_wmi_event_ids {
|
enum acer_wmi_event_ids {
|
||||||
|
@ -108,7 +104,7 @@ static const struct key_entry acer_wmi_keymap[] = {
|
||||||
{KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */
|
{KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */
|
||||||
{KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */
|
{KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */
|
||||||
{KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */
|
{KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */
|
||||||
{KE_KEY, 0x82, {KEY_F22} }, /* Touch Pad On/Off */
|
{KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad On/Off */
|
||||||
{KE_END, 0}
|
{KE_END, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -221,6 +217,7 @@ struct acer_debug {
|
||||||
static struct rfkill *wireless_rfkill;
|
static struct rfkill *wireless_rfkill;
|
||||||
static struct rfkill *bluetooth_rfkill;
|
static struct rfkill *bluetooth_rfkill;
|
||||||
static struct rfkill *threeg_rfkill;
|
static struct rfkill *threeg_rfkill;
|
||||||
|
static bool rfkill_inited;
|
||||||
|
|
||||||
/* Each low-level interface must define at least some of the following */
|
/* Each low-level interface must define at least some of the following */
|
||||||
struct wmi_interface {
|
struct wmi_interface {
|
||||||
|
@ -845,7 +842,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
|
||||||
has_type_aa = true;
|
has_type_aa = true;
|
||||||
type_aa = (struct hotkey_function_type_aa *) header;
|
type_aa = (struct hotkey_function_type_aa *) header;
|
||||||
|
|
||||||
printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n",
|
pr_info("Function bitmap for Communication Button: 0x%x\n",
|
||||||
type_aa->commun_func_bitmap);
|
type_aa->commun_func_bitmap);
|
||||||
|
|
||||||
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
|
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
|
||||||
|
@ -991,6 +988,7 @@ static int __devinit acer_led_init(struct device *dev)
|
||||||
|
|
||||||
static void acer_led_exit(void)
|
static void acer_led_exit(void)
|
||||||
{
|
{
|
||||||
|
set_u32(LED_OFF, ACER_CAP_MAILLED);
|
||||||
led_classdev_unregister(&mail_led);
|
led_classdev_unregister(&mail_led);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1036,7 +1034,7 @@ static int __devinit acer_backlight_init(struct device *dev)
|
||||||
bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops,
|
bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops,
|
||||||
&props);
|
&props);
|
||||||
if (IS_ERR(bd)) {
|
if (IS_ERR(bd)) {
|
||||||
printk(ACER_ERR "Could not register Acer backlight device\n");
|
pr_err("Could not register Acer backlight device\n");
|
||||||
acer_backlight_device = NULL;
|
acer_backlight_device = NULL;
|
||||||
return PTR_ERR(bd);
|
return PTR_ERR(bd);
|
||||||
}
|
}
|
||||||
|
@ -1083,8 +1081,7 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device)
|
||||||
return AE_ERROR;
|
return AE_ERROR;
|
||||||
}
|
}
|
||||||
if (obj->buffer.length != 8) {
|
if (obj->buffer.length != 8) {
|
||||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
|
||||||
obj->buffer.length);
|
|
||||||
kfree(obj);
|
kfree(obj);
|
||||||
return AE_ERROR;
|
return AE_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -1093,7 +1090,7 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device)
|
||||||
kfree(obj);
|
kfree(obj);
|
||||||
|
|
||||||
if (return_value.error_code || return_value.ec_return_value)
|
if (return_value.error_code || return_value.ec_return_value)
|
||||||
printk(ACER_WARNING "Get Device Status failed: "
|
pr_warning("Get Device Status failed: "
|
||||||
"0x%x - 0x%x\n", return_value.error_code,
|
"0x%x - 0x%x\n", return_value.error_code,
|
||||||
return_value.ec_return_value);
|
return_value.ec_return_value);
|
||||||
else
|
else
|
||||||
|
@ -1161,9 +1158,13 @@ static int acer_rfkill_set(void *data, bool blocked)
|
||||||
{
|
{
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
u32 cap = (unsigned long)data;
|
u32 cap = (unsigned long)data;
|
||||||
status = set_u32(!blocked, cap);
|
|
||||||
if (ACPI_FAILURE(status))
|
if (rfkill_inited) {
|
||||||
return -ENODEV;
|
status = set_u32(!blocked, cap);
|
||||||
|
if (ACPI_FAILURE(status))
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1187,14 +1188,16 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
status = get_device_status(&state, cap);
|
status = get_device_status(&state, cap);
|
||||||
if (ACPI_SUCCESS(status))
|
|
||||||
rfkill_init_sw_state(rfkill_dev, !state);
|
|
||||||
|
|
||||||
err = rfkill_register(rfkill_dev);
|
err = rfkill_register(rfkill_dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
rfkill_destroy(rfkill_dev);
|
rfkill_destroy(rfkill_dev);
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ACPI_SUCCESS(status))
|
||||||
|
rfkill_set_sw_state(rfkill_dev, !state);
|
||||||
|
|
||||||
return rfkill_dev;
|
return rfkill_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1229,14 +1232,19 @@ static int acer_rfkill_init(struct device *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
|
rfkill_inited = true;
|
||||||
|
|
||||||
|
if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
|
||||||
|
schedule_delayed_work(&acer_rfkill_work,
|
||||||
|
round_jiffies_relative(HZ));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void acer_rfkill_exit(void)
|
static void acer_rfkill_exit(void)
|
||||||
{
|
{
|
||||||
cancel_delayed_work_sync(&acer_rfkill_work);
|
if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
|
||||||
|
cancel_delayed_work_sync(&acer_rfkill_work);
|
||||||
|
|
||||||
rfkill_unregister(wireless_rfkill);
|
rfkill_unregister(wireless_rfkill);
|
||||||
rfkill_destroy(wireless_rfkill);
|
rfkill_destroy(wireless_rfkill);
|
||||||
|
@ -1309,7 +1317,7 @@ static void acer_wmi_notify(u32 value, void *context)
|
||||||
|
|
||||||
status = wmi_get_event_data(value, &response);
|
status = wmi_get_event_data(value, &response);
|
||||||
if (status != AE_OK) {
|
if (status != AE_OK) {
|
||||||
printk(ACER_WARNING "bad event status 0x%x\n", status);
|
pr_warning("bad event status 0x%x\n", status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1318,14 +1326,12 @@ static void acer_wmi_notify(u32 value, void *context)
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return;
|
return;
|
||||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||||
printk(ACER_WARNING "Unknown response received %d\n",
|
pr_warning("Unknown response received %d\n", obj->type);
|
||||||
obj->type);
|
|
||||||
kfree(obj);
|
kfree(obj);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (obj->buffer.length != 8) {
|
if (obj->buffer.length != 8) {
|
||||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
|
||||||
obj->buffer.length);
|
|
||||||
kfree(obj);
|
kfree(obj);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1335,13 +1341,26 @@ static void acer_wmi_notify(u32 value, void *context)
|
||||||
|
|
||||||
switch (return_value.function) {
|
switch (return_value.function) {
|
||||||
case WMID_HOTKEY_EVENT:
|
case WMID_HOTKEY_EVENT:
|
||||||
|
if (return_value.device_state) {
|
||||||
|
u16 device_state = return_value.device_state;
|
||||||
|
pr_debug("deivces states: 0x%x\n", device_state);
|
||||||
|
if (has_cap(ACER_CAP_WIRELESS))
|
||||||
|
rfkill_set_sw_state(wireless_rfkill,
|
||||||
|
!(device_state & ACER_WMID3_GDS_WIRELESS));
|
||||||
|
if (has_cap(ACER_CAP_BLUETOOTH))
|
||||||
|
rfkill_set_sw_state(bluetooth_rfkill,
|
||||||
|
!(device_state & ACER_WMID3_GDS_BLUETOOTH));
|
||||||
|
if (has_cap(ACER_CAP_THREEG))
|
||||||
|
rfkill_set_sw_state(threeg_rfkill,
|
||||||
|
!(device_state & ACER_WMID3_GDS_THREEG));
|
||||||
|
}
|
||||||
if (!sparse_keymap_report_event(acer_wmi_input_dev,
|
if (!sparse_keymap_report_event(acer_wmi_input_dev,
|
||||||
return_value.key_num, 1, true))
|
return_value.key_num, 1, true))
|
||||||
printk(ACER_WARNING "Unknown key number - 0x%x\n",
|
pr_warning("Unknown key number - 0x%x\n",
|
||||||
return_value.key_num);
|
return_value.key_num);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printk(ACER_WARNING "Unknown function number - %d - %d\n",
|
pr_warning("Unknown function number - %d - %d\n",
|
||||||
return_value.function, return_value.key_num);
|
return_value.function, return_value.key_num);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1370,8 +1389,7 @@ wmid3_set_lm_mode(struct lm_input_params *params,
|
||||||
return AE_ERROR;
|
return AE_ERROR;
|
||||||
}
|
}
|
||||||
if (obj->buffer.length != 4) {
|
if (obj->buffer.length != 4) {
|
||||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
|
||||||
obj->buffer.length);
|
|
||||||
kfree(obj);
|
kfree(obj);
|
||||||
return AE_ERROR;
|
return AE_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -1396,11 +1414,11 @@ static int acer_wmi_enable_ec_raw(void)
|
||||||
status = wmid3_set_lm_mode(¶ms, &return_value);
|
status = wmid3_set_lm_mode(¶ms, &return_value);
|
||||||
|
|
||||||
if (return_value.error_code || return_value.ec_return_value)
|
if (return_value.error_code || return_value.ec_return_value)
|
||||||
printk(ACER_WARNING "Enabling EC raw mode failed: "
|
pr_warning("Enabling EC raw mode failed: "
|
||||||
"0x%x - 0x%x\n", return_value.error_code,
|
"0x%x - 0x%x\n", return_value.error_code,
|
||||||
return_value.ec_return_value);
|
return_value.ec_return_value);
|
||||||
else
|
else
|
||||||
printk(ACER_INFO "Enabled EC raw mode");
|
pr_info("Enabled EC raw mode");
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -1419,7 +1437,7 @@ static int acer_wmi_enable_lm(void)
|
||||||
status = wmid3_set_lm_mode(¶ms, &return_value);
|
status = wmid3_set_lm_mode(¶ms, &return_value);
|
||||||
|
|
||||||
if (return_value.error_code || return_value.ec_return_value)
|
if (return_value.error_code || return_value.ec_return_value)
|
||||||
printk(ACER_WARNING "Enabling Launch Manager failed: "
|
pr_warning("Enabling Launch Manager failed: "
|
||||||
"0x%x - 0x%x\n", return_value.error_code,
|
"0x%x - 0x%x\n", return_value.error_code,
|
||||||
return_value.ec_return_value);
|
return_value.ec_return_value);
|
||||||
|
|
||||||
|
@ -1553,6 +1571,7 @@ pm_message_t state)
|
||||||
|
|
||||||
if (has_cap(ACER_CAP_MAILLED)) {
|
if (has_cap(ACER_CAP_MAILLED)) {
|
||||||
get_u32(&value, ACER_CAP_MAILLED);
|
get_u32(&value, ACER_CAP_MAILLED);
|
||||||
|
set_u32(LED_OFF, ACER_CAP_MAILLED);
|
||||||
data->mailled = value;
|
data->mailled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1580,6 +1599,17 @@ static int acer_platform_resume(struct platform_device *device)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void acer_platform_shutdown(struct platform_device *device)
|
||||||
|
{
|
||||||
|
struct acer_data *data = &interface->data;
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (has_cap(ACER_CAP_MAILLED))
|
||||||
|
set_u32(LED_OFF, ACER_CAP_MAILLED);
|
||||||
|
}
|
||||||
|
|
||||||
static struct platform_driver acer_platform_driver = {
|
static struct platform_driver acer_platform_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "acer-wmi",
|
.name = "acer-wmi",
|
||||||
|
@ -1589,6 +1619,7 @@ static struct platform_driver acer_platform_driver = {
|
||||||
.remove = acer_platform_remove,
|
.remove = acer_platform_remove,
|
||||||
.suspend = acer_platform_suspend,
|
.suspend = acer_platform_suspend,
|
||||||
.resume = acer_platform_resume,
|
.resume = acer_platform_resume,
|
||||||
|
.shutdown = acer_platform_shutdown,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device *acer_platform_device;
|
static struct platform_device *acer_platform_device;
|
||||||
|
@ -1636,7 +1667,7 @@ static int create_debugfs(void)
|
||||||
{
|
{
|
||||||
interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
|
interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
|
||||||
if (!interface->debug.root) {
|
if (!interface->debug.root) {
|
||||||
printk(ACER_ERR "Failed to create debugfs directory");
|
pr_err("Failed to create debugfs directory");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1657,11 +1688,10 @@ static int __init acer_wmi_init(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n");
|
pr_info("Acer Laptop ACPI-WMI Extras\n");
|
||||||
|
|
||||||
if (dmi_check_system(acer_blacklist)) {
|
if (dmi_check_system(acer_blacklist)) {
|
||||||
printk(ACER_INFO "Blacklisted hardware detected - "
|
pr_info("Blacklisted hardware detected - not loading\n");
|
||||||
"not loading\n");
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1678,12 +1708,11 @@ static int __init acer_wmi_init(void)
|
||||||
|
|
||||||
if (wmi_has_guid(WMID_GUID2) && interface) {
|
if (wmi_has_guid(WMID_GUID2) && interface) {
|
||||||
if (ACPI_FAILURE(WMID_set_capabilities())) {
|
if (ACPI_FAILURE(WMID_set_capabilities())) {
|
||||||
printk(ACER_ERR "Unable to detect available WMID "
|
pr_err("Unable to detect available WMID devices\n");
|
||||||
"devices\n");
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
} else if (!wmi_has_guid(WMID_GUID2) && interface) {
|
} else if (!wmi_has_guid(WMID_GUID2) && interface) {
|
||||||
printk(ACER_ERR "No WMID device detection method found\n");
|
pr_err("No WMID device detection method found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1691,8 +1720,7 @@ static int __init acer_wmi_init(void)
|
||||||
interface = &AMW0_interface;
|
interface = &AMW0_interface;
|
||||||
|
|
||||||
if (ACPI_FAILURE(AMW0_set_capabilities())) {
|
if (ACPI_FAILURE(AMW0_set_capabilities())) {
|
||||||
printk(ACER_ERR "Unable to detect available AMW0 "
|
pr_err("Unable to detect available AMW0 devices\n");
|
||||||
"devices\n");
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1701,8 +1729,7 @@ static int __init acer_wmi_init(void)
|
||||||
AMW0_find_mailled();
|
AMW0_find_mailled();
|
||||||
|
|
||||||
if (!interface) {
|
if (!interface) {
|
||||||
printk(ACER_INFO "No or unsupported WMI interface, unable to "
|
pr_err("No or unsupported WMI interface, unable to load\n");
|
||||||
"load\n");
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1710,22 +1737,22 @@ static int __init acer_wmi_init(void)
|
||||||
|
|
||||||
if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
|
if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
|
||||||
interface->capability &= ~ACER_CAP_BRIGHTNESS;
|
interface->capability &= ~ACER_CAP_BRIGHTNESS;
|
||||||
printk(ACER_INFO "Brightness must be controlled by "
|
pr_info("Brightness must be controlled by "
|
||||||
"generic video driver\n");
|
"generic video driver\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wmi_has_guid(WMID_GUID3)) {
|
if (wmi_has_guid(WMID_GUID3)) {
|
||||||
if (ec_raw_mode) {
|
if (ec_raw_mode) {
|
||||||
if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
|
if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
|
||||||
printk(ACER_ERR "Cannot enable EC raw mode\n");
|
pr_err("Cannot enable EC raw mode\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
} else if (ACPI_FAILURE(acer_wmi_enable_lm())) {
|
} else if (ACPI_FAILURE(acer_wmi_enable_lm())) {
|
||||||
printk(ACER_ERR "Cannot enable Launch Manager mode\n");
|
pr_err("Cannot enable Launch Manager mode\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
} else if (ec_raw_mode) {
|
} else if (ec_raw_mode) {
|
||||||
printk(ACER_INFO "No WMID EC raw mode enable method\n");
|
pr_info("No WMID EC raw mode enable method\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wmi_has_guid(ACERWMID_EVENT_GUID)) {
|
if (wmi_has_guid(ACERWMID_EVENT_GUID)) {
|
||||||
|
@ -1736,7 +1763,7 @@ static int __init acer_wmi_init(void)
|
||||||
|
|
||||||
err = platform_driver_register(&acer_platform_driver);
|
err = platform_driver_register(&acer_platform_driver);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(ACER_ERR "Unable to register platform driver.\n");
|
pr_err("Unable to register platform driver.\n");
|
||||||
goto error_platform_register;
|
goto error_platform_register;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1791,7 +1818,7 @@ static void __exit acer_wmi_exit(void)
|
||||||
platform_device_unregister(acer_platform_device);
|
platform_device_unregister(acer_platform_device);
|
||||||
platform_driver_unregister(&acer_platform_driver);
|
platform_driver_unregister(&acer_platform_driver);
|
||||||
|
|
||||||
printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
|
pr_info("Acer Laptop WMI Extras unloaded\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
* John Belmonte - ACPI code for Toshiba laptop was a good starting point.
|
* John Belmonte - ACPI code for Toshiba laptop was a good starting point.
|
||||||
* Eric Burghard - LED display support for W1N
|
* Eric Burghard - LED display support for W1N
|
||||||
* Josh Green - Light Sens support
|
* Josh Green - Light Sens support
|
||||||
* Thomas Tuttle - His first patch for led support was very helpfull
|
* Thomas Tuttle - His first patch for led support was very helpful
|
||||||
* Sam Lin - GPS support
|
* Sam Lin - GPS support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@
|
||||||
#include <linux/input/sparse-keymap.h>
|
#include <linux/input/sparse-keymap.h>
|
||||||
#include <linux/rfkill.h>
|
#include <linux/rfkill.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
#include <acpi/acpi_drivers.h>
|
#include <acpi/acpi_drivers.h>
|
||||||
#include <acpi/acpi_bus.h>
|
#include <acpi/acpi_bus.h>
|
||||||
|
|
||||||
|
@ -157,46 +158,9 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot "
|
||||||
#define METHOD_BRIGHTNESS_SET "SPLV"
|
#define METHOD_BRIGHTNESS_SET "SPLV"
|
||||||
#define METHOD_BRIGHTNESS_GET "GPLV"
|
#define METHOD_BRIGHTNESS_GET "GPLV"
|
||||||
|
|
||||||
/* Backlight */
|
|
||||||
static acpi_handle lcd_switch_handle;
|
|
||||||
static char *lcd_switch_paths[] = {
|
|
||||||
"\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */
|
|
||||||
"\\_SB.PCI0.ISA.EC0._Q10", /* A1x */
|
|
||||||
"\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */
|
|
||||||
"\\_SB.PCI0.PX40.EC0.Q10", /* M1A */
|
|
||||||
"\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
|
|
||||||
"\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */
|
|
||||||
"\\_SB.PCI0.PX40.Q10", /* S1x */
|
|
||||||
"\\Q10"}; /* A2x, L2D, L3D, M2E */
|
|
||||||
|
|
||||||
/* Display */
|
/* Display */
|
||||||
#define METHOD_SWITCH_DISPLAY "SDSP"
|
#define METHOD_SWITCH_DISPLAY "SDSP"
|
||||||
|
|
||||||
static acpi_handle display_get_handle;
|
|
||||||
static char *display_get_paths[] = {
|
|
||||||
/* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */
|
|
||||||
"\\_SB.PCI0.P0P1.VGA.GETD",
|
|
||||||
/* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */
|
|
||||||
"\\_SB.PCI0.P0P2.VGA.GETD",
|
|
||||||
/* A6V A6Q */
|
|
||||||
"\\_SB.PCI0.P0P3.VGA.GETD",
|
|
||||||
/* A6T, A6M */
|
|
||||||
"\\_SB.PCI0.P0PA.VGA.GETD",
|
|
||||||
/* L3C */
|
|
||||||
"\\_SB.PCI0.PCI1.VGAC.NMAP",
|
|
||||||
/* Z96F */
|
|
||||||
"\\_SB.PCI0.VGA.GETD",
|
|
||||||
/* A2D */
|
|
||||||
"\\ACTD",
|
|
||||||
/* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
|
|
||||||
"\\ADVG",
|
|
||||||
/* P30 */
|
|
||||||
"\\DNXT",
|
|
||||||
/* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
|
|
||||||
"\\INFB",
|
|
||||||
/* A3F A6F A3N A3L M6N W3N W6A */
|
|
||||||
"\\SSTE"};
|
|
||||||
|
|
||||||
#define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */
|
#define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */
|
||||||
#define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */
|
#define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */
|
||||||
|
|
||||||
|
@ -246,7 +210,6 @@ struct asus_laptop {
|
||||||
|
|
||||||
int wireless_status;
|
int wireless_status;
|
||||||
bool have_rsts;
|
bool have_rsts;
|
||||||
int lcd_state;
|
|
||||||
|
|
||||||
struct rfkill *gps_rfkill;
|
struct rfkill *gps_rfkill;
|
||||||
|
|
||||||
|
@ -559,48 +522,6 @@ error:
|
||||||
/*
|
/*
|
||||||
* Backlight device
|
* Backlight device
|
||||||
*/
|
*/
|
||||||
static int asus_lcd_status(struct asus_laptop *asus)
|
|
||||||
{
|
|
||||||
return asus->lcd_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asus_lcd_set(struct asus_laptop *asus, int value)
|
|
||||||
{
|
|
||||||
int lcd = 0;
|
|
||||||
acpi_status status = 0;
|
|
||||||
|
|
||||||
lcd = !!value;
|
|
||||||
|
|
||||||
if (lcd == asus_lcd_status(asus))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!lcd_switch_handle)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
status = acpi_evaluate_object(lcd_switch_handle,
|
|
||||||
NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
pr_warning("Error switching LCD\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
asus->lcd_state = lcd;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lcd_blank(struct asus_laptop *asus, int blank)
|
|
||||||
{
|
|
||||||
struct backlight_device *bd = asus->backlight_device;
|
|
||||||
|
|
||||||
asus->lcd_state = (blank == FB_BLANK_UNBLANK);
|
|
||||||
|
|
||||||
if (bd) {
|
|
||||||
bd->props.power = blank;
|
|
||||||
backlight_update_status(bd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int asus_read_brightness(struct backlight_device *bd)
|
static int asus_read_brightness(struct backlight_device *bd)
|
||||||
{
|
{
|
||||||
struct asus_laptop *asus = bl_get_data(bd);
|
struct asus_laptop *asus = bl_get_data(bd);
|
||||||
|
@ -628,16 +549,9 @@ static int asus_set_brightness(struct backlight_device *bd, int value)
|
||||||
|
|
||||||
static int update_bl_status(struct backlight_device *bd)
|
static int update_bl_status(struct backlight_device *bd)
|
||||||
{
|
{
|
||||||
struct asus_laptop *asus = bl_get_data(bd);
|
|
||||||
int rv;
|
|
||||||
int value = bd->props.brightness;
|
int value = bd->props.brightness;
|
||||||
|
|
||||||
rv = asus_set_brightness(bd, value);
|
return asus_set_brightness(bd, value);
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
|
|
||||||
return asus_lcd_set(asus, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct backlight_ops asusbl_ops = {
|
static const struct backlight_ops asusbl_ops = {
|
||||||
|
@ -661,8 +575,7 @@ static int asus_backlight_init(struct asus_laptop *asus)
|
||||||
struct backlight_properties props;
|
struct backlight_properties props;
|
||||||
|
|
||||||
if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
|
if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
|
||||||
acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) ||
|
acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL))
|
||||||
!lcd_switch_handle)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
memset(&props, 0, sizeof(struct backlight_properties));
|
memset(&props, 0, sizeof(struct backlight_properties));
|
||||||
|
@ -971,41 +884,6 @@ static void asus_set_display(struct asus_laptop *asus, int value)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_display(struct asus_laptop *asus)
|
|
||||||
{
|
|
||||||
unsigned long long value = 0;
|
|
||||||
acpi_status rv = AE_OK;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In most of the case, we know how to set the display, but sometime
|
|
||||||
* we can't read it
|
|
||||||
*/
|
|
||||||
if (display_get_handle) {
|
|
||||||
rv = acpi_evaluate_integer(display_get_handle, NULL,
|
|
||||||
NULL, &value);
|
|
||||||
if (ACPI_FAILURE(rv))
|
|
||||||
pr_warning("Error reading display status\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
value &= 0x0F; /* needed for some models, shouldn't hurt others */
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now, *this* one could be more user-friendly, but so far, no-one has
|
|
||||||
* complained. The significance of bits is the same as in store_disp()
|
|
||||||
*/
|
|
||||||
static ssize_t show_disp(struct device *dev,
|
|
||||||
struct device_attribute *attr, char *buf)
|
|
||||||
{
|
|
||||||
struct asus_laptop *asus = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
if (!display_get_handle)
|
|
||||||
return -ENODEV;
|
|
||||||
return sprintf(buf, "%d\n", read_display(asus));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Experimental support for display switching. As of now: 1 should activate
|
* Experimental support for display switching. As of now: 1 should activate
|
||||||
* the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
|
* the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
|
||||||
|
@ -1247,15 +1125,6 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
|
||||||
struct asus_laptop *asus = acpi_driver_data(device);
|
struct asus_laptop *asus = acpi_driver_data(device);
|
||||||
u16 count;
|
u16 count;
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to tell the backlight device when the backlight power is
|
|
||||||
* switched
|
|
||||||
*/
|
|
||||||
if (event == ATKD_LCD_ON)
|
|
||||||
lcd_blank(asus, FB_BLANK_UNBLANK);
|
|
||||||
else if (event == ATKD_LCD_OFF)
|
|
||||||
lcd_blank(asus, FB_BLANK_POWERDOWN);
|
|
||||||
|
|
||||||
/* TODO Find a better way to handle events count. */
|
/* TODO Find a better way to handle events count. */
|
||||||
count = asus->event_count[event % 128]++;
|
count = asus->event_count[event % 128]++;
|
||||||
acpi_bus_generate_proc_event(asus->device, event, count);
|
acpi_bus_generate_proc_event(asus->device, event, count);
|
||||||
|
@ -1282,7 +1151,7 @@ static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,
|
||||||
show_bluetooth, store_bluetooth);
|
show_bluetooth, store_bluetooth);
|
||||||
static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax);
|
static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax);
|
||||||
static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan);
|
static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan);
|
||||||
static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp);
|
static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp);
|
||||||
static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
|
static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
|
||||||
static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
|
static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
|
||||||
static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
|
static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
|
||||||
|
@ -1393,26 +1262,6 @@ static struct platform_driver platform_driver = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int asus_handle_init(char *name, acpi_handle * handle,
|
|
||||||
char **paths, int num_paths)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
for (i = 0; i < num_paths; i++) {
|
|
||||||
status = acpi_get_handle(NULL, paths[i], handle);
|
|
||||||
if (ACPI_SUCCESS(status))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*handle = NULL;
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ASUS_HANDLE_INIT(object) \
|
|
||||||
asus_handle_init(#object, &object##_handle, object##_paths, \
|
|
||||||
ARRAY_SIZE(object##_paths))
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is used to initialize the context with right values. In this
|
* This function is used to initialize the context with right values. In this
|
||||||
* method, we can make all the detection we want, and modify the asus_laptop
|
* method, we can make all the detection we want, and modify the asus_laptop
|
||||||
|
@ -1498,10 +1347,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
|
||||||
if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
|
if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
|
||||||
asus->have_rsts = true;
|
asus->have_rsts = true;
|
||||||
|
|
||||||
/* Scheduled for removal */
|
|
||||||
ASUS_HANDLE_INIT(lcd_switch);
|
|
||||||
ASUS_HANDLE_INIT(display_get);
|
|
||||||
|
|
||||||
kfree(model);
|
kfree(model);
|
||||||
|
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
@ -1553,10 +1398,23 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
|
||||||
asus_als_level(asus, asus->light_level);
|
asus_als_level(asus, asus->light_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
asus->lcd_state = 1; /* LCD should be on when the module load */
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __devinit asus_dmi_check(void)
|
||||||
|
{
|
||||||
|
const char *model;
|
||||||
|
|
||||||
|
model = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||||
|
if (!model)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* On L1400B WLED control the sound card, don't mess with it ... */
|
||||||
|
if (strncmp(model, "L1400B", 6) == 0) {
|
||||||
|
wlan_status = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool asus_device_present;
|
static bool asus_device_present;
|
||||||
|
|
||||||
static int __devinit asus_acpi_add(struct acpi_device *device)
|
static int __devinit asus_acpi_add(struct acpi_device *device)
|
||||||
|
@ -1575,6 +1433,8 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
|
||||||
device->driver_data = asus;
|
device->driver_data = asus;
|
||||||
asus->device = device;
|
asus->device = device;
|
||||||
|
|
||||||
|
asus_dmi_check();
|
||||||
|
|
||||||
result = asus_acpi_init(asus);
|
result = asus_acpi_init(asus);
|
||||||
if (result)
|
if (result)
|
||||||
goto fail_platform;
|
goto fail_platform;
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Asus Notebooks WMI hotkey driver
|
||||||
|
*
|
||||||
|
* Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/input/sparse-keymap.h>
|
||||||
|
|
||||||
|
#include "asus-wmi.h"
|
||||||
|
|
||||||
|
#define ASUS_NB_WMI_FILE "asus-nb-wmi"
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>");
|
||||||
|
MODULE_DESCRIPTION("Asus Notebooks WMI Hotkey Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
|
||||||
|
|
||||||
|
MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
|
||||||
|
|
||||||
|
static const struct key_entry asus_nb_wmi_keymap[] = {
|
||||||
|
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
|
||||||
|
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
||||||
|
{ KE_KEY, 0x32, { KEY_MUTE } },
|
||||||
|
{ KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */
|
||||||
|
{ KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
|
||||||
|
{ KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
|
||||||
|
{ KE_KEY, 0x41, { KEY_NEXTSONG } },
|
||||||
|
{ KE_KEY, 0x43, { KEY_STOPCD } },
|
||||||
|
{ KE_KEY, 0x45, { KEY_PLAYPAUSE } },
|
||||||
|
{ KE_KEY, 0x4c, { KEY_MEDIA } },
|
||||||
|
{ KE_KEY, 0x50, { KEY_EMAIL } },
|
||||||
|
{ KE_KEY, 0x51, { KEY_WWW } },
|
||||||
|
{ KE_KEY, 0x55, { KEY_CALC } },
|
||||||
|
{ KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */
|
||||||
|
{ KE_KEY, 0x5D, { KEY_WLAN } },
|
||||||
|
{ KE_KEY, 0x5E, { KEY_WLAN } },
|
||||||
|
{ KE_KEY, 0x5F, { KEY_WLAN } },
|
||||||
|
{ KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
|
||||||
|
{ KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
|
||||||
|
{ KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
|
||||||
|
{ KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
|
||||||
|
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
|
||||||
|
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } },
|
||||||
|
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } },
|
||||||
|
{ KE_KEY, 0x82, { KEY_CAMERA } },
|
||||||
|
{ KE_KEY, 0x88, { KEY_RFKILL } },
|
||||||
|
{ KE_KEY, 0x8A, { KEY_PROG1 } },
|
||||||
|
{ KE_KEY, 0x95, { KEY_MEDIA } },
|
||||||
|
{ KE_KEY, 0x99, { KEY_PHONE } },
|
||||||
|
{ KE_KEY, 0xb5, { KEY_CALC } },
|
||||||
|
{ KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
|
||||||
|
{ KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
|
||||||
|
{ KE_END, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct asus_wmi_driver asus_nb_wmi_driver = {
|
||||||
|
.name = ASUS_NB_WMI_FILE,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.event_guid = ASUS_NB_WMI_EVENT_GUID,
|
||||||
|
.keymap = asus_nb_wmi_keymap,
|
||||||
|
.input_name = "Asus WMI hotkeys",
|
||||||
|
.input_phys = ASUS_NB_WMI_FILE "/input0",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int __init asus_nb_wmi_init(void)
|
||||||
|
{
|
||||||
|
return asus_wmi_register_driver(&asus_nb_wmi_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit asus_nb_wmi_exit(void)
|
||||||
|
{
|
||||||
|
asus_wmi_unregister_driver(&asus_nb_wmi_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(asus_nb_wmi_init);
|
||||||
|
module_exit(asus_nb_wmi_exit);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Asus PC WMI hotkey driver
|
||||||
|
*
|
||||||
|
* Copyright(C) 2010 Intel Corporation.
|
||||||
|
* Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
|
||||||
|
*
|
||||||
|
* Portions based on wistron_btns.c:
|
||||||
|
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
||||||
|
* Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
|
||||||
|
* Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASUS_WMI_H_
|
||||||
|
#define _ASUS_WMI_H_
|
||||||
|
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
struct module;
|
||||||
|
struct key_entry;
|
||||||
|
struct asus_wmi;
|
||||||
|
|
||||||
|
struct asus_wmi_driver {
|
||||||
|
bool hotplug_wireless;
|
||||||
|
|
||||||
|
const char *name;
|
||||||
|
struct module *owner;
|
||||||
|
|
||||||
|
const char *event_guid;
|
||||||
|
|
||||||
|
const struct key_entry *keymap;
|
||||||
|
const char *input_name;
|
||||||
|
const char *input_phys;
|
||||||
|
|
||||||
|
int (*probe) (struct platform_device *device);
|
||||||
|
void (*quirks) (struct asus_wmi_driver *driver);
|
||||||
|
|
||||||
|
struct platform_driver platform_driver;
|
||||||
|
struct platform_device *platform_device;
|
||||||
|
};
|
||||||
|
|
||||||
|
int asus_wmi_register_driver(struct asus_wmi_driver *driver);
|
||||||
|
void asus_wmi_unregister_driver(struct asus_wmi_driver *driver);
|
||||||
|
|
||||||
|
#endif /* !_ASUS_WMI_H_ */
|
|
@ -201,7 +201,7 @@ static bool extra_features;
|
||||||
* into 0x4F and read a few bytes from the output, like so:
|
* into 0x4F and read a few bytes from the output, like so:
|
||||||
* u8 writeData = 0x33;
|
* u8 writeData = 0x33;
|
||||||
* ec_transaction(0x4F, &writeData, 1, buffer, 32, 0);
|
* ec_transaction(0x4F, &writeData, 1, buffer, 32, 0);
|
||||||
* That address is labled "fan1 table information" in the service manual.
|
* That address is labelled "fan1 table information" in the service manual.
|
||||||
* It should be clear which value in 'buffer' changes). This seems to be
|
* It should be clear which value in 'buffer' changes). This seems to be
|
||||||
* related to fan speed. It isn't a proper 'realtime' fan speed value
|
* related to fan speed. It isn't a proper 'realtime' fan speed value
|
||||||
* though, because physically stopping or speeding up the fan doesn't
|
* though, because physically stopping or speeding up the fan doesn't
|
||||||
|
@ -275,7 +275,7 @@ static int set_backlight_level(int level)
|
||||||
|
|
||||||
ec_write(BACKLIGHT_LEVEL_ADDR, level);
|
ec_write(BACKLIGHT_LEVEL_ADDR, level);
|
||||||
|
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_backlight_level(void)
|
static int get_backlight_level(void)
|
||||||
|
@ -763,7 +763,7 @@ static int dmi_check_cb(const struct dmi_system_id *id)
|
||||||
printk(KERN_INFO DRIVER_NAME": Identified laptop model '%s'\n",
|
printk(KERN_INFO DRIVER_NAME": Identified laptop model '%s'\n",
|
||||||
id->ident);
|
id->ident);
|
||||||
extra_features = false;
|
extra_features = false;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dmi_check_cb_extra(const struct dmi_system_id *id)
|
static int dmi_check_cb_extra(const struct dmi_system_id *id)
|
||||||
|
@ -772,7 +772,7 @@ static int dmi_check_cb_extra(const struct dmi_system_id *id)
|
||||||
"enabling extra features\n",
|
"enabling extra features\n",
|
||||||
id->ident);
|
id->ident);
|
||||||
extra_features = true;
|
extra_features = true;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dmi_system_id __initdata compal_dmi_table[] = {
|
static struct dmi_system_id __initdata compal_dmi_table[] = {
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* WMI hotkeys support for Dell All-In-One series
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/input/sparse-keymap.h>
|
||||||
|
#include <acpi/acpi_drivers.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4"
|
||||||
|
#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8"
|
||||||
|
|
||||||
|
static const char *dell_wmi_aio_guids[] = {
|
||||||
|
EVENT_GUID1,
|
||||||
|
EVENT_GUID2,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_ALIAS("wmi:"EVENT_GUID1);
|
||||||
|
MODULE_ALIAS("wmi:"EVENT_GUID2);
|
||||||
|
|
||||||
|
static const struct key_entry dell_wmi_aio_keymap[] = {
|
||||||
|
{ KE_KEY, 0xc0, { KEY_VOLUMEUP } },
|
||||||
|
{ KE_KEY, 0xc1, { KEY_VOLUMEDOWN } },
|
||||||
|
{ KE_END, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct input_dev *dell_wmi_aio_input_dev;
|
||||||
|
|
||||||
|
static void dell_wmi_aio_notify(u32 value, void *context)
|
||||||
|
{
|
||||||
|
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
union acpi_object *obj;
|
||||||
|
acpi_status status;
|
||||||
|
|
||||||
|
status = wmi_get_event_data(value, &response);
|
||||||
|
if (status != AE_OK) {
|
||||||
|
pr_info("bad event status 0x%x\n", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = (union acpi_object *)response.pointer;
|
||||||
|
if (obj) {
|
||||||
|
unsigned int scancode;
|
||||||
|
|
||||||
|
switch (obj->type) {
|
||||||
|
case ACPI_TYPE_INTEGER:
|
||||||
|
/* Most All-In-One correctly return integer scancode */
|
||||||
|
scancode = obj->integer.value;
|
||||||
|
sparse_keymap_report_event(dell_wmi_aio_input_dev,
|
||||||
|
scancode, 1, true);
|
||||||
|
break;
|
||||||
|
case ACPI_TYPE_BUFFER:
|
||||||
|
/* Broken machines return the scancode in a buffer */
|
||||||
|
if (obj->buffer.pointer && obj->buffer.length > 0) {
|
||||||
|
scancode = obj->buffer.pointer[0];
|
||||||
|
sparse_keymap_report_event(
|
||||||
|
dell_wmi_aio_input_dev,
|
||||||
|
scancode, 1, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kfree(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init dell_wmi_aio_input_setup(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
dell_wmi_aio_input_dev = input_allocate_device();
|
||||||
|
|
||||||
|
if (!dell_wmi_aio_input_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys";
|
||||||
|
dell_wmi_aio_input_dev->phys = "wmi/input0";
|
||||||
|
dell_wmi_aio_input_dev->id.bustype = BUS_HOST;
|
||||||
|
|
||||||
|
err = sparse_keymap_setup(dell_wmi_aio_input_dev,
|
||||||
|
dell_wmi_aio_keymap, NULL);
|
||||||
|
if (err) {
|
||||||
|
pr_err("Unable to setup input device keymap\n");
|
||||||
|
goto err_free_dev;
|
||||||
|
}
|
||||||
|
err = input_register_device(dell_wmi_aio_input_dev);
|
||||||
|
if (err) {
|
||||||
|
pr_info("Unable to register input device\n");
|
||||||
|
goto err_free_keymap;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free_keymap:
|
||||||
|
sparse_keymap_free(dell_wmi_aio_input_dev);
|
||||||
|
err_free_dev:
|
||||||
|
input_free_device(dell_wmi_aio_input_dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *dell_wmi_aio_find(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; dell_wmi_aio_guids[i] != NULL; i++)
|
||||||
|
if (wmi_has_guid(dell_wmi_aio_guids[i]))
|
||||||
|
return dell_wmi_aio_guids[i];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init dell_wmi_aio_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
const char *guid;
|
||||||
|
|
||||||
|
guid = dell_wmi_aio_find();
|
||||||
|
if (!guid) {
|
||||||
|
pr_warning("No known WMI GUID found\n");
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dell_wmi_aio_input_setup();
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL);
|
||||||
|
if (err) {
|
||||||
|
pr_err("Unable to register notify handler - %d\n", err);
|
||||||
|
sparse_keymap_free(dell_wmi_aio_input_dev);
|
||||||
|
input_unregister_device(dell_wmi_aio_input_dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit dell_wmi_aio_exit(void)
|
||||||
|
{
|
||||||
|
const char *guid;
|
||||||
|
|
||||||
|
guid = dell_wmi_aio_find();
|
||||||
|
wmi_remove_notify_handler(guid);
|
||||||
|
sparse_keymap_free(dell_wmi_aio_input_dev);
|
||||||
|
input_unregister_device(dell_wmi_aio_input_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(dell_wmi_aio_init);
|
||||||
|
module_exit(dell_wmi_aio_exit);
|
|
@ -1322,7 +1322,7 @@ static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
|
||||||
{
|
{
|
||||||
int dummy;
|
int dummy;
|
||||||
|
|
||||||
/* Some BIOSes do not report cm although it is avaliable.
|
/* Some BIOSes do not report cm although it is available.
|
||||||
Check if cm_getv[cm] works and, if yes, assume cm should be set. */
|
Check if cm_getv[cm] works and, if yes, assume cm should be set. */
|
||||||
if (!(eeepc->cm_supported & (1 << cm))
|
if (!(eeepc->cm_supported & (1 << cm))
|
||||||
&& !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
|
&& !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Eee PC WMI hotkey driver
|
* Eee PC WMI hotkey driver
|
||||||
*
|
*
|
||||||
* Copyright(C) 2010 Intel Corporation.
|
* Copyright(C) 2010 Intel Corporation.
|
||||||
* Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
|
* Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
|
||||||
*
|
*
|
||||||
* Portions based on wistron_btns.c:
|
* Portions based on wistron_btns.c:
|
||||||
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
||||||
|
@ -29,841 +29,57 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/input/sparse-keymap.h>
|
#include <linux/input/sparse-keymap.h>
|
||||||
#include <linux/fb.h>
|
#include <linux/dmi.h>
|
||||||
#include <linux/backlight.h>
|
|
||||||
#include <linux/leds.h>
|
|
||||||
#include <linux/rfkill.h>
|
|
||||||
#include <linux/debugfs.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <acpi/acpi_bus.h>
|
#include <acpi/acpi_bus.h>
|
||||||
#include <acpi/acpi_drivers.h>
|
|
||||||
|
#include "asus-wmi.h"
|
||||||
|
|
||||||
#define EEEPC_WMI_FILE "eeepc-wmi"
|
#define EEEPC_WMI_FILE "eeepc-wmi"
|
||||||
|
|
||||||
MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
|
MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>");
|
||||||
MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
|
MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */
|
#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */
|
||||||
|
|
||||||
#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
|
#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
|
||||||
#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
|
|
||||||
|
|
||||||
MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID);
|
MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID);
|
||||||
MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
|
|
||||||
|
|
||||||
#define NOTIFY_BRNUP_MIN 0x11
|
static bool hotplug_wireless;
|
||||||
#define NOTIFY_BRNUP_MAX 0x1f
|
|
||||||
#define NOTIFY_BRNDOWN_MIN 0x20
|
|
||||||
#define NOTIFY_BRNDOWN_MAX 0x2e
|
|
||||||
|
|
||||||
#define EEEPC_WMI_METHODID_DEVS 0x53564544
|
module_param(hotplug_wireless, bool, 0444);
|
||||||
#define EEEPC_WMI_METHODID_DSTS 0x53544344
|
MODULE_PARM_DESC(hotplug_wireless,
|
||||||
#define EEEPC_WMI_METHODID_CFVS 0x53564643
|
"Enable hotplug for wireless device. "
|
||||||
|
"If your laptop needs that, please report to "
|
||||||
#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
|
"acpi4asus-user@lists.sourceforge.net.");
|
||||||
#define EEEPC_WMI_DEVID_TPDLED 0x00100011
|
|
||||||
#define EEEPC_WMI_DEVID_WLAN 0x00010011
|
|
||||||
#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013
|
|
||||||
#define EEEPC_WMI_DEVID_WWAN3G 0x00010019
|
|
||||||
|
|
||||||
static const struct key_entry eeepc_wmi_keymap[] = {
|
static const struct key_entry eeepc_wmi_keymap[] = {
|
||||||
/* Sleep already handled via generic ACPI code */
|
/* Sleep already handled via generic ACPI code */
|
||||||
{ KE_KEY, 0x5d, { KEY_WLAN } },
|
|
||||||
{ KE_KEY, 0x32, { KEY_MUTE } },
|
|
||||||
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
|
||||||
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
|
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
|
||||||
{ KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
|
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
||||||
{ KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
|
{ KE_KEY, 0x32, { KEY_MUTE } },
|
||||||
|
{ KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */
|
||||||
|
{ KE_KEY, 0x5d, { KEY_WLAN } },
|
||||||
|
{ KE_KEY, 0x6b, { KEY_TOUCHPAD_TOGGLE } }, /* Toggle Touchpad */
|
||||||
|
{ KE_KEY, 0x82, { KEY_CAMERA } },
|
||||||
|
{ KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } },
|
||||||
|
{ KE_KEY, 0x88, { KEY_WLAN } },
|
||||||
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
|
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
|
||||||
{ KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
|
{ KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
|
||||||
{ KE_KEY, 0xe1, { KEY_F14 } },
|
{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
|
||||||
{ KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
|
{ KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
|
||||||
{ KE_KEY, 0xe0, { KEY_PROG1 } },
|
{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
|
||||||
{ KE_KEY, 0x5c, { KEY_F15 } },
|
{ KE_KEY, 0xec, { KEY_CAMERA_UP } },
|
||||||
|
{ KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
|
||||||
|
{ KE_KEY, 0xee, { KEY_CAMERA_LEFT } },
|
||||||
|
{ KE_KEY, 0xef, { KEY_CAMERA_RIGHT } },
|
||||||
{ KE_END, 0},
|
{ KE_END, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bios_args {
|
static acpi_status eeepc_wmi_parse_device(acpi_handle handle, u32 level,
|
||||||
u32 dev_id;
|
|
||||||
u32 ctrl_param;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* eeepc-wmi/ - debugfs root directory
|
|
||||||
* dev_id - current dev_id
|
|
||||||
* ctrl_param - current ctrl_param
|
|
||||||
* devs - call DEVS(dev_id, ctrl_param) and print result
|
|
||||||
* dsts - call DSTS(dev_id) and print result
|
|
||||||
*/
|
|
||||||
struct eeepc_wmi_debug {
|
|
||||||
struct dentry *root;
|
|
||||||
u32 dev_id;
|
|
||||||
u32 ctrl_param;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct eeepc_wmi {
|
|
||||||
struct input_dev *inputdev;
|
|
||||||
struct backlight_device *backlight_device;
|
|
||||||
struct platform_device *platform_device;
|
|
||||||
|
|
||||||
struct led_classdev tpd_led;
|
|
||||||
int tpd_led_wk;
|
|
||||||
struct workqueue_struct *led_workqueue;
|
|
||||||
struct work_struct tpd_led_work;
|
|
||||||
|
|
||||||
struct rfkill *wlan_rfkill;
|
|
||||||
struct rfkill *bluetooth_rfkill;
|
|
||||||
struct rfkill *wwan3g_rfkill;
|
|
||||||
|
|
||||||
struct eeepc_wmi_debug debug;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
|
|
||||||
static struct platform_device *platform_device;
|
|
||||||
|
|
||||||
static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
eeepc->inputdev = input_allocate_device();
|
|
||||||
if (!eeepc->inputdev)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
eeepc->inputdev->name = "Eee PC WMI hotkeys";
|
|
||||||
eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
|
|
||||||
eeepc->inputdev->id.bustype = BUS_HOST;
|
|
||||||
eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
|
|
||||||
|
|
||||||
err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
|
|
||||||
if (err)
|
|
||||||
goto err_free_dev;
|
|
||||||
|
|
||||||
err = input_register_device(eeepc->inputdev);
|
|
||||||
if (err)
|
|
||||||
goto err_free_keymap;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_free_keymap:
|
|
||||||
sparse_keymap_free(eeepc->inputdev);
|
|
||||||
err_free_dev:
|
|
||||||
input_free_device(eeepc->inputdev);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
if (eeepc->inputdev) {
|
|
||||||
sparse_keymap_free(eeepc->inputdev);
|
|
||||||
input_unregister_device(eeepc->inputdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
eeepc->inputdev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval)
|
|
||||||
{
|
|
||||||
struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id };
|
|
||||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
||||||
union acpi_object *obj;
|
|
||||||
acpi_status status;
|
|
||||||
u32 tmp;
|
|
||||||
|
|
||||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
|
|
||||||
1, EEEPC_WMI_METHODID_DSTS, &input, &output);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
obj = (union acpi_object *)output.pointer;
|
|
||||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
|
||||||
tmp = (u32)obj->integer.value;
|
|
||||||
else
|
|
||||||
tmp = 0;
|
|
||||||
|
|
||||||
if (retval)
|
|
||||||
*retval = tmp;
|
|
||||||
|
|
||||||
kfree(obj);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
|
|
||||||
u32 *retval)
|
|
||||||
{
|
|
||||||
struct bios_args args = {
|
|
||||||
.dev_id = dev_id,
|
|
||||||
.ctrl_param = ctrl_param,
|
|
||||||
};
|
|
||||||
struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
if (!retval) {
|
|
||||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
|
|
||||||
EEEPC_WMI_METHODID_DEVS,
|
|
||||||
&input, NULL);
|
|
||||||
} else {
|
|
||||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
||||||
union acpi_object *obj;
|
|
||||||
u32 tmp;
|
|
||||||
|
|
||||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
|
|
||||||
EEEPC_WMI_METHODID_DEVS,
|
|
||||||
&input, &output);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
obj = (union acpi_object *)output.pointer;
|
|
||||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
|
||||||
tmp = (u32)obj->integer.value;
|
|
||||||
else
|
|
||||||
tmp = 0;
|
|
||||||
|
|
||||||
*retval = tmp;
|
|
||||||
|
|
||||||
kfree(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LEDs
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* These functions actually update the LED's, and are called from a
|
|
||||||
* workqueue. By doing this as separate work rather than when the LED
|
|
||||||
* subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
|
|
||||||
* potentially bad time, such as a timer interrupt.
|
|
||||||
*/
|
|
||||||
static void tpd_led_update(struct work_struct *work)
|
|
||||||
{
|
|
||||||
int ctrl_param;
|
|
||||||
struct eeepc_wmi *eeepc;
|
|
||||||
|
|
||||||
eeepc = container_of(work, struct eeepc_wmi, tpd_led_work);
|
|
||||||
|
|
||||||
ctrl_param = eeepc->tpd_led_wk;
|
|
||||||
eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tpd_led_set(struct led_classdev *led_cdev,
|
|
||||||
enum led_brightness value)
|
|
||||||
{
|
|
||||||
struct eeepc_wmi *eeepc;
|
|
||||||
|
|
||||||
eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
|
|
||||||
|
|
||||||
eeepc->tpd_led_wk = !!value;
|
|
||||||
queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_tpd_state(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
u32 retval;
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -1;
|
|
||||||
else if (!retval || retval == 0x00060000)
|
|
||||||
/*
|
|
||||||
* if touchpad led is present, DSTS will set some bits,
|
|
||||||
* usually 0x00020000.
|
|
||||||
* 0x00060000 means that the device is not supported
|
|
||||||
*/
|
|
||||||
return -ENODEV;
|
|
||||||
else
|
|
||||||
/* Status is stored in the first bit */
|
|
||||||
return retval & 0x1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
|
|
||||||
{
|
|
||||||
struct eeepc_wmi *eeepc;
|
|
||||||
|
|
||||||
eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
|
|
||||||
|
|
||||||
return read_tpd_state(eeepc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (read_tpd_state(eeepc) < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
|
|
||||||
if (!eeepc->led_workqueue)
|
|
||||||
return -ENOMEM;
|
|
||||||
INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
|
|
||||||
|
|
||||||
eeepc->tpd_led.name = "eeepc::touchpad";
|
|
||||||
eeepc->tpd_led.brightness_set = tpd_led_set;
|
|
||||||
eeepc->tpd_led.brightness_get = tpd_led_get;
|
|
||||||
eeepc->tpd_led.max_brightness = 1;
|
|
||||||
|
|
||||||
rv = led_classdev_register(&eeepc->platform_device->dev,
|
|
||||||
&eeepc->tpd_led);
|
|
||||||
if (rv) {
|
|
||||||
destroy_workqueue(eeepc->led_workqueue);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
if (eeepc->tpd_led.dev)
|
|
||||||
led_classdev_unregister(&eeepc->tpd_led);
|
|
||||||
if (eeepc->led_workqueue)
|
|
||||||
destroy_workqueue(eeepc->led_workqueue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rfkill devices
|
|
||||||
*/
|
|
||||||
static int eeepc_rfkill_set(void *data, bool blocked)
|
|
||||||
{
|
|
||||||
int dev_id = (unsigned long)data;
|
|
||||||
u32 ctrl_param = !blocked;
|
|
||||||
|
|
||||||
return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
|
|
||||||
{
|
|
||||||
int dev_id = (unsigned long)data;
|
|
||||||
u32 retval;
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
status = eeepc_wmi_get_devstate(dev_id, &retval);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return ;
|
|
||||||
|
|
||||||
rfkill_set_sw_state(rfkill, !(retval & 0x1));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct rfkill_ops eeepc_rfkill_ops = {
|
|
||||||
.set_block = eeepc_rfkill_set,
|
|
||||||
.query = eeepc_rfkill_query,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
|
|
||||||
struct rfkill **rfkill,
|
|
||||||
const char *name,
|
|
||||||
enum rfkill_type type, int dev_id)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
u32 retval;
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
status = eeepc_wmi_get_devstate(dev_id, &retval);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* If the device is present, DSTS will always set some bits
|
|
||||||
* 0x00070000 - 1110000000000000000 - device supported
|
|
||||||
* 0x00060000 - 1100000000000000000 - not supported
|
|
||||||
* 0x00020000 - 0100000000000000000 - device supported
|
|
||||||
* 0x00010000 - 0010000000000000000 - not supported / special mode ?
|
|
||||||
*/
|
|
||||||
if (!retval || retval == 0x00060000)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
*rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
|
|
||||||
&eeepc_rfkill_ops, (void *)(long)dev_id);
|
|
||||||
|
|
||||||
if (!*rfkill)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
rfkill_init_sw_state(*rfkill, !(retval & 0x1));
|
|
||||||
result = rfkill_register(*rfkill);
|
|
||||||
if (result) {
|
|
||||||
rfkill_destroy(*rfkill);
|
|
||||||
*rfkill = NULL;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
if (eeepc->wlan_rfkill) {
|
|
||||||
rfkill_unregister(eeepc->wlan_rfkill);
|
|
||||||
rfkill_destroy(eeepc->wlan_rfkill);
|
|
||||||
eeepc->wlan_rfkill = NULL;
|
|
||||||
}
|
|
||||||
if (eeepc->bluetooth_rfkill) {
|
|
||||||
rfkill_unregister(eeepc->bluetooth_rfkill);
|
|
||||||
rfkill_destroy(eeepc->bluetooth_rfkill);
|
|
||||||
eeepc->bluetooth_rfkill = NULL;
|
|
||||||
}
|
|
||||||
if (eeepc->wwan3g_rfkill) {
|
|
||||||
rfkill_unregister(eeepc->wwan3g_rfkill);
|
|
||||||
rfkill_destroy(eeepc->wwan3g_rfkill);
|
|
||||||
eeepc->wwan3g_rfkill = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
|
|
||||||
"eeepc-wlan", RFKILL_TYPE_WLAN,
|
|
||||||
EEEPC_WMI_DEVID_WLAN);
|
|
||||||
|
|
||||||
if (result && result != -ENODEV)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
|
|
||||||
"eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
|
|
||||||
EEEPC_WMI_DEVID_BLUETOOTH);
|
|
||||||
|
|
||||||
if (result && result != -ENODEV)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
|
|
||||||
"eeepc-wwan3g", RFKILL_TYPE_WWAN,
|
|
||||||
EEEPC_WMI_DEVID_WWAN3G);
|
|
||||||
|
|
||||||
if (result && result != -ENODEV)
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
if (result && result != -ENODEV)
|
|
||||||
eeepc_wmi_rfkill_exit(eeepc);
|
|
||||||
|
|
||||||
if (result == -ENODEV)
|
|
||||||
result = 0;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Backlight
|
|
||||||
*/
|
|
||||||
static int read_brightness(struct backlight_device *bd)
|
|
||||||
{
|
|
||||||
u32 retval;
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return retval & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int update_bl_status(struct backlight_device *bd)
|
|
||||||
{
|
|
||||||
|
|
||||||
u32 ctrl_param;
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
ctrl_param = bd->props.brightness;
|
|
||||||
|
|
||||||
status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
|
|
||||||
ctrl_param, NULL);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct backlight_ops eeepc_wmi_bl_ops = {
|
|
||||||
.get_brightness = read_brightness,
|
|
||||||
.update_status = update_bl_status,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
|
|
||||||
{
|
|
||||||
struct backlight_device *bd = eeepc->backlight_device;
|
|
||||||
int old = bd->props.brightness;
|
|
||||||
int new = old;
|
|
||||||
|
|
||||||
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
|
|
||||||
new = code - NOTIFY_BRNUP_MIN + 1;
|
|
||||||
else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
|
|
||||||
new = code - NOTIFY_BRNDOWN_MIN;
|
|
||||||
|
|
||||||
bd->props.brightness = new;
|
|
||||||
backlight_update_status(bd);
|
|
||||||
backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
|
|
||||||
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
struct backlight_device *bd;
|
|
||||||
struct backlight_properties props;
|
|
||||||
|
|
||||||
memset(&props, 0, sizeof(struct backlight_properties));
|
|
||||||
props.max_brightness = 15;
|
|
||||||
bd = backlight_device_register(EEEPC_WMI_FILE,
|
|
||||||
&eeepc->platform_device->dev, eeepc,
|
|
||||||
&eeepc_wmi_bl_ops, &props);
|
|
||||||
if (IS_ERR(bd)) {
|
|
||||||
pr_err("Could not register backlight device\n");
|
|
||||||
return PTR_ERR(bd);
|
|
||||||
}
|
|
||||||
|
|
||||||
eeepc->backlight_device = bd;
|
|
||||||
|
|
||||||
bd->props.brightness = read_brightness(bd);
|
|
||||||
bd->props.power = FB_BLANK_UNBLANK;
|
|
||||||
backlight_update_status(bd);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
if (eeepc->backlight_device)
|
|
||||||
backlight_device_unregister(eeepc->backlight_device);
|
|
||||||
|
|
||||||
eeepc->backlight_device = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eeepc_wmi_notify(u32 value, void *context)
|
|
||||||
{
|
|
||||||
struct eeepc_wmi *eeepc = context;
|
|
||||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
||||||
union acpi_object *obj;
|
|
||||||
acpi_status status;
|
|
||||||
int code;
|
|
||||||
int orig_code;
|
|
||||||
|
|
||||||
status = wmi_get_event_data(value, &response);
|
|
||||||
if (status != AE_OK) {
|
|
||||||
pr_err("bad event status 0x%x\n", status);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = (union acpi_object *)response.pointer;
|
|
||||||
|
|
||||||
if (obj && obj->type == ACPI_TYPE_INTEGER) {
|
|
||||||
code = obj->integer.value;
|
|
||||||
orig_code = code;
|
|
||||||
|
|
||||||
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
|
|
||||||
code = NOTIFY_BRNUP_MIN;
|
|
||||||
else if (code >= NOTIFY_BRNDOWN_MIN &&
|
|
||||||
code <= NOTIFY_BRNDOWN_MAX)
|
|
||||||
code = NOTIFY_BRNDOWN_MIN;
|
|
||||||
|
|
||||||
if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
|
|
||||||
if (!acpi_video_backlight_support())
|
|
||||||
eeepc_wmi_backlight_notify(eeepc, orig_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sparse_keymap_report_event(eeepc->inputdev,
|
|
||||||
code, 1, true))
|
|
||||||
pr_info("Unknown key %x pressed\n", code);
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
|
|
||||||
const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
int value;
|
|
||||||
struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
if (!count || sscanf(buf, "%i", &value) != 1)
|
|
||||||
return -EINVAL;
|
|
||||||
if (value < 0 || value > 2)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
|
|
||||||
1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -EIO;
|
|
||||||
else
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
|
|
||||||
|
|
||||||
static struct attribute *platform_attributes[] = {
|
|
||||||
&dev_attr_cpufv.attr,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct attribute_group platform_attribute_group = {
|
|
||||||
.attrs = platform_attributes
|
|
||||||
};
|
|
||||||
|
|
||||||
static void eeepc_wmi_sysfs_exit(struct platform_device *device)
|
|
||||||
{
|
|
||||||
sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int eeepc_wmi_sysfs_init(struct platform_device *device)
|
|
||||||
{
|
|
||||||
return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Platform device
|
|
||||||
*/
|
|
||||||
static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
|
|
||||||
if (!eeepc->platform_device)
|
|
||||||
return -ENOMEM;
|
|
||||||
platform_set_drvdata(eeepc->platform_device, eeepc);
|
|
||||||
|
|
||||||
err = platform_device_add(eeepc->platform_device);
|
|
||||||
if (err)
|
|
||||||
goto fail_platform_device;
|
|
||||||
|
|
||||||
err = eeepc_wmi_sysfs_init(eeepc->platform_device);
|
|
||||||
if (err)
|
|
||||||
goto fail_sysfs;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail_sysfs:
|
|
||||||
platform_device_del(eeepc->platform_device);
|
|
||||||
fail_platform_device:
|
|
||||||
platform_device_put(eeepc->platform_device);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
eeepc_wmi_sysfs_exit(eeepc->platform_device);
|
|
||||||
platform_device_unregister(eeepc->platform_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* debugfs
|
|
||||||
*/
|
|
||||||
struct eeepc_wmi_debugfs_node {
|
|
||||||
struct eeepc_wmi *eeepc;
|
|
||||||
char *name;
|
|
||||||
int (*show)(struct seq_file *m, void *data);
|
|
||||||
};
|
|
||||||
|
|
||||||
static int show_dsts(struct seq_file *m, void *data)
|
|
||||||
{
|
|
||||||
struct eeepc_wmi *eeepc = m->private;
|
|
||||||
acpi_status status;
|
|
||||||
u32 retval = -1;
|
|
||||||
|
|
||||||
status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int show_devs(struct seq_file *m, void *data)
|
|
||||||
{
|
|
||||||
struct eeepc_wmi *eeepc = m->private;
|
|
||||||
acpi_status status;
|
|
||||||
u32 retval = -1;
|
|
||||||
|
|
||||||
status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
|
|
||||||
eeepc->debug.ctrl_param, &retval);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
|
|
||||||
eeepc->debug.ctrl_param, retval);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
|
|
||||||
{ NULL, "devs", show_devs },
|
|
||||||
{ NULL, "dsts", show_dsts },
|
|
||||||
};
|
|
||||||
|
|
||||||
static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct eeepc_wmi_debugfs_node *node = inode->i_private;
|
|
||||||
|
|
||||||
return single_open(file, node->show, node->eeepc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations eeepc_wmi_debugfs_io_ops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.open = eeepc_wmi_debugfs_open,
|
|
||||||
.read = seq_read,
|
|
||||||
.llseek = seq_lseek,
|
|
||||||
.release = single_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
debugfs_remove_recursive(eeepc->debug.root);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
|
|
||||||
{
|
|
||||||
struct dentry *dent;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
|
|
||||||
if (!eeepc->debug.root) {
|
|
||||||
pr_err("failed to create debugfs directory");
|
|
||||||
goto error_debugfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
|
|
||||||
eeepc->debug.root, &eeepc->debug.dev_id);
|
|
||||||
if (!dent)
|
|
||||||
goto error_debugfs;
|
|
||||||
|
|
||||||
dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
|
|
||||||
eeepc->debug.root, &eeepc->debug.ctrl_param);
|
|
||||||
if (!dent)
|
|
||||||
goto error_debugfs;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
|
|
||||||
struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
|
|
||||||
|
|
||||||
node->eeepc = eeepc;
|
|
||||||
dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
|
|
||||||
eeepc->debug.root, node,
|
|
||||||
&eeepc_wmi_debugfs_io_ops);
|
|
||||||
if (!dent) {
|
|
||||||
pr_err("failed to create debug file: %s\n", node->name);
|
|
||||||
goto error_debugfs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error_debugfs:
|
|
||||||
eeepc_wmi_debugfs_exit(eeepc);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* WMI Driver
|
|
||||||
*/
|
|
||||||
static struct platform_device * __init eeepc_wmi_add(void)
|
|
||||||
{
|
|
||||||
struct eeepc_wmi *eeepc;
|
|
||||||
acpi_status status;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
|
|
||||||
if (!eeepc)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Register the platform device first. It is used as a parent for the
|
|
||||||
* sub-devices below.
|
|
||||||
*/
|
|
||||||
err = eeepc_wmi_platform_init(eeepc);
|
|
||||||
if (err)
|
|
||||||
goto fail_platform;
|
|
||||||
|
|
||||||
err = eeepc_wmi_input_init(eeepc);
|
|
||||||
if (err)
|
|
||||||
goto fail_input;
|
|
||||||
|
|
||||||
err = eeepc_wmi_led_init(eeepc);
|
|
||||||
if (err)
|
|
||||||
goto fail_leds;
|
|
||||||
|
|
||||||
err = eeepc_wmi_rfkill_init(eeepc);
|
|
||||||
if (err)
|
|
||||||
goto fail_rfkill;
|
|
||||||
|
|
||||||
if (!acpi_video_backlight_support()) {
|
|
||||||
err = eeepc_wmi_backlight_init(eeepc);
|
|
||||||
if (err)
|
|
||||||
goto fail_backlight;
|
|
||||||
} else
|
|
||||||
pr_info("Backlight controlled by ACPI video driver\n");
|
|
||||||
|
|
||||||
status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
|
|
||||||
eeepc_wmi_notify, eeepc);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
pr_err("Unable to register notify handler - %d\n",
|
|
||||||
status);
|
|
||||||
err = -ENODEV;
|
|
||||||
goto fail_wmi_handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = eeepc_wmi_debugfs_init(eeepc);
|
|
||||||
if (err)
|
|
||||||
goto fail_debugfs;
|
|
||||||
|
|
||||||
return eeepc->platform_device;
|
|
||||||
|
|
||||||
fail_debugfs:
|
|
||||||
wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
|
|
||||||
fail_wmi_handler:
|
|
||||||
eeepc_wmi_backlight_exit(eeepc);
|
|
||||||
fail_backlight:
|
|
||||||
eeepc_wmi_rfkill_exit(eeepc);
|
|
||||||
fail_rfkill:
|
|
||||||
eeepc_wmi_led_exit(eeepc);
|
|
||||||
fail_leds:
|
|
||||||
eeepc_wmi_input_exit(eeepc);
|
|
||||||
fail_input:
|
|
||||||
eeepc_wmi_platform_exit(eeepc);
|
|
||||||
fail_platform:
|
|
||||||
kfree(eeepc);
|
|
||||||
return ERR_PTR(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int eeepc_wmi_remove(struct platform_device *device)
|
|
||||||
{
|
|
||||||
struct eeepc_wmi *eeepc;
|
|
||||||
|
|
||||||
eeepc = platform_get_drvdata(device);
|
|
||||||
wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
|
|
||||||
eeepc_wmi_backlight_exit(eeepc);
|
|
||||||
eeepc_wmi_input_exit(eeepc);
|
|
||||||
eeepc_wmi_led_exit(eeepc);
|
|
||||||
eeepc_wmi_rfkill_exit(eeepc);
|
|
||||||
eeepc_wmi_debugfs_exit(eeepc);
|
|
||||||
eeepc_wmi_platform_exit(eeepc);
|
|
||||||
|
|
||||||
kfree(eeepc);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct platform_driver platform_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = EEEPC_WMI_FILE,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
|
|
||||||
void *context, void **retval)
|
void *context, void **retval)
|
||||||
{
|
{
|
||||||
pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
|
pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
|
||||||
|
@ -871,7 +87,7 @@ static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
|
||||||
return AE_CTRL_TERMINATE;
|
return AE_CTRL_TERMINATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init eeepc_wmi_check_atkd(void)
|
static int eeepc_wmi_check_atkd(void)
|
||||||
{
|
{
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -884,16 +100,8 @@ static int __init eeepc_wmi_check_atkd(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init eeepc_wmi_init(void)
|
static int eeepc_wmi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
|
|
||||||
!wmi_has_guid(EEEPC_WMI_MGMT_GUID)) {
|
|
||||||
pr_warning("No known WMI GUID found\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eeepc_wmi_check_atkd()) {
|
if (eeepc_wmi_check_atkd()) {
|
||||||
pr_warning("WMI device present, but legacy ATKD device is also "
|
pr_warning("WMI device present, but legacy ATKD device is also "
|
||||||
"present and enabled.");
|
"present and enabled.");
|
||||||
|
@ -901,33 +109,59 @@ static int __init eeepc_wmi_init(void)
|
||||||
"acpi_osi=\"!Windows 2009\"");
|
"acpi_osi=\"!Windows 2009\"");
|
||||||
pr_warning("Can't load eeepc-wmi, use default acpi_osi "
|
pr_warning("Can't load eeepc-wmi, use default acpi_osi "
|
||||||
"(preferred) or eeepc-laptop");
|
"(preferred) or eeepc-laptop");
|
||||||
return -ENODEV;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_device = eeepc_wmi_add();
|
|
||||||
if (IS_ERR(platform_device)) {
|
|
||||||
err = PTR_ERR(platform_device);
|
|
||||||
goto fail_eeepc_wmi;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = platform_driver_register(&platform_driver);
|
|
||||||
if (err) {
|
|
||||||
pr_warning("Unable to register platform driver\n");
|
|
||||||
goto fail_platform_driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
fail_platform_driver:
|
static void eeepc_dmi_check(struct asus_wmi_driver *driver)
|
||||||
eeepc_wmi_remove(platform_device);
|
{
|
||||||
fail_eeepc_wmi:
|
const char *model;
|
||||||
return err;
|
|
||||||
|
model = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||||
|
if (!model)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whitelist for wlan hotplug
|
||||||
|
*
|
||||||
|
* Asus 1000H needs the current hotplug code to handle
|
||||||
|
* Fn+F2 correctly. We may add other Asus here later, but
|
||||||
|
* it seems that most of the laptops supported by asus-wmi
|
||||||
|
* don't need to be on this list
|
||||||
|
*/
|
||||||
|
if (strcmp(model, "1000H") == 0) {
|
||||||
|
driver->hotplug_wireless = true;
|
||||||
|
pr_info("wlan hotplug enabled\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
|
||||||
|
{
|
||||||
|
driver->hotplug_wireless = hotplug_wireless;
|
||||||
|
eeepc_dmi_check(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct asus_wmi_driver asus_wmi_driver = {
|
||||||
|
.name = EEEPC_WMI_FILE,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.event_guid = EEEPC_WMI_EVENT_GUID,
|
||||||
|
.keymap = eeepc_wmi_keymap,
|
||||||
|
.input_name = "Eee PC WMI hotkeys",
|
||||||
|
.input_phys = EEEPC_WMI_FILE "/input0",
|
||||||
|
.probe = eeepc_wmi_probe,
|
||||||
|
.quirks = eeepc_wmi_quirks,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int __init eeepc_wmi_init(void)
|
||||||
|
{
|
||||||
|
return asus_wmi_register_driver(&asus_wmi_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit eeepc_wmi_exit(void)
|
static void __exit eeepc_wmi_exit(void)
|
||||||
{
|
{
|
||||||
eeepc_wmi_remove(platform_device);
|
asus_wmi_unregister_driver(&asus_wmi_driver);
|
||||||
platform_driver_unregister(&platform_driver);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(eeepc_wmi_init);
|
module_init(eeepc_wmi_init);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* HP WMI hotkeys
|
* HP WMI hotkeys
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008 Red Hat <mjg@redhat.com>
|
* Copyright (C) 2008 Red Hat <mjg@redhat.com>
|
||||||
|
* Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
|
||||||
*
|
*
|
||||||
* Portions based on wistron_btns.c:
|
* Portions based on wistron_btns.c:
|
||||||
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
||||||
|
@ -51,6 +52,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
|
||||||
#define HPWMI_HARDWARE_QUERY 0x4
|
#define HPWMI_HARDWARE_QUERY 0x4
|
||||||
#define HPWMI_WIRELESS_QUERY 0x5
|
#define HPWMI_WIRELESS_QUERY 0x5
|
||||||
#define HPWMI_HOTKEY_QUERY 0xc
|
#define HPWMI_HOTKEY_QUERY 0xc
|
||||||
|
#define HPWMI_WIRELESS2_QUERY 0x1b
|
||||||
|
|
||||||
#define PREFIX "HP WMI: "
|
#define PREFIX "HP WMI: "
|
||||||
#define UNIMP "Unimplemented "
|
#define UNIMP "Unimplemented "
|
||||||
|
@ -86,7 +88,46 @@ struct bios_args {
|
||||||
struct bios_return {
|
struct bios_return {
|
||||||
u32 sigpass;
|
u32 sigpass;
|
||||||
u32 return_code;
|
u32 return_code;
|
||||||
u32 value;
|
};
|
||||||
|
|
||||||
|
enum hp_return_value {
|
||||||
|
HPWMI_RET_WRONG_SIGNATURE = 0x02,
|
||||||
|
HPWMI_RET_UNKNOWN_COMMAND = 0x03,
|
||||||
|
HPWMI_RET_UNKNOWN_CMDTYPE = 0x04,
|
||||||
|
HPWMI_RET_INVALID_PARAMETERS = 0x05,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum hp_wireless2_bits {
|
||||||
|
HPWMI_POWER_STATE = 0x01,
|
||||||
|
HPWMI_POWER_SOFT = 0x02,
|
||||||
|
HPWMI_POWER_BIOS = 0x04,
|
||||||
|
HPWMI_POWER_HARD = 0x08,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
|
||||||
|
!= (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
|
||||||
|
#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
|
||||||
|
|
||||||
|
struct bios_rfkill2_device_state {
|
||||||
|
u8 radio_type;
|
||||||
|
u8 bus_type;
|
||||||
|
u16 vendor_id;
|
||||||
|
u16 product_id;
|
||||||
|
u16 subsys_vendor_id;
|
||||||
|
u16 subsys_product_id;
|
||||||
|
u8 rfkill_id;
|
||||||
|
u8 power;
|
||||||
|
u8 unknown[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 7 devices fit into the 128 byte buffer */
|
||||||
|
#define HPWMI_MAX_RFKILL2_DEVICES 7
|
||||||
|
|
||||||
|
struct bios_rfkill2_state {
|
||||||
|
u8 unknown[7];
|
||||||
|
u8 count;
|
||||||
|
u8 pad[8];
|
||||||
|
struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct key_entry hp_wmi_keymap[] = {
|
static const struct key_entry hp_wmi_keymap[] = {
|
||||||
|
@ -108,6 +149,15 @@ static struct rfkill *wifi_rfkill;
|
||||||
static struct rfkill *bluetooth_rfkill;
|
static struct rfkill *bluetooth_rfkill;
|
||||||
static struct rfkill *wwan_rfkill;
|
static struct rfkill *wwan_rfkill;
|
||||||
|
|
||||||
|
struct rfkill2_device {
|
||||||
|
u8 id;
|
||||||
|
int num;
|
||||||
|
struct rfkill *rfkill;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rfkill2_count;
|
||||||
|
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
|
||||||
|
|
||||||
static const struct dev_pm_ops hp_wmi_pm_ops = {
|
static const struct dev_pm_ops hp_wmi_pm_ops = {
|
||||||
.resume = hp_wmi_resume_handler,
|
.resume = hp_wmi_resume_handler,
|
||||||
.restore = hp_wmi_resume_handler,
|
.restore = hp_wmi_resume_handler,
|
||||||
|
@ -129,7 +179,8 @@ static struct platform_driver hp_wmi_driver = {
|
||||||
* query: The commandtype -> What should be queried
|
* query: The commandtype -> What should be queried
|
||||||
* write: The command -> 0 read, 1 write, 3 ODM specific
|
* write: The command -> 0 read, 1 write, 3 ODM specific
|
||||||
* buffer: Buffer used as input and/or output
|
* buffer: Buffer used as input and/or output
|
||||||
* buffersize: Size of buffer
|
* insize: Size of input buffer
|
||||||
|
* outsize: Size of output buffer
|
||||||
*
|
*
|
||||||
* returns zero on success
|
* returns zero on success
|
||||||
* an HP WMI query specific error code (which is positive)
|
* an HP WMI query specific error code (which is positive)
|
||||||
|
@ -140,25 +191,29 @@ static struct platform_driver hp_wmi_driver = {
|
||||||
* size. E.g. Battery info query (0x7) is defined to have 1 byte input
|
* size. E.g. Battery info query (0x7) is defined to have 1 byte input
|
||||||
* and 128 byte output. The caller would do:
|
* and 128 byte output. The caller would do:
|
||||||
* buffer = kzalloc(128, GFP_KERNEL);
|
* buffer = kzalloc(128, GFP_KERNEL);
|
||||||
* ret = hp_wmi_perform_query(0x7, 0, buffer, 128)
|
* ret = hp_wmi_perform_query(0x7, 0, buffer, 1, 128)
|
||||||
*/
|
*/
|
||||||
static int hp_wmi_perform_query(int query, int write, u32 *buffer,
|
static int hp_wmi_perform_query(int query, int write, void *buffer,
|
||||||
int buffersize)
|
int insize, int outsize)
|
||||||
{
|
{
|
||||||
struct bios_return bios_return;
|
struct bios_return *bios_return;
|
||||||
acpi_status status;
|
int actual_outsize;
|
||||||
union acpi_object *obj;
|
union acpi_object *obj;
|
||||||
struct bios_args args = {
|
struct bios_args args = {
|
||||||
.signature = 0x55434553,
|
.signature = 0x55434553,
|
||||||
.command = write ? 0x2 : 0x1,
|
.command = write ? 0x2 : 0x1,
|
||||||
.commandtype = query,
|
.commandtype = query,
|
||||||
.datasize = buffersize,
|
.datasize = insize,
|
||||||
.data = *buffer,
|
.data = 0,
|
||||||
};
|
};
|
||||||
struct acpi_buffer input = { sizeof(struct bios_args), &args };
|
struct acpi_buffer input = { sizeof(struct bios_args), &args };
|
||||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
|
||||||
status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
|
if (WARN_ON(insize > sizeof(args.data)))
|
||||||
|
return -EINVAL;
|
||||||
|
memcpy(&args.data, buffer, insize);
|
||||||
|
|
||||||
|
wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
|
||||||
|
|
||||||
obj = output.pointer;
|
obj = output.pointer;
|
||||||
|
|
||||||
|
@ -169,10 +224,26 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bios_return = *((struct bios_return *)obj->buffer.pointer);
|
bios_return = (struct bios_return *)obj->buffer.pointer;
|
||||||
|
|
||||||
memcpy(buffer, &bios_return.value, sizeof(bios_return.value));
|
if (bios_return->return_code) {
|
||||||
|
if (bios_return->return_code != HPWMI_RET_UNKNOWN_CMDTYPE)
|
||||||
|
printk(KERN_WARNING PREFIX "query 0x%x returned "
|
||||||
|
"error 0x%x\n",
|
||||||
|
query, bios_return->return_code);
|
||||||
|
kfree(obj);
|
||||||
|
return bios_return->return_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outsize) {
|
||||||
|
/* ignore output data */
|
||||||
|
kfree(obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return)));
|
||||||
|
memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize);
|
||||||
|
memset(buffer + actual_outsize, 0, outsize - actual_outsize);
|
||||||
kfree(obj);
|
kfree(obj);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +252,7 @@ static int hp_wmi_display_state(void)
|
||||||
{
|
{
|
||||||
int state = 0;
|
int state = 0;
|
||||||
int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state,
|
int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state,
|
||||||
sizeof(state));
|
sizeof(state), sizeof(state));
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
return state;
|
return state;
|
||||||
|
@ -191,7 +262,7 @@ static int hp_wmi_hddtemp_state(void)
|
||||||
{
|
{
|
||||||
int state = 0;
|
int state = 0;
|
||||||
int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state,
|
int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state,
|
||||||
sizeof(state));
|
sizeof(state), sizeof(state));
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
return state;
|
return state;
|
||||||
|
@ -201,7 +272,7 @@ static int hp_wmi_als_state(void)
|
||||||
{
|
{
|
||||||
int state = 0;
|
int state = 0;
|
||||||
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state,
|
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state,
|
||||||
sizeof(state));
|
sizeof(state), sizeof(state));
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
return state;
|
return state;
|
||||||
|
@ -211,7 +282,7 @@ static int hp_wmi_dock_state(void)
|
||||||
{
|
{
|
||||||
int state = 0;
|
int state = 0;
|
||||||
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
|
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
|
||||||
sizeof(state));
|
sizeof(state), sizeof(state));
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -223,7 +294,7 @@ static int hp_wmi_tablet_state(void)
|
||||||
{
|
{
|
||||||
int state = 0;
|
int state = 0;
|
||||||
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
|
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
|
||||||
sizeof(state));
|
sizeof(state), sizeof(state));
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -237,7 +308,7 @@ static int hp_wmi_set_block(void *data, bool blocked)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1,
|
ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1,
|
||||||
&query, sizeof(query));
|
&query, sizeof(query), 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -252,7 +323,8 @@ static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
|
||||||
int wireless = 0;
|
int wireless = 0;
|
||||||
int mask;
|
int mask;
|
||||||
hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
|
hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
|
||||||
&wireless, sizeof(wireless));
|
&wireless, sizeof(wireless),
|
||||||
|
sizeof(wireless));
|
||||||
/* TBD: Pass error */
|
/* TBD: Pass error */
|
||||||
|
|
||||||
mask = 0x200 << (r * 8);
|
mask = 0x200 << (r * 8);
|
||||||
|
@ -268,7 +340,8 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
|
||||||
int wireless = 0;
|
int wireless = 0;
|
||||||
int mask;
|
int mask;
|
||||||
hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
|
hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
|
||||||
&wireless, sizeof(wireless));
|
&wireless, sizeof(wireless),
|
||||||
|
sizeof(wireless));
|
||||||
/* TBD: Pass error */
|
/* TBD: Pass error */
|
||||||
|
|
||||||
mask = 0x800 << (r * 8);
|
mask = 0x800 << (r * 8);
|
||||||
|
@ -279,6 +352,51 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hp_wmi_rfkill2_set_block(void *data, bool blocked)
|
||||||
|
{
|
||||||
|
int rfkill_id = (int)(long)data;
|
||||||
|
char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked };
|
||||||
|
|
||||||
|
if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1,
|
||||||
|
buffer, sizeof(buffer), 0))
|
||||||
|
return -EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rfkill_ops hp_wmi_rfkill2_ops = {
|
||||||
|
.set_block = hp_wmi_rfkill2_set_block,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int hp_wmi_rfkill2_refresh(void)
|
||||||
|
{
|
||||||
|
int err, i;
|
||||||
|
struct bios_rfkill2_state state;
|
||||||
|
|
||||||
|
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
|
||||||
|
0, sizeof(state));
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 0; i < rfkill2_count; i++) {
|
||||||
|
int num = rfkill2[i].num;
|
||||||
|
struct bios_rfkill2_device_state *devstate;
|
||||||
|
devstate = &state.device[num];
|
||||||
|
|
||||||
|
if (num >= state.count ||
|
||||||
|
devstate->rfkill_id != rfkill2[i].id) {
|
||||||
|
printk(KERN_WARNING PREFIX "power configuration of "
|
||||||
|
"the wireless devices unexpectedly changed\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfkill_set_states(rfkill2[i].rfkill,
|
||||||
|
IS_SWBLOCKED(devstate->power),
|
||||||
|
IS_HWBLOCKED(devstate->power));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t show_display(struct device *dev, struct device_attribute *attr,
|
static ssize_t show_display(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
|
@ -329,7 +447,7 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr,
|
||||||
{
|
{
|
||||||
u32 tmp = simple_strtoul(buf, NULL, 10);
|
u32 tmp = simple_strtoul(buf, NULL, 10);
|
||||||
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp,
|
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp,
|
||||||
sizeof(tmp));
|
sizeof(tmp), sizeof(tmp));
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -402,6 +520,7 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||||
case HPWMI_BEZEL_BUTTON:
|
case HPWMI_BEZEL_BUTTON:
|
||||||
ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
|
ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
|
||||||
&key_code,
|
&key_code,
|
||||||
|
sizeof(key_code),
|
||||||
sizeof(key_code));
|
sizeof(key_code));
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
@ -412,6 +531,11 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||||
key_code);
|
key_code);
|
||||||
break;
|
break;
|
||||||
case HPWMI_WIRELESS:
|
case HPWMI_WIRELESS:
|
||||||
|
if (rfkill2_count) {
|
||||||
|
hp_wmi_rfkill2_refresh();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (wifi_rfkill)
|
if (wifi_rfkill)
|
||||||
rfkill_set_states(wifi_rfkill,
|
rfkill_set_states(wifi_rfkill,
|
||||||
hp_wmi_get_sw_state(HPWMI_WIFI),
|
hp_wmi_get_sw_state(HPWMI_WIFI),
|
||||||
|
@ -502,32 +626,16 @@ static void cleanup_sysfs(struct platform_device *device)
|
||||||
device_remove_file(&device->dev, &dev_attr_tablet);
|
device_remove_file(&device->dev, &dev_attr_tablet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devinit hp_wmi_bios_setup(struct platform_device *device)
|
static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
int wireless = 0;
|
int wireless = 0;
|
||||||
|
|
||||||
err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless,
|
err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless,
|
||||||
sizeof(wireless));
|
sizeof(wireless), sizeof(wireless));
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
err = device_create_file(&device->dev, &dev_attr_display);
|
|
||||||
if (err)
|
|
||||||
goto add_sysfs_error;
|
|
||||||
err = device_create_file(&device->dev, &dev_attr_hddtemp);
|
|
||||||
if (err)
|
|
||||||
goto add_sysfs_error;
|
|
||||||
err = device_create_file(&device->dev, &dev_attr_als);
|
|
||||||
if (err)
|
|
||||||
goto add_sysfs_error;
|
|
||||||
err = device_create_file(&device->dev, &dev_attr_dock);
|
|
||||||
if (err)
|
|
||||||
goto add_sysfs_error;
|
|
||||||
err = device_create_file(&device->dev, &dev_attr_tablet);
|
|
||||||
if (err)
|
|
||||||
goto add_sysfs_error;
|
|
||||||
|
|
||||||
if (wireless & 0x1) {
|
if (wireless & 0x1) {
|
||||||
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
|
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
|
||||||
RFKILL_TYPE_WLAN,
|
RFKILL_TYPE_WLAN,
|
||||||
|
@ -573,14 +681,131 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device)
|
||||||
return 0;
|
return 0;
|
||||||
register_wwan_err:
|
register_wwan_err:
|
||||||
rfkill_destroy(wwan_rfkill);
|
rfkill_destroy(wwan_rfkill);
|
||||||
|
wwan_rfkill = NULL;
|
||||||
if (bluetooth_rfkill)
|
if (bluetooth_rfkill)
|
||||||
rfkill_unregister(bluetooth_rfkill);
|
rfkill_unregister(bluetooth_rfkill);
|
||||||
register_bluetooth_error:
|
register_bluetooth_error:
|
||||||
rfkill_destroy(bluetooth_rfkill);
|
rfkill_destroy(bluetooth_rfkill);
|
||||||
|
bluetooth_rfkill = NULL;
|
||||||
if (wifi_rfkill)
|
if (wifi_rfkill)
|
||||||
rfkill_unregister(wifi_rfkill);
|
rfkill_unregister(wifi_rfkill);
|
||||||
register_wifi_error:
|
register_wifi_error:
|
||||||
rfkill_destroy(wifi_rfkill);
|
rfkill_destroy(wifi_rfkill);
|
||||||
|
wifi_rfkill = NULL;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
|
||||||
|
{
|
||||||
|
int err, i;
|
||||||
|
struct bios_rfkill2_state state;
|
||||||
|
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
|
||||||
|
0, sizeof(state));
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
|
||||||
|
printk(KERN_WARNING PREFIX "unable to parse 0x1b query output\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < state.count; i++) {
|
||||||
|
struct rfkill *rfkill;
|
||||||
|
enum rfkill_type type;
|
||||||
|
char *name;
|
||||||
|
switch (state.device[i].radio_type) {
|
||||||
|
case HPWMI_WIFI:
|
||||||
|
type = RFKILL_TYPE_WLAN;
|
||||||
|
name = "hp-wifi";
|
||||||
|
break;
|
||||||
|
case HPWMI_BLUETOOTH:
|
||||||
|
type = RFKILL_TYPE_BLUETOOTH;
|
||||||
|
name = "hp-bluetooth";
|
||||||
|
break;
|
||||||
|
case HPWMI_WWAN:
|
||||||
|
type = RFKILL_TYPE_WWAN;
|
||||||
|
name = "hp-wwan";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printk(KERN_WARNING PREFIX "unknown device type 0x%x\n",
|
||||||
|
state.device[i].radio_type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.device[i].vendor_id) {
|
||||||
|
printk(KERN_WARNING PREFIX "zero device %d while %d "
|
||||||
|
"reported\n", i, state.count);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfkill = rfkill_alloc(name, &device->dev, type,
|
||||||
|
&hp_wmi_rfkill2_ops, (void *)(long)i);
|
||||||
|
if (!rfkill) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfkill2[rfkill2_count].id = state.device[i].rfkill_id;
|
||||||
|
rfkill2[rfkill2_count].num = i;
|
||||||
|
rfkill2[rfkill2_count].rfkill = rfkill;
|
||||||
|
|
||||||
|
rfkill_init_sw_state(rfkill,
|
||||||
|
IS_SWBLOCKED(state.device[i].power));
|
||||||
|
rfkill_set_hw_state(rfkill,
|
||||||
|
IS_HWBLOCKED(state.device[i].power));
|
||||||
|
|
||||||
|
if (!(state.device[i].power & HPWMI_POWER_BIOS))
|
||||||
|
printk(KERN_INFO PREFIX "device %s blocked by BIOS\n",
|
||||||
|
name);
|
||||||
|
|
||||||
|
err = rfkill_register(rfkill);
|
||||||
|
if (err) {
|
||||||
|
rfkill_destroy(rfkill);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfkill2_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
for (; rfkill2_count > 0; rfkill2_count--) {
|
||||||
|
rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill);
|
||||||
|
rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit hp_wmi_bios_setup(struct platform_device *device)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* clear detected rfkill devices */
|
||||||
|
wifi_rfkill = NULL;
|
||||||
|
bluetooth_rfkill = NULL;
|
||||||
|
wwan_rfkill = NULL;
|
||||||
|
rfkill2_count = 0;
|
||||||
|
|
||||||
|
if (hp_wmi_rfkill_setup(device))
|
||||||
|
hp_wmi_rfkill2_setup(device);
|
||||||
|
|
||||||
|
err = device_create_file(&device->dev, &dev_attr_display);
|
||||||
|
if (err)
|
||||||
|
goto add_sysfs_error;
|
||||||
|
err = device_create_file(&device->dev, &dev_attr_hddtemp);
|
||||||
|
if (err)
|
||||||
|
goto add_sysfs_error;
|
||||||
|
err = device_create_file(&device->dev, &dev_attr_als);
|
||||||
|
if (err)
|
||||||
|
goto add_sysfs_error;
|
||||||
|
err = device_create_file(&device->dev, &dev_attr_dock);
|
||||||
|
if (err)
|
||||||
|
goto add_sysfs_error;
|
||||||
|
err = device_create_file(&device->dev, &dev_attr_tablet);
|
||||||
|
if (err)
|
||||||
|
goto add_sysfs_error;
|
||||||
|
return 0;
|
||||||
|
|
||||||
add_sysfs_error:
|
add_sysfs_error:
|
||||||
cleanup_sysfs(device);
|
cleanup_sysfs(device);
|
||||||
return err;
|
return err;
|
||||||
|
@ -588,8 +813,14 @@ add_sysfs_error:
|
||||||
|
|
||||||
static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
cleanup_sysfs(device);
|
cleanup_sysfs(device);
|
||||||
|
|
||||||
|
for (i = 0; i < rfkill2_count; i++) {
|
||||||
|
rfkill_unregister(rfkill2[i].rfkill);
|
||||||
|
rfkill_destroy(rfkill2[i].rfkill);
|
||||||
|
}
|
||||||
|
|
||||||
if (wifi_rfkill) {
|
if (wifi_rfkill) {
|
||||||
rfkill_unregister(wifi_rfkill);
|
rfkill_unregister(wifi_rfkill);
|
||||||
rfkill_destroy(wifi_rfkill);
|
rfkill_destroy(wifi_rfkill);
|
||||||
|
@ -622,6 +853,9 @@ static int hp_wmi_resume_handler(struct device *device)
|
||||||
input_sync(hp_wmi_input_dev);
|
input_sync(hp_wmi_input_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rfkill2_count)
|
||||||
|
hp_wmi_rfkill2_refresh();
|
||||||
|
|
||||||
if (wifi_rfkill)
|
if (wifi_rfkill)
|
||||||
rfkill_set_states(wifi_rfkill,
|
rfkill_set_states(wifi_rfkill,
|
||||||
hp_wmi_get_sw_state(HPWMI_WIFI),
|
hp_wmi_get_sw_state(HPWMI_WIFI),
|
||||||
|
|
|
@ -459,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
|
||||||
if (test_bit(vpc_bit, &vpc1)) {
|
if (test_bit(vpc_bit, &vpc1)) {
|
||||||
if (vpc_bit == 9)
|
if (vpc_bit == 9)
|
||||||
ideapad_sync_rfk_state(adevice);
|
ideapad_sync_rfk_state(adevice);
|
||||||
|
else if (vpc_bit == 4)
|
||||||
|
read_ec_data(handle, 0x12, &vpc2);
|
||||||
else
|
else
|
||||||
ideapad_input_report(priv, vpc_bit);
|
ideapad_input_report(priv, vpc_bit);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1111,7 +1111,7 @@ static int ips_monitor(void *data)
|
||||||
last_msecs = jiffies_to_msecs(jiffies);
|
last_msecs = jiffies_to_msecs(jiffies);
|
||||||
expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD);
|
expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD);
|
||||||
|
|
||||||
__set_current_state(TASK_UNINTERRUPTIBLE);
|
__set_current_state(TASK_INTERRUPTIBLE);
|
||||||
mod_timer(&timer, expire);
|
mod_timer(&timer, expire);
|
||||||
schedule();
|
schedule();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Power button driver for Medfield.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Intel Corp
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <asm/intel_scu_ipc.h>
|
||||||
|
|
||||||
|
#define DRIVER_NAME "msic_power_btn"
|
||||||
|
|
||||||
|
#define MSIC_IRQ_STAT 0x02
|
||||||
|
#define MSIC_IRQ_PB (1 << 0)
|
||||||
|
#define MSIC_PB_CONFIG 0x3e
|
||||||
|
#define MSIC_PB_STATUS 0x3f
|
||||||
|
#define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */
|
||||||
|
|
||||||
|
struct mfld_pb_priv {
|
||||||
|
struct input_dev *input;
|
||||||
|
unsigned int irq;
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t mfld_pb_isr(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct mfld_pb_priv *priv = dev_id;
|
||||||
|
int ret;
|
||||||
|
u8 pbstat;
|
||||||
|
|
||||||
|
ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat);
|
||||||
|
if (ret < 0)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
input_event(priv->input, EV_KEY, KEY_POWER, !(pbstat & MSIC_PB_LEVEL));
|
||||||
|
input_sync(priv->input);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit mfld_pb_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct mfld_pb_priv *priv;
|
||||||
|
struct input_dev *input;
|
||||||
|
int irq;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
priv = kzalloc(sizeof(struct mfld_pb_priv), GFP_KERNEL);
|
||||||
|
input = input_allocate_device();
|
||||||
|
if (!priv || !input) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto err_free_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->input = input;
|
||||||
|
priv->irq = irq;
|
||||||
|
|
||||||
|
input->name = pdev->name;
|
||||||
|
input->phys = "power-button/input0";
|
||||||
|
input->id.bustype = BUS_HOST;
|
||||||
|
input->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
input_set_capability(input, EV_KEY, KEY_POWER);
|
||||||
|
|
||||||
|
error = request_threaded_irq(priv->irq, NULL, mfld_pb_isr,
|
||||||
|
0, DRIVER_NAME, priv);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"unable to request irq %d for mfld power button\n",
|
||||||
|
irq);
|
||||||
|
goto err_free_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = input_register_device(input);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"unable to register input dev, error %d\n", error);
|
||||||
|
goto err_free_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, priv);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free_irq:
|
||||||
|
free_irq(priv->irq, priv);
|
||||||
|
err_free_mem:
|
||||||
|
input_free_device(input);
|
||||||
|
kfree(priv);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit mfld_pb_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct mfld_pb_priv *priv = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
free_irq(priv->irq, priv);
|
||||||
|
input_unregister_device(priv->input);
|
||||||
|
kfree(priv);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver mfld_pb_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = mfld_pb_probe,
|
||||||
|
.remove = __devexit_p(mfld_pb_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init mfld_pb_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&mfld_pb_driver);
|
||||||
|
}
|
||||||
|
module_init(mfld_pb_init);
|
||||||
|
|
||||||
|
static void __exit mfld_pb_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&mfld_pb_driver);
|
||||||
|
}
|
||||||
|
module_exit(mfld_pb_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>");
|
||||||
|
MODULE_DESCRIPTION("Intel Medfield Power Button Driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
@ -0,0 +1,576 @@
|
||||||
|
/*
|
||||||
|
* intel_mid_thermal.c - Intel MID platform thermal driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Intel Corporation
|
||||||
|
*
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
* Author: Durgadoss R <durgadoss.r@intel.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "intel_mid_thermal: " fmt
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/param.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/thermal.h>
|
||||||
|
|
||||||
|
#include <asm/intel_scu_ipc.h>
|
||||||
|
|
||||||
|
/* Number of thermal sensors */
|
||||||
|
#define MSIC_THERMAL_SENSORS 4
|
||||||
|
|
||||||
|
/* ADC1 - thermal registers */
|
||||||
|
#define MSIC_THERM_ADC1CNTL1 0x1C0
|
||||||
|
#define MSIC_ADC_ENBL 0x10
|
||||||
|
#define MSIC_ADC_START 0x08
|
||||||
|
|
||||||
|
#define MSIC_THERM_ADC1CNTL3 0x1C2
|
||||||
|
#define MSIC_ADCTHERM_ENBL 0x04
|
||||||
|
#define MSIC_ADCRRDATA_ENBL 0x05
|
||||||
|
#define MSIC_CHANL_MASK_VAL 0x0F
|
||||||
|
|
||||||
|
#define MSIC_STOPBIT_MASK 16
|
||||||
|
#define MSIC_ADCTHERM_MASK 4
|
||||||
|
#define ADC_CHANLS_MAX 15 /* Number of ADC channels */
|
||||||
|
#define ADC_LOOP_MAX (ADC_CHANLS_MAX - MSIC_THERMAL_SENSORS)
|
||||||
|
|
||||||
|
/* ADC channel code values */
|
||||||
|
#define SKIN_SENSOR0_CODE 0x08
|
||||||
|
#define SKIN_SENSOR1_CODE 0x09
|
||||||
|
#define SYS_SENSOR_CODE 0x0A
|
||||||
|
#define MSIC_DIE_SENSOR_CODE 0x03
|
||||||
|
|
||||||
|
#define SKIN_THERM_SENSOR0 0
|
||||||
|
#define SKIN_THERM_SENSOR1 1
|
||||||
|
#define SYS_THERM_SENSOR2 2
|
||||||
|
#define MSIC_DIE_THERM_SENSOR3 3
|
||||||
|
|
||||||
|
/* ADC code range */
|
||||||
|
#define ADC_MAX 977
|
||||||
|
#define ADC_MIN 162
|
||||||
|
#define ADC_VAL0C 887
|
||||||
|
#define ADC_VAL20C 720
|
||||||
|
#define ADC_VAL40C 508
|
||||||
|
#define ADC_VAL60C 315
|
||||||
|
|
||||||
|
/* ADC base addresses */
|
||||||
|
#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
|
||||||
|
#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
|
||||||
|
|
||||||
|
/* MSIC die attributes */
|
||||||
|
#define MSIC_DIE_ADC_MIN 488
|
||||||
|
#define MSIC_DIE_ADC_MAX 1004
|
||||||
|
|
||||||
|
/* This holds the address of the first free ADC channel,
|
||||||
|
* among the 15 channels
|
||||||
|
*/
|
||||||
|
static int channel_index;
|
||||||
|
|
||||||
|
struct platform_info {
|
||||||
|
struct platform_device *pdev;
|
||||||
|
struct thermal_zone_device *tzd[MSIC_THERMAL_SENSORS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct thermal_device_info {
|
||||||
|
unsigned int chnl_addr;
|
||||||
|
int direct;
|
||||||
|
/* This holds the current temperature in millidegree celsius */
|
||||||
|
long curr_temp;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* to_msic_die_temp - converts adc_val to msic_die temperature
|
||||||
|
* @adc_val: ADC value to be converted
|
||||||
|
*
|
||||||
|
* Can sleep
|
||||||
|
*/
|
||||||
|
static int to_msic_die_temp(uint16_t adc_val)
|
||||||
|
{
|
||||||
|
return (368 * (adc_val) / 1000) - 220;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is_valid_adc - checks whether the adc code is within the defined range
|
||||||
|
* @min: minimum value for the sensor
|
||||||
|
* @max: maximum value for the sensor
|
||||||
|
*
|
||||||
|
* Can sleep
|
||||||
|
*/
|
||||||
|
static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max)
|
||||||
|
{
|
||||||
|
return (adc_val >= min) && (adc_val <= max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adc_to_temp - converts the ADC code to temperature in C
|
||||||
|
* @direct: true if ths channel is direct index
|
||||||
|
* @adc_val: the adc_val that needs to be converted
|
||||||
|
* @tp: temperature return value
|
||||||
|
*
|
||||||
|
* Linear approximation is used to covert the skin adc value into temperature.
|
||||||
|
* This technique is used to avoid very long look-up table to get
|
||||||
|
* the appropriate temp value from ADC value.
|
||||||
|
* The adc code vs sensor temp curve is split into five parts
|
||||||
|
* to achieve very close approximate temp value with less than
|
||||||
|
* 0.5C error
|
||||||
|
*/
|
||||||
|
static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp)
|
||||||
|
{
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
/* Direct conversion for die temperature */
|
||||||
|
if (direct) {
|
||||||
|
if (is_valid_adc(adc_val, MSIC_DIE_ADC_MIN, MSIC_DIE_ADC_MAX)) {
|
||||||
|
*tp = to_msic_die_temp(adc_val) * 1000;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_valid_adc(adc_val, ADC_MIN, ADC_MAX))
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
/* Linear approximation for skin temperature */
|
||||||
|
if (adc_val > ADC_VAL0C)
|
||||||
|
temp = 177 - (adc_val/5);
|
||||||
|
else if ((adc_val <= ADC_VAL0C) && (adc_val > ADC_VAL20C))
|
||||||
|
temp = 111 - (adc_val/8);
|
||||||
|
else if ((adc_val <= ADC_VAL20C) && (adc_val > ADC_VAL40C))
|
||||||
|
temp = 92 - (adc_val/10);
|
||||||
|
else if ((adc_val <= ADC_VAL40C) && (adc_val > ADC_VAL60C))
|
||||||
|
temp = 91 - (adc_val/10);
|
||||||
|
else
|
||||||
|
temp = 112 - (adc_val/6);
|
||||||
|
|
||||||
|
/* Convert temperature in celsius to milli degree celsius */
|
||||||
|
*tp = temp * 1000;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mid_read_temp - read sensors for temperature
|
||||||
|
* @temp: holds the current temperature for the sensor after reading
|
||||||
|
*
|
||||||
|
* reads the adc_code from the channel and converts it to real
|
||||||
|
* temperature. The converted value is stored in temp.
|
||||||
|
*
|
||||||
|
* Can sleep
|
||||||
|
*/
|
||||||
|
static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
|
||||||
|
{
|
||||||
|
struct thermal_device_info *td_info = tzd->devdata;
|
||||||
|
uint16_t adc_val, addr;
|
||||||
|
uint8_t data = 0;
|
||||||
|
int ret;
|
||||||
|
unsigned long curr_temp;
|
||||||
|
|
||||||
|
|
||||||
|
addr = td_info->chnl_addr;
|
||||||
|
|
||||||
|
/* Enable the msic for conversion before reading */
|
||||||
|
ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Re-toggle the RRDATARD bit (temporary workaround) */
|
||||||
|
ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Read the higher bits of data */
|
||||||
|
ret = intel_scu_ipc_ioread8(addr, &data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Shift bits to accomodate the lower two data bits */
|
||||||
|
adc_val = (data << 2);
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Adding lower two bits to the higher bits */
|
||||||
|
data &= 03;
|
||||||
|
adc_val += data;
|
||||||
|
|
||||||
|
/* Convert ADC value to temperature */
|
||||||
|
ret = adc_to_temp(td_info->direct, adc_val, &curr_temp);
|
||||||
|
if (ret == 0)
|
||||||
|
*temp = td_info->curr_temp = curr_temp;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* configure_adc - enables/disables the ADC for conversion
|
||||||
|
* @val: zero: disables the ADC non-zero:enables the ADC
|
||||||
|
*
|
||||||
|
* Enable/Disable the ADC depending on the argument
|
||||||
|
*
|
||||||
|
* Can sleep
|
||||||
|
*/
|
||||||
|
static int configure_adc(int val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (val) {
|
||||||
|
/* Enable and start the ADC */
|
||||||
|
data |= (MSIC_ADC_ENBL | MSIC_ADC_START);
|
||||||
|
} else {
|
||||||
|
/* Just stop the ADC */
|
||||||
|
data &= (~MSIC_ADC_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set_up_therm_channel - enable thermal channel for conversion
|
||||||
|
* @base_addr: index of free msic ADC channel
|
||||||
|
*
|
||||||
|
* Enable all the three channels for conversion
|
||||||
|
*
|
||||||
|
* Can sleep
|
||||||
|
*/
|
||||||
|
static int set_up_therm_channel(u16 base_addr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Enable all the sensor channels */
|
||||||
|
ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Since this is the last channel, set the stop bit
|
||||||
|
to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
|
||||||
|
ret = intel_scu_ipc_iowrite8(base_addr + 3,
|
||||||
|
(MSIC_DIE_SENSOR_CODE | 0x10));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Enable ADC and start it */
|
||||||
|
return configure_adc(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reset_stopbit - sets the stop bit to 0 on the given channel
|
||||||
|
* @addr: address of the channel
|
||||||
|
*
|
||||||
|
* Can sleep
|
||||||
|
*/
|
||||||
|
static int reset_stopbit(uint16_t addr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint8_t data;
|
||||||
|
ret = intel_scu_ipc_ioread8(addr, &data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
/* Set the stop bit to zero */
|
||||||
|
return intel_scu_ipc_iowrite8(addr, (data & 0xEF));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find_free_channel - finds an empty channel for conversion
|
||||||
|
*
|
||||||
|
* If the ADC is not enabled then start using 0th channel
|
||||||
|
* itself. Otherwise find an empty channel by looking for a
|
||||||
|
* channel in which the stopbit is set to 1. returns the index
|
||||||
|
* of the first free channel if succeeds or an error code.
|
||||||
|
*
|
||||||
|
* Context: can sleep
|
||||||
|
*
|
||||||
|
* FIXME: Ultimately the channel allocator will move into the intel_scu_ipc
|
||||||
|
* code.
|
||||||
|
*/
|
||||||
|
static int find_free_channel(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
/* check whether ADC is enabled */
|
||||||
|
ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if ((data & MSIC_ADC_ENBL) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* ADC is already enabled; Looking for an empty channel */
|
||||||
|
for (i = 0; i < ADC_CHANLS_MAX; i++) {
|
||||||
|
ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (data & MSIC_STOPBIT_MASK) {
|
||||||
|
ret = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mid_initialize_adc - initializing the ADC
|
||||||
|
* @dev: our device structure
|
||||||
|
*
|
||||||
|
* Initialize the ADC for reading thermistor values. Can sleep.
|
||||||
|
*/
|
||||||
|
static int mid_initialize_adc(struct device *dev)
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
u16 base_addr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that adctherm is disabled before we
|
||||||
|
* initialize the ADC
|
||||||
|
*/
|
||||||
|
ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (data & MSIC_ADCTHERM_MASK)
|
||||||
|
dev_warn(dev, "ADCTHERM already set");
|
||||||
|
|
||||||
|
/* Index of the first channel in which the stop bit is set */
|
||||||
|
channel_index = find_free_channel();
|
||||||
|
if (channel_index < 0) {
|
||||||
|
dev_err(dev, "No free ADC channels");
|
||||||
|
return channel_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
base_addr = ADC_CHNL_START_ADDR + channel_index;
|
||||||
|
|
||||||
|
if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) {
|
||||||
|
/* Reset stop bit for channels other than 0 and 12 */
|
||||||
|
ret = reset_stopbit(base_addr);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Index of the first free channel */
|
||||||
|
base_addr++;
|
||||||
|
channel_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = set_up_therm_channel(base_addr);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "unable to enable ADC");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
dev_dbg(dev, "ADC initialization successful");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize_sensor - sets default temp and timer ranges
|
||||||
|
* @index: index of the sensor
|
||||||
|
*
|
||||||
|
* Context: can sleep
|
||||||
|
*/
|
||||||
|
static struct thermal_device_info *initialize_sensor(int index)
|
||||||
|
{
|
||||||
|
struct thermal_device_info *td_info =
|
||||||
|
kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (!td_info)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Set the base addr of the channel for this sensor */
|
||||||
|
td_info->chnl_addr = ADC_DATA_START_ADDR + 2 * (channel_index + index);
|
||||||
|
/* Sensor 3 is direct conversion */
|
||||||
|
if (index == 3)
|
||||||
|
td_info->direct = 1;
|
||||||
|
return td_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mid_thermal_resume - resume routine
|
||||||
|
* @pdev: platform device structure
|
||||||
|
*
|
||||||
|
* mid thermal resume: re-initializes the adc. Can sleep.
|
||||||
|
*/
|
||||||
|
static int mid_thermal_resume(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return mid_initialize_adc(&pdev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mid_thermal_suspend - suspend routine
|
||||||
|
* @pdev: platform device structure
|
||||||
|
*
|
||||||
|
* mid thermal suspend implements the suspend functionality
|
||||||
|
* by stopping the ADC. Can sleep.
|
||||||
|
*/
|
||||||
|
static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This just stops the ADC and does not disable it.
|
||||||
|
* temporary workaround until we have a generic ADC driver.
|
||||||
|
* If 0 is passed, it disables the ADC.
|
||||||
|
*/
|
||||||
|
return configure_adc(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read_curr_temp - reads the current temperature and stores in temp
|
||||||
|
* @temp: holds the current temperature value after reading
|
||||||
|
*
|
||||||
|
* Can sleep
|
||||||
|
*/
|
||||||
|
static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp)
|
||||||
|
{
|
||||||
|
WARN_ON(tzd == NULL);
|
||||||
|
return mid_read_temp(tzd, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can't be const */
|
||||||
|
static struct thermal_zone_device_ops tzd_ops = {
|
||||||
|
.get_temp = read_curr_temp,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mid_thermal_probe - mfld thermal initialize
|
||||||
|
* @pdev: platform device structure
|
||||||
|
*
|
||||||
|
* mid thermal probe initializes the hardware and registers
|
||||||
|
* all the sensors with the generic thermal framework. Can sleep.
|
||||||
|
*/
|
||||||
|
static int mid_thermal_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
static char *name[MSIC_THERMAL_SENSORS] = {
|
||||||
|
"skin0", "skin1", "sys", "msicdie"
|
||||||
|
};
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
struct platform_info *pinfo;
|
||||||
|
|
||||||
|
pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL);
|
||||||
|
if (!pinfo)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Initializing the hardware */
|
||||||
|
ret = mid_initialize_adc(&pdev->dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "ADC init failed");
|
||||||
|
kfree(pinfo);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register each sensor with the generic thermal framework*/
|
||||||
|
for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
|
||||||
|
pinfo->tzd[i] = thermal_zone_device_register(name[i],
|
||||||
|
0, initialize_sensor(i),
|
||||||
|
&tzd_ops, 0, 0, 0, 0);
|
||||||
|
if (IS_ERR(pinfo->tzd[i]))
|
||||||
|
goto reg_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
pinfo->pdev = pdev;
|
||||||
|
platform_set_drvdata(pdev, pinfo);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
reg_fail:
|
||||||
|
ret = PTR_ERR(pinfo->tzd[i]);
|
||||||
|
while (--i >= 0)
|
||||||
|
thermal_zone_device_unregister(pinfo->tzd[i]);
|
||||||
|
configure_adc(0);
|
||||||
|
kfree(pinfo);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mid_thermal_remove - mfld thermal finalize
|
||||||
|
* @dev: platform device structure
|
||||||
|
*
|
||||||
|
* MLFD thermal remove unregisters all the sensors from the generic
|
||||||
|
* thermal framework. Can sleep.
|
||||||
|
*/
|
||||||
|
static int mid_thermal_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct platform_info *pinfo = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
for (i = 0; i < MSIC_THERMAL_SENSORS; i++)
|
||||||
|
thermal_zone_device_unregister(pinfo->tzd[i]);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
/* Stop the ADC */
|
||||||
|
return configure_adc(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* Driver initialisation and finalization
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#define DRIVER_NAME "msic_sensor"
|
||||||
|
|
||||||
|
static const struct platform_device_id therm_id_table[] = {
|
||||||
|
{ DRIVER_NAME, 1 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver mid_thermal_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = mid_thermal_probe,
|
||||||
|
.suspend = mid_thermal_suspend,
|
||||||
|
.resume = mid_thermal_resume,
|
||||||
|
.remove = __devexit_p(mid_thermal_remove),
|
||||||
|
.id_table = therm_id_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init mid_thermal_module_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&mid_thermal_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit mid_thermal_module_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&mid_thermal_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(mid_thermal_module_init);
|
||||||
|
module_exit(mid_thermal_module_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
|
||||||
|
MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -485,7 +485,7 @@ EXPORT_SYMBOL(rar_lock);
|
||||||
*
|
*
|
||||||
* The register_rar function is to used by other device drivers
|
* The register_rar function is to used by other device drivers
|
||||||
* to ensure that this driver is ready. As we cannot be sure of
|
* to ensure that this driver is ready. As we cannot be sure of
|
||||||
* the compile/execute order of drivers in ther kernel, it is
|
* the compile/execute order of drivers in the kernel, it is
|
||||||
* best to give this driver a callback function to call when
|
* best to give this driver a callback function to call when
|
||||||
* it is ready to give out addresses. The callback function
|
* it is ready to give out addresses. The callback function
|
||||||
* would have those steps that continue the initialization of
|
* would have those steps that continue the initialization of
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* as published by the Free Software Foundation; version 2
|
* as published by the Free Software Foundation; version 2
|
||||||
* of the License.
|
* of the License.
|
||||||
*
|
*
|
||||||
* SCU runing in ARC processor communicates with other entity running in IA
|
* SCU running in ARC processor communicates with other entity running in IA
|
||||||
* core through IPC mechanism which in turn messaging between IA core ad SCU.
|
* core through IPC mechanism which in turn messaging between IA core ad SCU.
|
||||||
* SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and
|
* SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and
|
||||||
* SCU where IPC-2 is used between P-Unit and SCU. This driver delas with
|
* SCU where IPC-2 is used between P-Unit and SCU. This driver delas with
|
||||||
|
|
|
@ -51,6 +51,8 @@
|
||||||
* laptop as MSI S270. YMMV.
|
* laptop as MSI S270. YMMV.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -60,6 +62,8 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/rfkill.h>
|
#include <linux/rfkill.h>
|
||||||
#include <linux/i8042.h>
|
#include <linux/i8042.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/input/sparse-keymap.h>
|
||||||
|
|
||||||
#define MSI_DRIVER_VERSION "0.5"
|
#define MSI_DRIVER_VERSION "0.5"
|
||||||
|
|
||||||
|
@ -78,6 +82,9 @@
|
||||||
#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
|
#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
|
||||||
#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
|
#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
|
||||||
|
|
||||||
|
#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
|
||||||
|
#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
|
||||||
|
|
||||||
static int msi_laptop_resume(struct platform_device *device);
|
static int msi_laptop_resume(struct platform_device *device);
|
||||||
|
|
||||||
#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
|
#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
|
||||||
|
@ -90,6 +97,14 @@ static int auto_brightness;
|
||||||
module_param(auto_brightness, int, 0);
|
module_param(auto_brightness, int, 0);
|
||||||
MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
|
MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
|
||||||
|
|
||||||
|
static const struct key_entry msi_laptop_keymap[] = {
|
||||||
|
{KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */
|
||||||
|
{KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
|
||||||
|
{KE_END, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct input_dev *msi_laptop_input_dev;
|
||||||
|
|
||||||
static bool old_ec_model;
|
static bool old_ec_model;
|
||||||
static int wlan_s, bluetooth_s, threeg_s;
|
static int wlan_s, bluetooth_s, threeg_s;
|
||||||
static int threeg_exists;
|
static int threeg_exists;
|
||||||
|
@ -432,8 +447,7 @@ static struct platform_device *msipf_device;
|
||||||
|
|
||||||
static int dmi_check_cb(const struct dmi_system_id *id)
|
static int dmi_check_cb(const struct dmi_system_id *id)
|
||||||
{
|
{
|
||||||
printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n",
|
pr_info("Identified laptop model '%s'.\n", id->ident);
|
||||||
id->ident);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,6 +619,21 @@ static void msi_update_rfkill(struct work_struct *ignored)
|
||||||
}
|
}
|
||||||
static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
|
static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
|
||||||
|
|
||||||
|
static void msi_send_touchpad_key(struct work_struct *ignored)
|
||||||
|
{
|
||||||
|
u8 rdata;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata);
|
||||||
|
if (result < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sparse_keymap_report_event(msi_laptop_input_dev,
|
||||||
|
(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
|
||||||
|
KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
|
||||||
|
}
|
||||||
|
static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key);
|
||||||
|
|
||||||
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||||
struct serio *port)
|
struct serio *port)
|
||||||
{
|
{
|
||||||
|
@ -613,12 +642,17 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||||
if (str & 0x20)
|
if (str & 0x20)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/
|
/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
|
||||||
if (unlikely(data == 0xe0)) {
|
if (unlikely(data == 0xe0)) {
|
||||||
extended = true;
|
extended = true;
|
||||||
return false;
|
return false;
|
||||||
} else if (unlikely(extended)) {
|
} else if (unlikely(extended)) {
|
||||||
|
extended = false;
|
||||||
switch (data) {
|
switch (data) {
|
||||||
|
case 0xE4:
|
||||||
|
schedule_delayed_work(&msi_touchpad_work,
|
||||||
|
round_jiffies_relative(0.5 * HZ));
|
||||||
|
break;
|
||||||
case 0x54:
|
case 0x54:
|
||||||
case 0x62:
|
case 0x62:
|
||||||
case 0x76:
|
case 0x76:
|
||||||
|
@ -626,7 +660,6 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||||
round_jiffies_relative(0.5 * HZ));
|
round_jiffies_relative(0.5 * HZ));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extended = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -731,6 +764,42 @@ static int msi_laptop_resume(struct platform_device *device)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __init msi_laptop_input_setup(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
msi_laptop_input_dev = input_allocate_device();
|
||||||
|
if (!msi_laptop_input_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
msi_laptop_input_dev->name = "MSI Laptop hotkeys";
|
||||||
|
msi_laptop_input_dev->phys = "msi-laptop/input0";
|
||||||
|
msi_laptop_input_dev->id.bustype = BUS_HOST;
|
||||||
|
|
||||||
|
err = sparse_keymap_setup(msi_laptop_input_dev,
|
||||||
|
msi_laptop_keymap, NULL);
|
||||||
|
if (err)
|
||||||
|
goto err_free_dev;
|
||||||
|
|
||||||
|
err = input_register_device(msi_laptop_input_dev);
|
||||||
|
if (err)
|
||||||
|
goto err_free_keymap;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free_keymap:
|
||||||
|
sparse_keymap_free(msi_laptop_input_dev);
|
||||||
|
err_free_dev:
|
||||||
|
input_free_device(msi_laptop_input_dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msi_laptop_input_destroy(void)
|
||||||
|
{
|
||||||
|
sparse_keymap_free(msi_laptop_input_dev);
|
||||||
|
input_unregister_device(msi_laptop_input_dev);
|
||||||
|
}
|
||||||
|
|
||||||
static int load_scm_model_init(struct platform_device *sdev)
|
static int load_scm_model_init(struct platform_device *sdev)
|
||||||
{
|
{
|
||||||
u8 data;
|
u8 data;
|
||||||
|
@ -759,16 +828,23 @@ static int load_scm_model_init(struct platform_device *sdev)
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
goto fail_rfkill;
|
goto fail_rfkill;
|
||||||
|
|
||||||
|
/* setup input device */
|
||||||
|
result = msi_laptop_input_setup();
|
||||||
|
if (result)
|
||||||
|
goto fail_input;
|
||||||
|
|
||||||
result = i8042_install_filter(msi_laptop_i8042_filter);
|
result = i8042_install_filter(msi_laptop_i8042_filter);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(KERN_ERR
|
pr_err("Unable to install key filter\n");
|
||||||
"msi-laptop: Unable to install key filter\n");
|
|
||||||
goto fail_filter;
|
goto fail_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail_filter:
|
fail_filter:
|
||||||
|
msi_laptop_input_destroy();
|
||||||
|
|
||||||
|
fail_input:
|
||||||
rfkill_cleanup();
|
rfkill_cleanup();
|
||||||
|
|
||||||
fail_rfkill:
|
fail_rfkill:
|
||||||
|
@ -799,7 +875,7 @@ static int __init msi_init(void)
|
||||||
/* Register backlight stuff */
|
/* Register backlight stuff */
|
||||||
|
|
||||||
if (acpi_video_backlight_support()) {
|
if (acpi_video_backlight_support()) {
|
||||||
printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
|
pr_info("Brightness ignored, must be controlled "
|
||||||
"by ACPI video driver\n");
|
"by ACPI video driver\n");
|
||||||
} else {
|
} else {
|
||||||
struct backlight_properties props;
|
struct backlight_properties props;
|
||||||
|
@ -854,7 +930,7 @@ static int __init msi_init(void)
|
||||||
if (auto_brightness != 2)
|
if (auto_brightness != 2)
|
||||||
set_auto_brightness(auto_brightness);
|
set_auto_brightness(auto_brightness);
|
||||||
|
|
||||||
printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
|
pr_info("driver "MSI_DRIVER_VERSION" successfully loaded.\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -886,6 +962,7 @@ static void __exit msi_cleanup(void)
|
||||||
{
|
{
|
||||||
if (load_scm_model) {
|
if (load_scm_model) {
|
||||||
i8042_remove_filter(msi_laptop_i8042_filter);
|
i8042_remove_filter(msi_laptop_i8042_filter);
|
||||||
|
msi_laptop_input_destroy();
|
||||||
cancel_delayed_work_sync(&msi_rfkill_work);
|
cancel_delayed_work_sync(&msi_rfkill_work);
|
||||||
rfkill_cleanup();
|
rfkill_cleanup();
|
||||||
}
|
}
|
||||||
|
@ -901,7 +978,7 @@ static void __exit msi_cleanup(void)
|
||||||
if (auto_brightness != 2)
|
if (auto_brightness != 2)
|
||||||
set_auto_brightness(1);
|
set_auto_brightness(1);
|
||||||
|
|
||||||
printk(KERN_INFO "msi-laptop: driver unloaded.\n");
|
pr_info("driver unloaded.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(msi_init);
|
module_init(msi_init);
|
||||||
|
|
|
@ -0,0 +1,832 @@
|
||||||
|
/*
|
||||||
|
* Samsung Laptop driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de)
|
||||||
|
* Copyright (C) 2009,2011 Novell Inc.
|
||||||
|
*
|
||||||
|
* 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 published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/backlight.h>
|
||||||
|
#include <linux/fb.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/rfkill.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This driver is needed because a number of Samsung laptops do not hook
|
||||||
|
* their control settings through ACPI. So we have to poke around in the
|
||||||
|
* BIOS to do things like brightness values, and "special" key controls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have 0 - 8 as valid brightness levels. The specs say that level 0 should
|
||||||
|
* be reserved by the BIOS (which really doesn't make much sense), we tell
|
||||||
|
* userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
|
||||||
|
*/
|
||||||
|
#define MAX_BRIGHT 0x07
|
||||||
|
|
||||||
|
|
||||||
|
#define SABI_IFACE_MAIN 0x00
|
||||||
|
#define SABI_IFACE_SUB 0x02
|
||||||
|
#define SABI_IFACE_COMPLETE 0x04
|
||||||
|
#define SABI_IFACE_DATA 0x05
|
||||||
|
|
||||||
|
/* Structure to get data back to the calling function */
|
||||||
|
struct sabi_retval {
|
||||||
|
u8 retval[20];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sabi_header_offsets {
|
||||||
|
u8 port;
|
||||||
|
u8 re_mem;
|
||||||
|
u8 iface_func;
|
||||||
|
u8 en_mem;
|
||||||
|
u8 data_offset;
|
||||||
|
u8 data_segment;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sabi_commands {
|
||||||
|
/*
|
||||||
|
* Brightness is 0 - 8, as described above.
|
||||||
|
* Value 0 is for the BIOS to use
|
||||||
|
*/
|
||||||
|
u8 get_brightness;
|
||||||
|
u8 set_brightness;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* first byte:
|
||||||
|
* 0x00 - wireless is off
|
||||||
|
* 0x01 - wireless is on
|
||||||
|
* second byte:
|
||||||
|
* 0x02 - 3G is off
|
||||||
|
* 0x03 - 3G is on
|
||||||
|
* TODO, verify 3G is correct, that doesn't seem right...
|
||||||
|
*/
|
||||||
|
u8 get_wireless_button;
|
||||||
|
u8 set_wireless_button;
|
||||||
|
|
||||||
|
/* 0 is off, 1 is on */
|
||||||
|
u8 get_backlight;
|
||||||
|
u8 set_backlight;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0x80 or 0x00 - no action
|
||||||
|
* 0x81 - recovery key pressed
|
||||||
|
*/
|
||||||
|
u8 get_recovery_mode;
|
||||||
|
u8 set_recovery_mode;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* on seclinux: 0 is low, 1 is high,
|
||||||
|
* on swsmi: 0 is normal, 1 is silent, 2 is turbo
|
||||||
|
*/
|
||||||
|
u8 get_performance_level;
|
||||||
|
u8 set_performance_level;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tell the BIOS that Linux is running on this machine.
|
||||||
|
* 81 is on, 80 is off
|
||||||
|
*/
|
||||||
|
u8 set_linux;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sabi_performance_level {
|
||||||
|
const char *name;
|
||||||
|
u8 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sabi_config {
|
||||||
|
const char *test_string;
|
||||||
|
u16 main_function;
|
||||||
|
const struct sabi_header_offsets header_offsets;
|
||||||
|
const struct sabi_commands commands;
|
||||||
|
const struct sabi_performance_level performance_levels[4];
|
||||||
|
u8 min_brightness;
|
||||||
|
u8 max_brightness;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sabi_config sabi_configs[] = {
|
||||||
|
{
|
||||||
|
.test_string = "SECLINUX",
|
||||||
|
|
||||||
|
.main_function = 0x4c49,
|
||||||
|
|
||||||
|
.header_offsets = {
|
||||||
|
.port = 0x00,
|
||||||
|
.re_mem = 0x02,
|
||||||
|
.iface_func = 0x03,
|
||||||
|
.en_mem = 0x04,
|
||||||
|
.data_offset = 0x05,
|
||||||
|
.data_segment = 0x07,
|
||||||
|
},
|
||||||
|
|
||||||
|
.commands = {
|
||||||
|
.get_brightness = 0x00,
|
||||||
|
.set_brightness = 0x01,
|
||||||
|
|
||||||
|
.get_wireless_button = 0x02,
|
||||||
|
.set_wireless_button = 0x03,
|
||||||
|
|
||||||
|
.get_backlight = 0x04,
|
||||||
|
.set_backlight = 0x05,
|
||||||
|
|
||||||
|
.get_recovery_mode = 0x06,
|
||||||
|
.set_recovery_mode = 0x07,
|
||||||
|
|
||||||
|
.get_performance_level = 0x08,
|
||||||
|
.set_performance_level = 0x09,
|
||||||
|
|
||||||
|
.set_linux = 0x0a,
|
||||||
|
},
|
||||||
|
|
||||||
|
.performance_levels = {
|
||||||
|
{
|
||||||
|
.name = "silent",
|
||||||
|
.value = 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "normal",
|
||||||
|
.value = 1,
|
||||||
|
},
|
||||||
|
{ },
|
||||||
|
},
|
||||||
|
.min_brightness = 1,
|
||||||
|
.max_brightness = 8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.test_string = "SwSmi@",
|
||||||
|
|
||||||
|
.main_function = 0x5843,
|
||||||
|
|
||||||
|
.header_offsets = {
|
||||||
|
.port = 0x00,
|
||||||
|
.re_mem = 0x04,
|
||||||
|
.iface_func = 0x02,
|
||||||
|
.en_mem = 0x03,
|
||||||
|
.data_offset = 0x05,
|
||||||
|
.data_segment = 0x07,
|
||||||
|
},
|
||||||
|
|
||||||
|
.commands = {
|
||||||
|
.get_brightness = 0x10,
|
||||||
|
.set_brightness = 0x11,
|
||||||
|
|
||||||
|
.get_wireless_button = 0x12,
|
||||||
|
.set_wireless_button = 0x13,
|
||||||
|
|
||||||
|
.get_backlight = 0x2d,
|
||||||
|
.set_backlight = 0x2e,
|
||||||
|
|
||||||
|
.get_recovery_mode = 0xff,
|
||||||
|
.set_recovery_mode = 0xff,
|
||||||
|
|
||||||
|
.get_performance_level = 0x31,
|
||||||
|
.set_performance_level = 0x32,
|
||||||
|
|
||||||
|
.set_linux = 0xff,
|
||||||
|
},
|
||||||
|
|
||||||
|
.performance_levels = {
|
||||||
|
{
|
||||||
|
.name = "normal",
|
||||||
|
.value = 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "silent",
|
||||||
|
.value = 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "overclock",
|
||||||
|
.value = 2,
|
||||||
|
},
|
||||||
|
{ },
|
||||||
|
},
|
||||||
|
.min_brightness = 0,
|
||||||
|
.max_brightness = 8,
|
||||||
|
},
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sabi_config *sabi_config;
|
||||||
|
|
||||||
|
static void __iomem *sabi;
|
||||||
|
static void __iomem *sabi_iface;
|
||||||
|
static void __iomem *f0000_segment;
|
||||||
|
static struct backlight_device *backlight_device;
|
||||||
|
static struct mutex sabi_mutex;
|
||||||
|
static struct platform_device *sdev;
|
||||||
|
static struct rfkill *rfk;
|
||||||
|
|
||||||
|
static int force;
|
||||||
|
module_param(force, bool, 0);
|
||||||
|
MODULE_PARM_DESC(force,
|
||||||
|
"Disable the DMI check and forces the driver to be loaded");
|
||||||
|
|
||||||
|
static int debug;
|
||||||
|
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||||
|
MODULE_PARM_DESC(debug, "Debug enabled or not");
|
||||||
|
|
||||||
|
static int sabi_get_command(u8 command, struct sabi_retval *sretval)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
u16 port = readw(sabi + sabi_config->header_offsets.port);
|
||||||
|
u8 complete, iface_data;
|
||||||
|
|
||||||
|
mutex_lock(&sabi_mutex);
|
||||||
|
|
||||||
|
/* enable memory to be able to write to it */
|
||||||
|
outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
|
||||||
|
|
||||||
|
/* write out the command */
|
||||||
|
writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
|
||||||
|
writew(command, sabi_iface + SABI_IFACE_SUB);
|
||||||
|
writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
|
||||||
|
outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
|
||||||
|
|
||||||
|
/* write protect memory to make it safe */
|
||||||
|
outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
|
||||||
|
|
||||||
|
/* see if the command actually succeeded */
|
||||||
|
complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
|
||||||
|
iface_data = readb(sabi_iface + SABI_IFACE_DATA);
|
||||||
|
if (complete != 0xaa || iface_data == 0xff) {
|
||||||
|
pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
|
||||||
|
command, complete, iface_data);
|
||||||
|
retval = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Save off the data into a structure so the caller use it.
|
||||||
|
* Right now we only want the first 4 bytes,
|
||||||
|
* There are commands that need more, but not for the ones we
|
||||||
|
* currently care about.
|
||||||
|
*/
|
||||||
|
sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
|
||||||
|
sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
|
||||||
|
sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
|
||||||
|
sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
mutex_unlock(&sabi_mutex);
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sabi_set_command(u8 command, u8 data)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
u16 port = readw(sabi + sabi_config->header_offsets.port);
|
||||||
|
u8 complete, iface_data;
|
||||||
|
|
||||||
|
mutex_lock(&sabi_mutex);
|
||||||
|
|
||||||
|
/* enable memory to be able to write to it */
|
||||||
|
outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
|
||||||
|
|
||||||
|
/* write out the command */
|
||||||
|
writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
|
||||||
|
writew(command, sabi_iface + SABI_IFACE_SUB);
|
||||||
|
writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
|
||||||
|
writeb(data, sabi_iface + SABI_IFACE_DATA);
|
||||||
|
outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
|
||||||
|
|
||||||
|
/* write protect memory to make it safe */
|
||||||
|
outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
|
||||||
|
|
||||||
|
/* see if the command actually succeeded */
|
||||||
|
complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
|
||||||
|
iface_data = readb(sabi_iface + SABI_IFACE_DATA);
|
||||||
|
if (complete != 0xaa || iface_data == 0xff) {
|
||||||
|
pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
|
||||||
|
command, complete, iface_data);
|
||||||
|
retval = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&sabi_mutex);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_backlight(void)
|
||||||
|
{
|
||||||
|
struct sabi_retval sretval;
|
||||||
|
|
||||||
|
sabi_get_command(sabi_config->commands.get_backlight, &sretval);
|
||||||
|
printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
|
||||||
|
|
||||||
|
sabi_set_command(sabi_config->commands.set_backlight, 0);
|
||||||
|
printk(KERN_DEBUG "backlight should be off\n");
|
||||||
|
|
||||||
|
sabi_get_command(sabi_config->commands.get_backlight, &sretval);
|
||||||
|
printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
|
||||||
|
|
||||||
|
msleep(1000);
|
||||||
|
|
||||||
|
sabi_set_command(sabi_config->commands.set_backlight, 1);
|
||||||
|
printk(KERN_DEBUG "backlight should be on\n");
|
||||||
|
|
||||||
|
sabi_get_command(sabi_config->commands.get_backlight, &sretval);
|
||||||
|
printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_wireless(void)
|
||||||
|
{
|
||||||
|
struct sabi_retval sretval;
|
||||||
|
|
||||||
|
sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
|
||||||
|
printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
|
||||||
|
|
||||||
|
sabi_set_command(sabi_config->commands.set_wireless_button, 0);
|
||||||
|
printk(KERN_DEBUG "wireless led should be off\n");
|
||||||
|
|
||||||
|
sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
|
||||||
|
printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
|
||||||
|
|
||||||
|
msleep(1000);
|
||||||
|
|
||||||
|
sabi_set_command(sabi_config->commands.set_wireless_button, 1);
|
||||||
|
printk(KERN_DEBUG "wireless led should be on\n");
|
||||||
|
|
||||||
|
sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
|
||||||
|
printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 read_brightness(void)
|
||||||
|
{
|
||||||
|
struct sabi_retval sretval;
|
||||||
|
int user_brightness = 0;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = sabi_get_command(sabi_config->commands.get_brightness,
|
||||||
|
&sretval);
|
||||||
|
if (!retval) {
|
||||||
|
user_brightness = sretval.retval[0];
|
||||||
|
if (user_brightness != 0)
|
||||||
|
user_brightness -= sabi_config->min_brightness;
|
||||||
|
}
|
||||||
|
return user_brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_brightness(u8 user_brightness)
|
||||||
|
{
|
||||||
|
u8 user_level = user_brightness - sabi_config->min_brightness;
|
||||||
|
|
||||||
|
sabi_set_command(sabi_config->commands.set_brightness, user_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_brightness(struct backlight_device *bd)
|
||||||
|
{
|
||||||
|
return (int)read_brightness();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int update_status(struct backlight_device *bd)
|
||||||
|
{
|
||||||
|
set_brightness(bd->props.brightness);
|
||||||
|
|
||||||
|
if (bd->props.power == FB_BLANK_UNBLANK)
|
||||||
|
sabi_set_command(sabi_config->commands.set_backlight, 1);
|
||||||
|
else
|
||||||
|
sabi_set_command(sabi_config->commands.set_backlight, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct backlight_ops backlight_ops = {
|
||||||
|
.get_brightness = get_brightness,
|
||||||
|
.update_status = update_status,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rfkill_set(void *data, bool blocked)
|
||||||
|
{
|
||||||
|
/* Do something with blocked...*/
|
||||||
|
/*
|
||||||
|
* blocked == false is on
|
||||||
|
* blocked == true is off
|
||||||
|
*/
|
||||||
|
if (blocked)
|
||||||
|
sabi_set_command(sabi_config->commands.set_wireless_button, 0);
|
||||||
|
else
|
||||||
|
sabi_set_command(sabi_config->commands.set_wireless_button, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rfkill_ops rfkill_ops = {
|
||||||
|
.set_block = rfkill_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int init_wireless(struct platform_device *sdev)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
|
||||||
|
&rfkill_ops, NULL);
|
||||||
|
if (!rfk)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
retval = rfkill_register(rfk);
|
||||||
|
if (retval) {
|
||||||
|
rfkill_destroy(rfk);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroy_wireless(void)
|
||||||
|
{
|
||||||
|
rfkill_unregister(rfk);
|
||||||
|
rfkill_destroy(rfk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t get_performance_level(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct sabi_retval sretval;
|
||||||
|
int retval;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Read the state */
|
||||||
|
retval = sabi_get_command(sabi_config->commands.get_performance_level,
|
||||||
|
&sretval);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/* The logic is backwards, yeah, lots of fun... */
|
||||||
|
for (i = 0; sabi_config->performance_levels[i].name; ++i) {
|
||||||
|
if (sretval.retval[0] == sabi_config->performance_levels[i].value)
|
||||||
|
return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name);
|
||||||
|
}
|
||||||
|
return sprintf(buf, "%s\n", "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t set_performance_level(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
if (count >= 1) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; sabi_config->performance_levels[i].name; ++i) {
|
||||||
|
const struct sabi_performance_level *level =
|
||||||
|
&sabi_config->performance_levels[i];
|
||||||
|
if (!strncasecmp(level->name, buf, strlen(level->name))) {
|
||||||
|
sabi_set_command(sabi_config->commands.set_performance_level,
|
||||||
|
level->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!sabi_config->performance_levels[i].name)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
|
||||||
|
get_performance_level, set_performance_level);
|
||||||
|
|
||||||
|
|
||||||
|
static int __init dmi_check_cb(const struct dmi_system_id *id)
|
||||||
|
{
|
||||||
|
pr_info("found laptop model '%s'\n",
|
||||||
|
id->ident);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dmi_system_id __initdata samsung_dmi_table[] = {
|
||||||
|
{
|
||||||
|
.ident = "N128",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "N128"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "N130",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "N130"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "X125",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "X125"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "X120/X170",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "NC10",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "NC10"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "NP-Q45",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "X360",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "X360"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "R518",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "R518"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "R519/R719",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "N150/N210/N220",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "N150P/N210P/N220P",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "R530/R730",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "NF110/NF210/NF310",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "N145P/N250P/N260P",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "R70/R71",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR,
|
||||||
|
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ident = "P460",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
|
||||||
|
DMI_MATCH(DMI_BOARD_NAME, "P460"),
|
||||||
|
},
|
||||||
|
.callback = dmi_check_cb,
|
||||||
|
},
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
|
||||||
|
|
||||||
|
static int find_signature(void __iomem *memcheck, const char *testStr)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int loca;
|
||||||
|
|
||||||
|
for (loca = 0; loca < 0xffff; loca++) {
|
||||||
|
char temp = readb(memcheck + loca);
|
||||||
|
|
||||||
|
if (temp == testStr[i]) {
|
||||||
|
if (i == strlen(testStr)-1)
|
||||||
|
break;
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loca;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init samsung_init(void)
|
||||||
|
{
|
||||||
|
struct backlight_properties props;
|
||||||
|
struct sabi_retval sretval;
|
||||||
|
unsigned int ifaceP;
|
||||||
|
int i;
|
||||||
|
int loca;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
mutex_init(&sabi_mutex);
|
||||||
|
|
||||||
|
if (!force && !dmi_check_system(samsung_dmi_table))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
f0000_segment = ioremap_nocache(0xf0000, 0xffff);
|
||||||
|
if (!f0000_segment) {
|
||||||
|
pr_err("Can't map the segment at 0xf0000\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to find one of the signatures in memory to find the header */
|
||||||
|
for (i = 0; sabi_configs[i].test_string != 0; ++i) {
|
||||||
|
sabi_config = &sabi_configs[i];
|
||||||
|
loca = find_signature(f0000_segment, sabi_config->test_string);
|
||||||
|
if (loca != 0xffff)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loca == 0xffff) {
|
||||||
|
pr_err("This computer does not support SABI\n");
|
||||||
|
goto error_no_signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* point to the SMI port Number */
|
||||||
|
loca += 1;
|
||||||
|
sabi = (f0000_segment + loca);
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
printk(KERN_DEBUG "This computer supports SABI==%x\n",
|
||||||
|
loca + 0xf0000 - 6);
|
||||||
|
printk(KERN_DEBUG "SABI header:\n");
|
||||||
|
printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
|
||||||
|
readw(sabi + sabi_config->header_offsets.port));
|
||||||
|
printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
|
||||||
|
readb(sabi + sabi_config->header_offsets.iface_func));
|
||||||
|
printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
|
||||||
|
readb(sabi + sabi_config->header_offsets.en_mem));
|
||||||
|
printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
|
||||||
|
readb(sabi + sabi_config->header_offsets.re_mem));
|
||||||
|
printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
|
||||||
|
readw(sabi + sabi_config->header_offsets.data_offset));
|
||||||
|
printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
|
||||||
|
readw(sabi + sabi_config->header_offsets.data_segment));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a pointer to the SABI Interface */
|
||||||
|
ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4;
|
||||||
|
ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff;
|
||||||
|
sabi_iface = ioremap_nocache(ifaceP, 16);
|
||||||
|
if (!sabi_iface) {
|
||||||
|
pr_err("Can't remap %x\n", ifaceP);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (debug) {
|
||||||
|
printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
|
||||||
|
printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
|
||||||
|
|
||||||
|
test_backlight();
|
||||||
|
test_wireless();
|
||||||
|
|
||||||
|
retval = sabi_get_command(sabi_config->commands.get_brightness,
|
||||||
|
&sretval);
|
||||||
|
printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn on "Linux" mode in the BIOS */
|
||||||
|
if (sabi_config->commands.set_linux != 0xff) {
|
||||||
|
retval = sabi_set_command(sabi_config->commands.set_linux,
|
||||||
|
0x81);
|
||||||
|
if (retval) {
|
||||||
|
pr_warn("Linux mode was not set!\n");
|
||||||
|
goto error_no_platform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* knock up a platform device to hang stuff off of */
|
||||||
|
sdev = platform_device_register_simple("samsung", -1, NULL, 0);
|
||||||
|
if (IS_ERR(sdev))
|
||||||
|
goto error_no_platform;
|
||||||
|
|
||||||
|
/* create a backlight device to talk to this one */
|
||||||
|
memset(&props, 0, sizeof(struct backlight_properties));
|
||||||
|
props.max_brightness = sabi_config->max_brightness;
|
||||||
|
backlight_device = backlight_device_register("samsung", &sdev->dev,
|
||||||
|
NULL, &backlight_ops,
|
||||||
|
&props);
|
||||||
|
if (IS_ERR(backlight_device))
|
||||||
|
goto error_no_backlight;
|
||||||
|
|
||||||
|
backlight_device->props.brightness = read_brightness();
|
||||||
|
backlight_device->props.power = FB_BLANK_UNBLANK;
|
||||||
|
backlight_update_status(backlight_device);
|
||||||
|
|
||||||
|
retval = init_wireless(sdev);
|
||||||
|
if (retval)
|
||||||
|
goto error_no_rfk;
|
||||||
|
|
||||||
|
retval = device_create_file(&sdev->dev, &dev_attr_performance_level);
|
||||||
|
if (retval)
|
||||||
|
goto error_file_create;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error_file_create:
|
||||||
|
destroy_wireless();
|
||||||
|
|
||||||
|
error_no_rfk:
|
||||||
|
backlight_device_unregister(backlight_device);
|
||||||
|
|
||||||
|
error_no_backlight:
|
||||||
|
platform_device_unregister(sdev);
|
||||||
|
|
||||||
|
error_no_platform:
|
||||||
|
iounmap(sabi_iface);
|
||||||
|
|
||||||
|
error_no_signature:
|
||||||
|
iounmap(f0000_segment);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit samsung_exit(void)
|
||||||
|
{
|
||||||
|
/* Turn off "Linux" mode in the BIOS */
|
||||||
|
if (sabi_config->commands.set_linux != 0xff)
|
||||||
|
sabi_set_command(sabi_config->commands.set_linux, 0x80);
|
||||||
|
|
||||||
|
device_remove_file(&sdev->dev, &dev_attr_performance_level);
|
||||||
|
backlight_device_unregister(backlight_device);
|
||||||
|
destroy_wireless();
|
||||||
|
iounmap(sabi_iface);
|
||||||
|
iounmap(f0000_segment);
|
||||||
|
platform_device_unregister(sdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(samsung_init);
|
||||||
|
module_exit(samsung_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
|
||||||
|
MODULE_DESCRIPTION("Samsung Backlight driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -71,8 +71,9 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define DRV_PFX "sony-laptop: "
|
#define DRV_PFX "sony-laptop: "
|
||||||
#define dprintk(msg...) do { \
|
#define dprintk(msg...) do { \
|
||||||
if (debug) printk(KERN_WARNING DRV_PFX msg); \
|
if (debug) \
|
||||||
|
pr_warn(DRV_PFX msg); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define SONY_LAPTOP_DRIVER_VERSION "0.6"
|
#define SONY_LAPTOP_DRIVER_VERSION "0.6"
|
||||||
|
@ -124,6 +125,19 @@ MODULE_PARM_DESC(minor,
|
||||||
"default is -1 (automatic)");
|
"default is -1 (automatic)");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int kbd_backlight; /* = 1 */
|
||||||
|
module_param(kbd_backlight, int, 0444);
|
||||||
|
MODULE_PARM_DESC(kbd_backlight,
|
||||||
|
"set this to 0 to disable keyboard backlight, "
|
||||||
|
"1 to enable it (default: 0)");
|
||||||
|
|
||||||
|
static int kbd_backlight_timeout; /* = 0 */
|
||||||
|
module_param(kbd_backlight_timeout, int, 0444);
|
||||||
|
MODULE_PARM_DESC(kbd_backlight_timeout,
|
||||||
|
"set this to 0 to set the default 10 seconds timeout, "
|
||||||
|
"1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
|
||||||
|
"(default: 0)");
|
||||||
|
|
||||||
enum sony_nc_rfkill {
|
enum sony_nc_rfkill {
|
||||||
SONY_WIFI,
|
SONY_WIFI,
|
||||||
SONY_BLUETOOTH,
|
SONY_BLUETOOTH,
|
||||||
|
@ -402,7 +416,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
|
||||||
error = kfifo_alloc(&sony_laptop_input.fifo,
|
error = kfifo_alloc(&sony_laptop_input.fifo,
|
||||||
SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
|
SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
|
||||||
if (error) {
|
if (error) {
|
||||||
printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
|
pr_err(DRV_PFX "kfifo_alloc failed\n");
|
||||||
goto err_dec_users;
|
goto err_dec_users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,7 +605,7 @@ struct sony_nc_value {
|
||||||
int value; /* current setting */
|
int value; /* current setting */
|
||||||
int valid; /* Has ever been set */
|
int valid; /* Has ever been set */
|
||||||
int debug; /* active only in debug mode ? */
|
int debug; /* active only in debug mode ? */
|
||||||
struct device_attribute devattr; /* sysfs atribute */
|
struct device_attribute devattr; /* sysfs attribute */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SNC_HANDLE_NAMES(_name, _values...) \
|
#define SNC_HANDLE_NAMES(_name, _values...) \
|
||||||
|
@ -686,7 +700,7 @@ static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
|
pr_warn(DRV_PFX "acpi_callreadfunc failed\n");
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -712,7 +726,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
|
||||||
if (status == AE_OK) {
|
if (status == AE_OK) {
|
||||||
if (result != NULL) {
|
if (result != NULL) {
|
||||||
if (out_obj.type != ACPI_TYPE_INTEGER) {
|
if (out_obj.type != ACPI_TYPE_INTEGER) {
|
||||||
printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
|
pr_warn(DRV_PFX "acpi_evaluate_object bad "
|
||||||
"return type\n");
|
"return type\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -721,34 +735,103 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
|
pr_warn(DRV_PFX "acpi_evaluate_object failed\n");
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sony_nc_handles {
|
||||||
|
u16 cap[0x10];
|
||||||
|
struct device_attribute devattr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sony_nc_handles *handles;
|
||||||
|
|
||||||
|
static ssize_t sony_nc_handles_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buffer)
|
||||||
|
{
|
||||||
|
ssize_t len = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
|
||||||
|
len += snprintf(buffer + len, PAGE_SIZE - len, "0x%.4x ",
|
||||||
|
handles->cap[i]);
|
||||||
|
}
|
||||||
|
len += snprintf(buffer + len, PAGE_SIZE - len, "\n");
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_handles_setup(struct platform_device *pd)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
handles = kzalloc(sizeof(*handles), GFP_KERNEL);
|
||||||
|
if (!handles)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sysfs_attr_init(&handles->devattr.attr);
|
||||||
|
handles->devattr.attr.name = "handles";
|
||||||
|
handles->devattr.attr.mode = S_IRUGO;
|
||||||
|
handles->devattr.show = sony_nc_handles_show;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
|
||||||
|
if (!acpi_callsetfunc(sony_nc_acpi_handle,
|
||||||
|
"SN00", i + 0x20, &result)) {
|
||||||
|
dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n",
|
||||||
|
result, i);
|
||||||
|
handles->cap[i] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allow reading capabilities via sysfs */
|
||||||
|
if (device_create_file(&pd->dev, &handles->devattr)) {
|
||||||
|
kfree(handles);
|
||||||
|
handles = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_handles_cleanup(struct platform_device *pd)
|
||||||
|
{
|
||||||
|
if (handles) {
|
||||||
|
device_remove_file(&pd->dev, &handles->devattr);
|
||||||
|
kfree(handles);
|
||||||
|
handles = NULL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int sony_find_snc_handle(int handle)
|
static int sony_find_snc_handle(int handle)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int result;
|
for (i = 0; i < 0x10; i++) {
|
||||||
|
if (handles->cap[i] == handle) {
|
||||||
for (i = 0x20; i < 0x30; i++) {
|
dprintk("found handle 0x%.4x (offset: 0x%.2x)\n",
|
||||||
acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result);
|
handle, i);
|
||||||
if (result == handle)
|
return i;
|
||||||
return i-0x20;
|
}
|
||||||
}
|
}
|
||||||
|
dprintk("handle 0x%.4x not found\n", handle);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sony_call_snc_handle(int handle, int argument, int *result)
|
static int sony_call_snc_handle(int handle, int argument, int *result)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
int offset = sony_find_snc_handle(handle);
|
int offset = sony_find_snc_handle(handle);
|
||||||
|
|
||||||
if (offset < 0)
|
if (offset < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument,
|
ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument,
|
||||||
result);
|
result);
|
||||||
|
dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument,
|
||||||
|
*result);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -857,11 +940,39 @@ static int sony_backlight_get_brightness(struct backlight_device *bd)
|
||||||
return value - 1;
|
return value - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct backlight_device *sony_backlight_device;
|
static int sony_nc_get_brightness_ng(struct backlight_device *bd)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int *handle = (int *)bl_get_data(bd);
|
||||||
|
|
||||||
|
sony_call_snc_handle(*handle, 0x0200, &result);
|
||||||
|
|
||||||
|
return result & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_update_status_ng(struct backlight_device *bd)
|
||||||
|
{
|
||||||
|
int value, result;
|
||||||
|
int *handle = (int *)bl_get_data(bd);
|
||||||
|
|
||||||
|
value = bd->props.brightness;
|
||||||
|
sony_call_snc_handle(*handle, 0x0100 | (value << 16), &result);
|
||||||
|
|
||||||
|
return sony_nc_get_brightness_ng(bd);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct backlight_ops sony_backlight_ops = {
|
static const struct backlight_ops sony_backlight_ops = {
|
||||||
|
.options = BL_CORE_SUSPENDRESUME,
|
||||||
.update_status = sony_backlight_update_status,
|
.update_status = sony_backlight_update_status,
|
||||||
.get_brightness = sony_backlight_get_brightness,
|
.get_brightness = sony_backlight_get_brightness,
|
||||||
};
|
};
|
||||||
|
static const struct backlight_ops sony_backlight_ng_ops = {
|
||||||
|
.options = BL_CORE_SUSPENDRESUME,
|
||||||
|
.update_status = sony_nc_update_status_ng,
|
||||||
|
.get_brightness = sony_nc_get_brightness_ng,
|
||||||
|
};
|
||||||
|
static int backlight_ng_handle;
|
||||||
|
static struct backlight_device *sony_backlight_device;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* New SNC-only Vaios event mapping to driver known keys
|
* New SNC-only Vaios event mapping to driver known keys
|
||||||
|
@ -972,7 +1083,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key_event->data)
|
if (!key_event->data)
|
||||||
printk(KERN_INFO DRV_PFX
|
pr_info(DRV_PFX
|
||||||
"Unknown event: 0x%x 0x%x\n",
|
"Unknown event: 0x%x 0x%x\n",
|
||||||
key_handle,
|
key_handle,
|
||||||
ev);
|
ev);
|
||||||
|
@ -996,7 +1107,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
|
||||||
struct acpi_device_info *info;
|
struct acpi_device_info *info;
|
||||||
|
|
||||||
if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) {
|
if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) {
|
||||||
printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n",
|
pr_warn(DRV_PFX "method: name: %4.4s, args %X\n",
|
||||||
(char *)&info->name, info->param_count);
|
(char *)&info->name, info->param_count);
|
||||||
|
|
||||||
kfree(info);
|
kfree(info);
|
||||||
|
@ -1037,7 +1148,7 @@ static int sony_nc_resume(struct acpi_device *device)
|
||||||
ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
|
ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
|
||||||
item->value, NULL);
|
item->value, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printk("%s: %d\n", __func__, ret);
|
pr_err(DRV_PFX "%s: %d\n", __func__, ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1054,11 +1165,6 @@ static int sony_nc_resume(struct acpi_device *device)
|
||||||
sony_nc_function_setup(device);
|
sony_nc_function_setup(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set the last requested brightness level */
|
|
||||||
if (sony_backlight_device &&
|
|
||||||
sony_backlight_update_status(sony_backlight_device) < 0)
|
|
||||||
printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
|
|
||||||
|
|
||||||
/* re-read rfkill state */
|
/* re-read rfkill state */
|
||||||
sony_nc_rfkill_update();
|
sony_nc_rfkill_update();
|
||||||
|
|
||||||
|
@ -1206,12 +1312,12 @@ static void sony_nc_rfkill_setup(struct acpi_device *device)
|
||||||
|
|
||||||
device_enum = (union acpi_object *) buffer.pointer;
|
device_enum = (union acpi_object *) buffer.pointer;
|
||||||
if (!device_enum) {
|
if (!device_enum) {
|
||||||
pr_err("Invalid SN06 return object\n");
|
pr_err(DRV_PFX "No SN06 return object.");
|
||||||
goto out_no_enum;
|
goto out_no_enum;
|
||||||
}
|
}
|
||||||
if (device_enum->type != ACPI_TYPE_BUFFER) {
|
if (device_enum->type != ACPI_TYPE_BUFFER) {
|
||||||
pr_err("Invalid SN06 return object type 0x%.2x\n",
|
pr_err(DRV_PFX "Invalid SN06 return object 0x%.2x\n",
|
||||||
device_enum->type);
|
device_enum->type);
|
||||||
goto out_no_enum;
|
goto out_no_enum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1245,6 +1351,209 @@ out_no_enum:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Keyboard backlight feature */
|
||||||
|
#define KBDBL_HANDLER 0x137
|
||||||
|
#define KBDBL_PRESENT 0xB00
|
||||||
|
#define SET_MODE 0xC00
|
||||||
|
#define SET_TIMEOUT 0xE00
|
||||||
|
|
||||||
|
struct kbd_backlight {
|
||||||
|
int mode;
|
||||||
|
int timeout;
|
||||||
|
struct device_attribute mode_attr;
|
||||||
|
struct device_attribute timeout_attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct kbd_backlight *kbdbl_handle;
|
||||||
|
|
||||||
|
static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (value > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (sony_call_snc_handle(KBDBL_HANDLER,
|
||||||
|
(value << 0x10) | SET_MODE, &result))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
kbdbl_handle->mode = value;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buffer, size_t count)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
unsigned long value;
|
||||||
|
|
||||||
|
if (count > 31)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (strict_strtoul(buffer, 10, &value))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = __sony_nc_kbd_backlight_mode_set(value);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buffer)
|
||||||
|
{
|
||||||
|
ssize_t count = 0;
|
||||||
|
count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __sony_nc_kbd_backlight_timeout_set(u8 value)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (value > 3)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (sony_call_snc_handle(KBDBL_HANDLER,
|
||||||
|
(value << 0x10) | SET_TIMEOUT, &result))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
kbdbl_handle->timeout = value;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buffer, size_t count)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
unsigned long value;
|
||||||
|
|
||||||
|
if (count > 31)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (strict_strtoul(buffer, 10, &value))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = __sony_nc_kbd_backlight_timeout_set(value);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buffer)
|
||||||
|
{
|
||||||
|
ssize_t count = 0;
|
||||||
|
count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_kbd_backlight_setup(struct platform_device *pd)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (sony_call_snc_handle(0x137, KBDBL_PRESENT, &result))
|
||||||
|
return 0;
|
||||||
|
if (!(result & 0x02))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL);
|
||||||
|
if (!kbdbl_handle)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sysfs_attr_init(&kbdbl_handle->mode_attr.attr);
|
||||||
|
kbdbl_handle->mode_attr.attr.name = "kbd_backlight";
|
||||||
|
kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR;
|
||||||
|
kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show;
|
||||||
|
kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store;
|
||||||
|
|
||||||
|
sysfs_attr_init(&kbdbl_handle->timeout_attr.attr);
|
||||||
|
kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout";
|
||||||
|
kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR;
|
||||||
|
kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show;
|
||||||
|
kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store;
|
||||||
|
|
||||||
|
if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr))
|
||||||
|
goto outkzalloc;
|
||||||
|
|
||||||
|
if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr))
|
||||||
|
goto outmode;
|
||||||
|
|
||||||
|
__sony_nc_kbd_backlight_mode_set(kbd_backlight);
|
||||||
|
__sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
outmode:
|
||||||
|
device_remove_file(&pd->dev, &kbdbl_handle->mode_attr);
|
||||||
|
outkzalloc:
|
||||||
|
kfree(kbdbl_handle);
|
||||||
|
kbdbl_handle = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
|
||||||
|
{
|
||||||
|
if (kbdbl_handle) {
|
||||||
|
device_remove_file(&pd->dev, &kbdbl_handle->mode_attr);
|
||||||
|
device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr);
|
||||||
|
kfree(kbdbl_handle);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sony_nc_backlight_setup(void)
|
||||||
|
{
|
||||||
|
acpi_handle unused;
|
||||||
|
int max_brightness = 0;
|
||||||
|
const struct backlight_ops *ops = NULL;
|
||||||
|
struct backlight_properties props;
|
||||||
|
|
||||||
|
if (sony_find_snc_handle(0x12f) != -1) {
|
||||||
|
backlight_ng_handle = 0x12f;
|
||||||
|
ops = &sony_backlight_ng_ops;
|
||||||
|
max_brightness = 0xff;
|
||||||
|
|
||||||
|
} else if (sony_find_snc_handle(0x137) != -1) {
|
||||||
|
backlight_ng_handle = 0x137;
|
||||||
|
ops = &sony_backlight_ng_ops;
|
||||||
|
max_brightness = 0xff;
|
||||||
|
|
||||||
|
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
|
||||||
|
&unused))) {
|
||||||
|
ops = &sony_backlight_ops;
|
||||||
|
max_brightness = SONY_MAX_BRIGHTNESS - 1;
|
||||||
|
|
||||||
|
} else
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(&props, 0, sizeof(struct backlight_properties));
|
||||||
|
props.type = BACKLIGHT_PLATFORM;
|
||||||
|
props.max_brightness = max_brightness;
|
||||||
|
sony_backlight_device = backlight_device_register("sony", NULL,
|
||||||
|
&backlight_ng_handle,
|
||||||
|
ops, &props);
|
||||||
|
|
||||||
|
if (IS_ERR(sony_backlight_device)) {
|
||||||
|
pr_warning(DRV_PFX "unable to register backlight device\n");
|
||||||
|
sony_backlight_device = NULL;
|
||||||
|
} else
|
||||||
|
sony_backlight_device->props.brightness =
|
||||||
|
ops->get_brightness(sony_backlight_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sony_nc_backlight_cleanup(void)
|
||||||
|
{
|
||||||
|
if (sony_backlight_device)
|
||||||
|
backlight_device_unregister(sony_backlight_device);
|
||||||
|
}
|
||||||
|
|
||||||
static int sony_nc_add(struct acpi_device *device)
|
static int sony_nc_add(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
|
@ -1252,8 +1561,8 @@ static int sony_nc_add(struct acpi_device *device)
|
||||||
acpi_handle handle;
|
acpi_handle handle;
|
||||||
struct sony_nc_value *item;
|
struct sony_nc_value *item;
|
||||||
|
|
||||||
printk(KERN_INFO DRV_PFX "%s v%s.\n",
|
pr_info(DRV_PFX "%s v%s.\n", SONY_NC_DRIVER_NAME,
|
||||||
SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
|
SONY_LAPTOP_DRIVER_VERSION);
|
||||||
|
|
||||||
sony_nc_acpi_device = device;
|
sony_nc_acpi_device = device;
|
||||||
strcpy(acpi_device_class(device), "sony/hotkey");
|
strcpy(acpi_device_class(device), "sony/hotkey");
|
||||||
|
@ -1269,13 +1578,18 @@ static int sony_nc_add(struct acpi_device *device)
|
||||||
goto outwalk;
|
goto outwalk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = sony_pf_add();
|
||||||
|
if (result)
|
||||||
|
goto outpresent;
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
|
status = acpi_walk_namespace(ACPI_TYPE_METHOD,
|
||||||
1, sony_walk_callback, NULL, NULL, NULL);
|
sony_nc_acpi_handle, 1, sony_walk_callback,
|
||||||
|
NULL, NULL, NULL);
|
||||||
if (ACPI_FAILURE(status)) {
|
if (ACPI_FAILURE(status)) {
|
||||||
printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
|
pr_warn(DRV_PFX "unable to walk acpi resources\n");
|
||||||
result = -ENODEV;
|
result = -ENODEV;
|
||||||
goto outwalk;
|
goto outpresent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1288,6 +1602,12 @@ static int sony_nc_add(struct acpi_device *device)
|
||||||
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
|
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
|
||||||
&handle))) {
|
&handle))) {
|
||||||
dprintk("Doing SNC setup\n");
|
dprintk("Doing SNC setup\n");
|
||||||
|
result = sony_nc_handles_setup(sony_pf_device);
|
||||||
|
if (result)
|
||||||
|
goto outpresent;
|
||||||
|
result = sony_nc_kbd_backlight_setup(sony_pf_device);
|
||||||
|
if (result)
|
||||||
|
goto outsnc;
|
||||||
sony_nc_function_setup(device);
|
sony_nc_function_setup(device);
|
||||||
sony_nc_rfkill_setup(device);
|
sony_nc_rfkill_setup(device);
|
||||||
}
|
}
|
||||||
|
@ -1295,40 +1615,17 @@ static int sony_nc_add(struct acpi_device *device)
|
||||||
/* setup input devices and helper fifo */
|
/* setup input devices and helper fifo */
|
||||||
result = sony_laptop_setup_input(device);
|
result = sony_laptop_setup_input(device);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(KERN_ERR DRV_PFX
|
pr_err(DRV_PFX "Unable to create input devices.\n");
|
||||||
"Unable to create input devices.\n");
|
goto outkbdbacklight;
|
||||||
goto outwalk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acpi_video_backlight_support()) {
|
if (acpi_video_backlight_support()) {
|
||||||
printk(KERN_INFO DRV_PFX "brightness ignored, must be "
|
pr_info(DRV_PFX "brightness ignored, must be "
|
||||||
"controlled by ACPI video driver\n");
|
"controlled by ACPI video driver\n");
|
||||||
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
|
} else {
|
||||||
&handle))) {
|
sony_nc_backlight_setup();
|
||||||
struct backlight_properties props;
|
|
||||||
memset(&props, 0, sizeof(struct backlight_properties));
|
|
||||||
props.type = BACKLIGHT_PLATFORM;
|
|
||||||
props.max_brightness = SONY_MAX_BRIGHTNESS - 1;
|
|
||||||
sony_backlight_device = backlight_device_register("sony", NULL,
|
|
||||||
NULL,
|
|
||||||
&sony_backlight_ops,
|
|
||||||
&props);
|
|
||||||
|
|
||||||
if (IS_ERR(sony_backlight_device)) {
|
|
||||||
printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
|
|
||||||
sony_backlight_device = NULL;
|
|
||||||
} else {
|
|
||||||
sony_backlight_device->props.brightness =
|
|
||||||
sony_backlight_get_brightness
|
|
||||||
(sony_backlight_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = sony_pf_add();
|
|
||||||
if (result)
|
|
||||||
goto outbacklight;
|
|
||||||
|
|
||||||
/* create sony_pf sysfs attributes related to the SNC device */
|
/* create sony_pf sysfs attributes related to the SNC device */
|
||||||
for (item = sony_nc_values; item->name; ++item) {
|
for (item = sony_nc_values; item->name; ++item) {
|
||||||
|
|
||||||
|
@ -1374,14 +1671,19 @@ static int sony_nc_add(struct acpi_device *device)
|
||||||
for (item = sony_nc_values; item->name; ++item) {
|
for (item = sony_nc_values; item->name; ++item) {
|
||||||
device_remove_file(&sony_pf_device->dev, &item->devattr);
|
device_remove_file(&sony_pf_device->dev, &item->devattr);
|
||||||
}
|
}
|
||||||
sony_pf_remove();
|
sony_nc_backlight_cleanup();
|
||||||
|
|
||||||
outbacklight:
|
|
||||||
if (sony_backlight_device)
|
|
||||||
backlight_device_unregister(sony_backlight_device);
|
|
||||||
|
|
||||||
sony_laptop_remove_input();
|
sony_laptop_remove_input();
|
||||||
|
|
||||||
|
outkbdbacklight:
|
||||||
|
sony_nc_kbd_backlight_cleanup(sony_pf_device);
|
||||||
|
|
||||||
|
outsnc:
|
||||||
|
sony_nc_handles_cleanup(sony_pf_device);
|
||||||
|
|
||||||
|
outpresent:
|
||||||
|
sony_pf_remove();
|
||||||
|
|
||||||
outwalk:
|
outwalk:
|
||||||
sony_nc_rfkill_cleanup();
|
sony_nc_rfkill_cleanup();
|
||||||
return result;
|
return result;
|
||||||
|
@ -1391,8 +1693,7 @@ static int sony_nc_remove(struct acpi_device *device, int type)
|
||||||
{
|
{
|
||||||
struct sony_nc_value *item;
|
struct sony_nc_value *item;
|
||||||
|
|
||||||
if (sony_backlight_device)
|
sony_nc_backlight_cleanup();
|
||||||
backlight_device_unregister(sony_backlight_device);
|
|
||||||
|
|
||||||
sony_nc_acpi_device = NULL;
|
sony_nc_acpi_device = NULL;
|
||||||
|
|
||||||
|
@ -1400,6 +1701,8 @@ static int sony_nc_remove(struct acpi_device *device, int type)
|
||||||
device_remove_file(&sony_pf_device->dev, &item->devattr);
|
device_remove_file(&sony_pf_device->dev, &item->devattr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sony_nc_kbd_backlight_cleanup(sony_pf_device);
|
||||||
|
sony_nc_handles_cleanup(sony_pf_device);
|
||||||
sony_pf_remove();
|
sony_pf_remove();
|
||||||
sony_laptop_remove_input();
|
sony_laptop_remove_input();
|
||||||
sony_nc_rfkill_cleanup();
|
sony_nc_rfkill_cleanup();
|
||||||
|
@ -1438,7 +1741,6 @@ static struct acpi_driver sony_nc_driver = {
|
||||||
#define SONYPI_DEVICE_TYPE1 0x00000001
|
#define SONYPI_DEVICE_TYPE1 0x00000001
|
||||||
#define SONYPI_DEVICE_TYPE2 0x00000002
|
#define SONYPI_DEVICE_TYPE2 0x00000002
|
||||||
#define SONYPI_DEVICE_TYPE3 0x00000004
|
#define SONYPI_DEVICE_TYPE3 0x00000004
|
||||||
#define SONYPI_DEVICE_TYPE4 0x00000008
|
|
||||||
|
|
||||||
#define SONYPI_TYPE1_OFFSET 0x04
|
#define SONYPI_TYPE1_OFFSET 0x04
|
||||||
#define SONYPI_TYPE2_OFFSET 0x12
|
#define SONYPI_TYPE2_OFFSET 0x12
|
||||||
|
@ -1584,8 +1886,8 @@ static struct sonypi_event sonypi_blueev[] = {
|
||||||
|
|
||||||
/* The set of possible wireless events */
|
/* The set of possible wireless events */
|
||||||
static struct sonypi_event sonypi_wlessev[] = {
|
static struct sonypi_event sonypi_wlessev[] = {
|
||||||
{ 0x59, SONYPI_EVENT_WIRELESS_ON },
|
{ 0x59, SONYPI_EVENT_IGNORE },
|
||||||
{ 0x5a, SONYPI_EVENT_WIRELESS_OFF },
|
{ 0x5a, SONYPI_EVENT_IGNORE },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1842,7 +2144,7 @@ out:
|
||||||
if (pcidev)
|
if (pcidev)
|
||||||
pci_dev_put(pcidev);
|
pci_dev_put(pcidev);
|
||||||
|
|
||||||
printk(KERN_INFO DRV_PFX "detected Type%d model\n",
|
pr_info(DRV_PFX "detected Type%d model\n",
|
||||||
dev->model == SONYPI_DEVICE_TYPE1 ? 1 :
|
dev->model == SONYPI_DEVICE_TYPE1 ? 1 :
|
||||||
dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
|
dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
|
||||||
}
|
}
|
||||||
|
@ -1890,7 +2192,7 @@ static int __sony_pic_camera_ready(void)
|
||||||
static int __sony_pic_camera_off(void)
|
static int __sony_pic_camera_off(void)
|
||||||
{
|
{
|
||||||
if (!camera) {
|
if (!camera) {
|
||||||
printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
|
pr_warn(DRV_PFX "camera control not enabled\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1910,7 +2212,7 @@ static int __sony_pic_camera_on(void)
|
||||||
int i, j, x;
|
int i, j, x;
|
||||||
|
|
||||||
if (!camera) {
|
if (!camera) {
|
||||||
printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
|
pr_warn(DRV_PFX "camera control not enabled\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1933,7 +2235,7 @@ static int __sony_pic_camera_on(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j == 0) {
|
if (j == 0) {
|
||||||
printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
|
pr_warn(DRV_PFX "failed to power on camera\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1989,7 +2291,7 @@ int sony_pic_camera_command(int command, u8 value)
|
||||||
ITERATIONS_SHORT);
|
ITERATIONS_SHORT);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
|
pr_err(DRV_PFX "sony_pic_camera_command invalid: %d\n",
|
||||||
command);
|
command);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2396,7 +2698,7 @@ static int sonypi_compat_init(void)
|
||||||
error =
|
error =
|
||||||
kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
|
kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
|
||||||
if (error) {
|
if (error) {
|
||||||
printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
|
pr_err(DRV_PFX "kfifo_alloc failed\n");
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2406,11 +2708,11 @@ static int sonypi_compat_init(void)
|
||||||
sonypi_misc_device.minor = minor;
|
sonypi_misc_device.minor = minor;
|
||||||
error = misc_register(&sonypi_misc_device);
|
error = misc_register(&sonypi_misc_device);
|
||||||
if (error) {
|
if (error) {
|
||||||
printk(KERN_ERR DRV_PFX "misc_register failed\n");
|
pr_err(DRV_PFX "misc_register failed\n");
|
||||||
goto err_free_kfifo;
|
goto err_free_kfifo;
|
||||||
}
|
}
|
||||||
if (minor == -1)
|
if (minor == -1)
|
||||||
printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
|
pr_info(DRV_PFX "device allocated minor is %d\n",
|
||||||
sonypi_misc_device.minor);
|
sonypi_misc_device.minor);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2470,8 +2772,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
|
||||||
}
|
}
|
||||||
for (i = 0; i < p->interrupt_count; i++) {
|
for (i = 0; i < p->interrupt_count; i++) {
|
||||||
if (!p->interrupts[i]) {
|
if (!p->interrupts[i]) {
|
||||||
printk(KERN_WARNING DRV_PFX
|
pr_warn(DRV_PFX "Invalid IRQ %d\n",
|
||||||
"Invalid IRQ %d\n",
|
|
||||||
p->interrupts[i]);
|
p->interrupts[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2510,7 +2811,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
|
||||||
ioport->io2.address_length);
|
ioport->io2.address_length);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
|
pr_err(DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
|
||||||
return AE_ERROR;
|
return AE_ERROR;
|
||||||
}
|
}
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
@ -2538,7 +2839,7 @@ static int sony_pic_possible_resources(struct acpi_device *device)
|
||||||
dprintk("Evaluating _STA\n");
|
dprintk("Evaluating _STA\n");
|
||||||
result = acpi_bus_get_status(device);
|
result = acpi_bus_get_status(device);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(KERN_WARNING DRV_PFX "Unable to read status\n");
|
pr_warn(DRV_PFX "Unable to read status\n");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2554,8 +2855,7 @@ static int sony_pic_possible_resources(struct acpi_device *device)
|
||||||
status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
|
status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
|
||||||
sony_pic_read_possible_resource, &spic_dev);
|
sony_pic_read_possible_resource, &spic_dev);
|
||||||
if (ACPI_FAILURE(status)) {
|
if (ACPI_FAILURE(status)) {
|
||||||
printk(KERN_WARNING DRV_PFX
|
pr_warn(DRV_PFX "Failure evaluating %s\n",
|
||||||
"Failure evaluating %s\n",
|
|
||||||
METHOD_NAME__PRS);
|
METHOD_NAME__PRS);
|
||||||
result = -ENODEV;
|
result = -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -2669,7 +2969,7 @@ static int sony_pic_enable(struct acpi_device *device,
|
||||||
|
|
||||||
/* check for total failure */
|
/* check for total failure */
|
||||||
if (ACPI_FAILURE(status)) {
|
if (ACPI_FAILURE(status)) {
|
||||||
printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n");
|
pr_err(DRV_PFX "Error evaluating _SRS\n");
|
||||||
result = -ENODEV;
|
result = -ENODEV;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -2725,6 +3025,9 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id)
|
||||||
if (ev == dev->event_types[i].events[j].data) {
|
if (ev == dev->event_types[i].events[j].data) {
|
||||||
device_event =
|
device_event =
|
||||||
dev->event_types[i].events[j].event;
|
dev->event_types[i].events[j].event;
|
||||||
|
/* some events may require ignoring */
|
||||||
|
if (!device_event)
|
||||||
|
return IRQ_HANDLED;
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2744,7 +3047,6 @@ found:
|
||||||
sony_laptop_report_input_event(device_event);
|
sony_laptop_report_input_event(device_event);
|
||||||
acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event);
|
acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event);
|
||||||
sonypi_compat_report_event(device_event);
|
sonypi_compat_report_event(device_event);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2759,7 +3061,7 @@ static int sony_pic_remove(struct acpi_device *device, int type)
|
||||||
struct sony_pic_irq *irq, *tmp_irq;
|
struct sony_pic_irq *irq, *tmp_irq;
|
||||||
|
|
||||||
if (sony_pic_disable(device)) {
|
if (sony_pic_disable(device)) {
|
||||||
printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
|
pr_err(DRV_PFX "Couldn't disable device.\n");
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2799,8 +3101,8 @@ static int sony_pic_add(struct acpi_device *device)
|
||||||
struct sony_pic_ioport *io, *tmp_io;
|
struct sony_pic_ioport *io, *tmp_io;
|
||||||
struct sony_pic_irq *irq, *tmp_irq;
|
struct sony_pic_irq *irq, *tmp_irq;
|
||||||
|
|
||||||
printk(KERN_INFO DRV_PFX "%s v%s.\n",
|
pr_info(DRV_PFX "%s v%s.\n", SONY_PIC_DRIVER_NAME,
|
||||||
SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
|
SONY_LAPTOP_DRIVER_VERSION);
|
||||||
|
|
||||||
spic_dev.acpi_dev = device;
|
spic_dev.acpi_dev = device;
|
||||||
strcpy(acpi_device_class(device), "sony/hotkey");
|
strcpy(acpi_device_class(device), "sony/hotkey");
|
||||||
|
@ -2810,16 +3112,14 @@ static int sony_pic_add(struct acpi_device *device)
|
||||||
/* read _PRS resources */
|
/* read _PRS resources */
|
||||||
result = sony_pic_possible_resources(device);
|
result = sony_pic_possible_resources(device);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(KERN_ERR DRV_PFX
|
pr_err(DRV_PFX "Unable to read possible resources.\n");
|
||||||
"Unable to read possible resources.\n");
|
|
||||||
goto err_free_resources;
|
goto err_free_resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* setup input devices and helper fifo */
|
/* setup input devices and helper fifo */
|
||||||
result = sony_laptop_setup_input(device);
|
result = sony_laptop_setup_input(device);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(KERN_ERR DRV_PFX
|
pr_err(DRV_PFX "Unable to create input devices.\n");
|
||||||
"Unable to create input devices.\n");
|
|
||||||
goto err_free_resources;
|
goto err_free_resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2829,7 +3129,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||||
/* request io port */
|
/* request io port */
|
||||||
list_for_each_entry_reverse(io, &spic_dev.ioports, list) {
|
list_for_each_entry_reverse(io, &spic_dev.ioports, list) {
|
||||||
if (request_region(io->io1.minimum, io->io1.address_length,
|
if (request_region(io->io1.minimum, io->io1.address_length,
|
||||||
"Sony Programable I/O Device")) {
|
"Sony Programmable I/O Device")) {
|
||||||
dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",
|
dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",
|
||||||
io->io1.minimum, io->io1.maximum,
|
io->io1.minimum, io->io1.maximum,
|
||||||
io->io1.address_length);
|
io->io1.address_length);
|
||||||
|
@ -2837,7 +3137,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||||
if (io->io2.minimum) {
|
if (io->io2.minimum) {
|
||||||
if (request_region(io->io2.minimum,
|
if (request_region(io->io2.minimum,
|
||||||
io->io2.address_length,
|
io->io2.address_length,
|
||||||
"Sony Programable I/O Device")) {
|
"Sony Programmable I/O Device")) {
|
||||||
dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",
|
dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",
|
||||||
io->io2.minimum, io->io2.maximum,
|
io->io2.minimum, io->io2.maximum,
|
||||||
io->io2.address_length);
|
io->io2.address_length);
|
||||||
|
@ -2860,7 +3160,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!spic_dev.cur_ioport) {
|
if (!spic_dev.cur_ioport) {
|
||||||
printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
|
pr_err(DRV_PFX "Failed to request_region.\n");
|
||||||
result = -ENODEV;
|
result = -ENODEV;
|
||||||
goto err_remove_compat;
|
goto err_remove_compat;
|
||||||
}
|
}
|
||||||
|
@ -2880,7 +3180,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!spic_dev.cur_irq) {
|
if (!spic_dev.cur_irq) {
|
||||||
printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
|
pr_err(DRV_PFX "Failed to request_irq.\n");
|
||||||
result = -ENODEV;
|
result = -ENODEV;
|
||||||
goto err_release_region;
|
goto err_release_region;
|
||||||
}
|
}
|
||||||
|
@ -2888,7 +3188,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||||
/* set resource status _SRS */
|
/* set resource status _SRS */
|
||||||
result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
|
result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
|
pr_err(DRV_PFX "Couldn't enable device.\n");
|
||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2997,8 +3297,7 @@ static int __init sony_laptop_init(void)
|
||||||
if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
|
if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
|
||||||
result = acpi_bus_register_driver(&sony_pic_driver);
|
result = acpi_bus_register_driver(&sony_pic_driver);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(KERN_ERR DRV_PFX
|
pr_err(DRV_PFX "Unable to register SPIC driver.");
|
||||||
"Unable to register SPIC driver.");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
spic_drv_registered = 1;
|
spic_drv_registered = 1;
|
||||||
|
@ -3006,7 +3305,7 @@ static int __init sony_laptop_init(void)
|
||||||
|
|
||||||
result = acpi_bus_register_driver(&sony_nc_driver);
|
result = acpi_bus_register_driver(&sony_nc_driver);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
|
pr_err(DRV_PFX "Unable to register SNC driver.");
|
||||||
goto out_unregister_pic;
|
goto out_unregister_pic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2407,7 +2407,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
||||||
* This code is supposed to duplicate the IBM firmware behaviour:
|
* This code is supposed to duplicate the IBM firmware behaviour:
|
||||||
* - Pressing MUTE issues mute hotkey message, even when already mute
|
* - Pressing MUTE issues mute hotkey message, even when already mute
|
||||||
* - Pressing Volume up/down issues volume up/down hotkey messages,
|
* - Pressing Volume up/down issues volume up/down hotkey messages,
|
||||||
* even when already at maximum or minumum volume
|
* even when already at maximum or minimum volume
|
||||||
* - The act of unmuting issues volume up/down notification,
|
* - The act of unmuting issues volume up/down notification,
|
||||||
* depending which key was used to unmute
|
* depending which key was used to unmute
|
||||||
*
|
*
|
||||||
|
@ -2990,7 +2990,7 @@ static void tpacpi_send_radiosw_update(void)
|
||||||
* rfkill input events, or we will race the rfkill core input
|
* rfkill input events, or we will race the rfkill core input
|
||||||
* handler.
|
* handler.
|
||||||
*
|
*
|
||||||
* tpacpi_inputdev_send_mutex works as a syncronization point
|
* tpacpi_inputdev_send_mutex works as a synchronization point
|
||||||
* for the above.
|
* for the above.
|
||||||
*
|
*
|
||||||
* We optimize to avoid numerous calls to hotkey_get_wlsw.
|
* We optimize to avoid numerous calls to hotkey_get_wlsw.
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* OLPC XO-1.5 ebook switch driver
|
||||||
|
* (based on generic ACPI button driver)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Paul Fox <pgf@laptop.org>
|
||||||
|
* Copyright (C) 2010 One Laptop per Child
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <acpi/acpi_bus.h>
|
||||||
|
#include <acpi/acpi_drivers.h>
|
||||||
|
|
||||||
|
#define MODULE_NAME "xo15-ebook"
|
||||||
|
#define PREFIX MODULE_NAME ": "
|
||||||
|
|
||||||
|
#define XO15_EBOOK_CLASS MODULE_NAME
|
||||||
|
#define XO15_EBOOK_TYPE_UNKNOWN 0x00
|
||||||
|
#define XO15_EBOOK_NOTIFY_STATUS 0x80
|
||||||
|
|
||||||
|
#define XO15_EBOOK_SUBCLASS "ebook"
|
||||||
|
#define XO15_EBOOK_HID "XO15EBK"
|
||||||
|
#define XO15_EBOOK_DEVICE_NAME "EBook Switch"
|
||||||
|
|
||||||
|
ACPI_MODULE_NAME(MODULE_NAME);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
static const struct acpi_device_id ebook_device_ids[] = {
|
||||||
|
{ XO15_EBOOK_HID, 0 },
|
||||||
|
{ "", 0 },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, ebook_device_ids);
|
||||||
|
|
||||||
|
struct ebook_switch {
|
||||||
|
struct input_dev *input;
|
||||||
|
char phys[32]; /* for input device */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ebook_send_state(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
struct ebook_switch *button = acpi_driver_data(device);
|
||||||
|
unsigned long long state;
|
||||||
|
acpi_status status;
|
||||||
|
|
||||||
|
status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state);
|
||||||
|
if (ACPI_FAILURE(status))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
/* input layer checks if event is redundant */
|
||||||
|
input_report_switch(button->input, SW_TABLET_MODE, !state);
|
||||||
|
input_sync(button->input);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ebook_switch_notify(struct acpi_device *device, u32 event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case ACPI_FIXED_HARDWARE_EVENT:
|
||||||
|
case XO15_EBOOK_NOTIFY_STATUS:
|
||||||
|
ebook_send_state(device);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||||
|
"Unsupported event [0x%x]\n", event));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ebook_switch_resume(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
return ebook_send_state(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ebook_switch_add(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
struct ebook_switch *button;
|
||||||
|
struct input_dev *input;
|
||||||
|
const char *hid = acpi_device_hid(device);
|
||||||
|
char *name, *class;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
button = kzalloc(sizeof(struct ebook_switch), GFP_KERNEL);
|
||||||
|
if (!button)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
device->driver_data = button;
|
||||||
|
|
||||||
|
button->input = input = input_allocate_device();
|
||||||
|
if (!input) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto err_free_button;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = acpi_device_name(device);
|
||||||
|
class = acpi_device_class(device);
|
||||||
|
|
||||||
|
if (strcmp(hid, XO15_EBOOK_HID)) {
|
||||||
|
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
|
||||||
|
error = -ENODEV;
|
||||||
|
goto err_free_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(name, XO15_EBOOK_DEVICE_NAME);
|
||||||
|
sprintf(class, "%s/%s", XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS);
|
||||||
|
|
||||||
|
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
|
||||||
|
|
||||||
|
input->name = name;
|
||||||
|
input->phys = button->phys;
|
||||||
|
input->id.bustype = BUS_HOST;
|
||||||
|
input->dev.parent = &device->dev;
|
||||||
|
|
||||||
|
input->evbit[0] = BIT_MASK(EV_SW);
|
||||||
|
set_bit(SW_TABLET_MODE, input->swbit);
|
||||||
|
|
||||||
|
error = input_register_device(input);
|
||||||
|
if (error)
|
||||||
|
goto err_free_input;
|
||||||
|
|
||||||
|
ebook_send_state(device);
|
||||||
|
|
||||||
|
if (device->wakeup.flags.valid) {
|
||||||
|
/* Button's GPE is run-wake GPE */
|
||||||
|
acpi_enable_gpe(device->wakeup.gpe_device,
|
||||||
|
device->wakeup.gpe_number);
|
||||||
|
device_set_wakeup_enable(&device->dev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free_input:
|
||||||
|
input_free_device(input);
|
||||||
|
err_free_button:
|
||||||
|
kfree(button);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ebook_switch_remove(struct acpi_device *device, int type)
|
||||||
|
{
|
||||||
|
struct ebook_switch *button = acpi_driver_data(device);
|
||||||
|
|
||||||
|
input_unregister_device(button->input);
|
||||||
|
kfree(button);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct acpi_driver xo15_ebook_driver = {
|
||||||
|
.name = MODULE_NAME,
|
||||||
|
.class = XO15_EBOOK_CLASS,
|
||||||
|
.ids = ebook_device_ids,
|
||||||
|
.ops = {
|
||||||
|
.add = ebook_switch_add,
|
||||||
|
.resume = ebook_switch_resume,
|
||||||
|
.remove = ebook_switch_remove,
|
||||||
|
.notify = ebook_switch_notify,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init xo15_ebook_init(void)
|
||||||
|
{
|
||||||
|
return acpi_bus_register_driver(&xo15_ebook_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit xo15_ebook_exit(void)
|
||||||
|
{
|
||||||
|
acpi_bus_unregister_driver(&xo15_ebook_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(xo15_ebook_init);
|
||||||
|
module_exit(xo15_ebook_exit);
|
|
@ -664,6 +664,13 @@ struct input_keymap_entry {
|
||||||
#define KEY_TOUCHPAD_ON 0x213
|
#define KEY_TOUCHPAD_ON 0x213
|
||||||
#define KEY_TOUCHPAD_OFF 0x214
|
#define KEY_TOUCHPAD_OFF 0x214
|
||||||
|
|
||||||
|
#define KEY_CAMERA_ZOOMIN 0x215
|
||||||
|
#define KEY_CAMERA_ZOOMOUT 0x216
|
||||||
|
#define KEY_CAMERA_UP 0x217
|
||||||
|
#define KEY_CAMERA_DOWN 0x218
|
||||||
|
#define KEY_CAMERA_LEFT 0x219
|
||||||
|
#define KEY_CAMERA_RIGHT 0x21a
|
||||||
|
|
||||||
#define BTN_TRIGGER_HAPPY 0x2c0
|
#define BTN_TRIGGER_HAPPY 0x2c0
|
||||||
#define BTN_TRIGGER_HAPPY1 0x2c0
|
#define BTN_TRIGGER_HAPPY1 0x2c0
|
||||||
#define BTN_TRIGGER_HAPPY2 0x2c1
|
#define BTN_TRIGGER_HAPPY2 0x2c1
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
/* events the user application reading /dev/sonypi can use */
|
/* events the user application reading /dev/sonypi can use */
|
||||||
|
|
||||||
|
#define SONYPI_EVENT_IGNORE 0
|
||||||
#define SONYPI_EVENT_JOGDIAL_DOWN 1
|
#define SONYPI_EVENT_JOGDIAL_DOWN 1
|
||||||
#define SONYPI_EVENT_JOGDIAL_UP 2
|
#define SONYPI_EVENT_JOGDIAL_UP 2
|
||||||
#define SONYPI_EVENT_JOGDIAL_DOWN_PRESSED 3
|
#define SONYPI_EVENT_JOGDIAL_DOWN_PRESSED 3
|
||||||
|
|
Reference in New Issue