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: (44 commits) eeepc-wmi: Add cpufv sysfs interface eeepc-wmi: add additional hotkeys panasonic-laptop: Simplify calls to acpi_pcc_retrieve_biosdata panasonic-laptop: Handle errors properly if they happen intel_pmic_gpio: fix off-by-one value range checking IBM Real-Time "SMI Free" mode driver -v7 Add OLPC XO-1 rfkill driver Move hdaps driver to platform/x86 ideapad-laptop: Fix Makefile intel_pmic_gpio: swap the bits and mask args for intel_scu_ipc_update_register ideapad: Add param: no_bt_rfkill ideapad: Change the driver name to ideapad-laptop ideapad: rewrite the sw rfkill set ideapad: rewrite the hw rfkill notify ideapad: use EC command to control camera ideapad: use return value of _CFG to tell if device exist or not ideapad: make sure we bind on the correct device ideapad: check VPC bit before sync rfkill hw status ideapad: add ACPI helpers dell-laptop: Add debugfs support ...
This commit is contained in:
commit
fbaab1dc19
|
@ -0,0 +1,22 @@
|
|||
What: state
|
||||
Date: Sep 2010
|
||||
KernelVersion: 2.6.37
|
||||
Contact: Vernon Mauery <vernux@us.ibm.com>
|
||||
Description: The state file allows a means by which to change in and
|
||||
out of Premium Real-Time Mode (PRTM), as well as the
|
||||
ability to query the current state.
|
||||
0 => PRTM off
|
||||
1 => PRTM enabled
|
||||
Users: The ibm-prtm userspace daemon uses this interface.
|
||||
|
||||
|
||||
What: version
|
||||
Date: Sep 2010
|
||||
KernelVersion: 2.6.37
|
||||
Contact: Vernon Mauery <vernux@us.ibm.com>
|
||||
Description: The version file provides a means by which to query
|
||||
the RTL table version that lives in the Extended
|
||||
BIOS Data Area (EBDA).
|
||||
Users: The ibm-prtm userspace daemon uses this interface.
|
||||
|
||||
|
|
@ -2646,10 +2646,10 @@ F: drivers/net/greth*
|
|||
|
||||
HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
|
||||
M: Frank Seidel <frank@f-seidel.de>
|
||||
L: lm-sensors@lm-sensors.org
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/
|
||||
S: Maintained
|
||||
F: drivers/hwmon/hdaps.c
|
||||
F: drivers/platform/x86/hdaps.c
|
||||
|
||||
HWPOISON MEMORY FAILURE HANDLING
|
||||
M: Andi Kleen <andi@firstfloor.org>
|
||||
|
|
|
@ -89,6 +89,8 @@ extern int olpc_ec_mask_unset(uint8_t bits);
|
|||
/* EC commands */
|
||||
|
||||
#define EC_FIRMWARE_REV 0x08
|
||||
#define EC_WLAN_ENTER_RESET 0x35
|
||||
#define EC_WLAN_LEAVE_RESET 0x25
|
||||
|
||||
/* SCI source values */
|
||||
|
||||
|
|
|
@ -1088,26 +1088,6 @@ config SENSORS_ULTRA45
|
|||
This driver provides support for the Ultra45 workstation environmental
|
||||
sensors.
|
||||
|
||||
config SENSORS_HDAPS
|
||||
tristate "IBM Hard Drive Active Protection System (hdaps)"
|
||||
depends on INPUT && X86
|
||||
select INPUT_POLLDEV
|
||||
default n
|
||||
help
|
||||
This driver provides support for the IBM Hard Drive Active Protection
|
||||
System (hdaps), which provides an accelerometer and other misc. data.
|
||||
ThinkPads starting with the R50, T41, and X40 are supported. The
|
||||
accelerometer data is readable via sysfs.
|
||||
|
||||
This driver also provides an absolute input class device, allowing
|
||||
the laptop to act as a pinball machine-esque joystick.
|
||||
|
||||
If your ThinkPad is not recognized by the driver, please update to latest
|
||||
BIOS. This is especially the case for some R52 ThinkPads.
|
||||
|
||||
Say Y here if you have an applicable laptop and want to experience
|
||||
the awesome power of hdaps.
|
||||
|
||||
config SENSORS_LIS3_SPI
|
||||
tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)"
|
||||
depends on !ACPI && SPI_MASTER && INPUT
|
||||
|
|
|
@ -52,7 +52,6 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o
|
|||
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
|
||||
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
|
||||
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
|
||||
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
|
||||
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
|
||||
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
|
||||
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
|
||||
|
|
|
@ -92,6 +92,7 @@ config DELL_WMI
|
|||
tristate "Dell WMI extras"
|
||||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
|
||||
|
||||
|
@ -140,6 +141,7 @@ config HP_WMI
|
|||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
depends on RFKILL || RFKILL = n
|
||||
select INPUT_SPARSEKMAP
|
||||
help
|
||||
Say Y here if you want to support WMI-based hotkeys on HP laptops and
|
||||
to read data from WMI such as docking or ambient light sensor state.
|
||||
|
@ -171,6 +173,7 @@ config PANASONIC_LAPTOP
|
|||
tristate "Panasonic Laptop Extras"
|
||||
depends on INPUT && ACPI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
This driver adds support for access to backlight control and hotkeys
|
||||
on Panasonic Let's Note laptops.
|
||||
|
@ -219,8 +222,8 @@ config SONYPI_COMPAT
|
|||
---help---
|
||||
Build the sonypi driver compatibility code into the sony-laptop driver.
|
||||
|
||||
config IDEAPAD_ACPI
|
||||
tristate "Lenovo IdeaPad ACPI Laptop Extras"
|
||||
config IDEAPAD_LAPTOP
|
||||
tristate "Lenovo IdeaPad Laptop Extras"
|
||||
depends on ACPI
|
||||
depends on RFKILL
|
||||
help
|
||||
|
@ -365,6 +368,26 @@ config THINKPAD_ACPI_HOTKEY_POLL
|
|||
If you are not sure, say Y here. The driver enables polling only if
|
||||
it is strictly necessary to do so.
|
||||
|
||||
config SENSORS_HDAPS
|
||||
tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
|
||||
depends on INPUT && X86
|
||||
select INPUT_POLLDEV
|
||||
default n
|
||||
help
|
||||
This driver provides support for the IBM Hard Drive Active Protection
|
||||
System (hdaps), which provides an accelerometer and other misc. data.
|
||||
ThinkPads starting with the R50, T41, and X40 are supported. The
|
||||
accelerometer data is readable via sysfs.
|
||||
|
||||
This driver also provides an absolute input class device, allowing
|
||||
the laptop to act as a pinball machine-esque joystick.
|
||||
|
||||
If your ThinkPad is not recognized by the driver, please update to latest
|
||||
BIOS. This is especially the case for some R52 ThinkPads.
|
||||
|
||||
Say Y here if you have an applicable laptop and want to experience
|
||||
the awesome power of hdaps.
|
||||
|
||||
config INTEL_MENLOW
|
||||
tristate "Thermal Management driver for Intel menlow platform"
|
||||
depends on ACPI_THERMAL
|
||||
|
@ -478,6 +501,7 @@ config TOPSTAR_LAPTOP
|
|||
tristate "Topstar Laptop Extras"
|
||||
depends on ACPI
|
||||
depends on INPUT
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
This driver adds support for hotkeys found on Topstar laptops.
|
||||
|
||||
|
@ -492,6 +516,7 @@ config ACPI_TOSHIBA
|
|||
depends on INPUT
|
||||
depends on RFKILL || RFKILL = n
|
||||
select INPUT_POLLDEV
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
This driver adds support for access to certain system settings
|
||||
on "legacy free" Toshiba laptops. These laptops can be recognized by
|
||||
|
@ -590,4 +615,28 @@ config INTEL_IPS
|
|||
functionality. If in doubt, say Y here; it will only load on
|
||||
supported platforms.
|
||||
|
||||
config IBM_RTL
|
||||
tristate "Device driver to enable PRTL support"
|
||||
depends on X86 && PCI
|
||||
---help---
|
||||
Enable support for IBM Premium Real Time Mode (PRTM).
|
||||
This module will allow you the enter and exit PRTM in the BIOS via
|
||||
sysfs on platforms that support this feature. System in PRTM will
|
||||
not receive CPU-generated SMIs for recoverable errors. Use of this
|
||||
feature without proper support may void your hardware warranty.
|
||||
|
||||
If the proper BIOS support is found the driver will load and create
|
||||
/sys/devices/system/ibm_rtl/. The "state" variable will indicate
|
||||
whether or not the BIOS is in PRTM.
|
||||
state = 0 (BIOS SMIs on)
|
||||
state = 1 (BIOS SMIs off)
|
||||
|
||||
config XO1_RFKILL
|
||||
tristate "OLPC XO-1 software RF kill switch"
|
||||
depends on OLPC
|
||||
depends on RFKILL
|
||||
---help---
|
||||
Support for enabling/disabling the WLAN interface on the OLPC XO-1
|
||||
laptop.
|
||||
|
||||
endif # X86_PLATFORM_DEVICES
|
||||
|
|
|
@ -15,8 +15,9 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
|
|||
obj-$(CONFIG_HP_WMI) += hp-wmi.o
|
||||
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
|
||||
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
|
||||
obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o
|
||||
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
|
||||
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
|
||||
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
|
||||
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
|
||||
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
|
||||
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
|
||||
|
@ -30,4 +31,5 @@ obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
|||
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
|
||||
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
|
||||
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
|
||||
|
||||
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
|
||||
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
|
||||
|
|
|
@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void)
|
|||
AMW0_find_mailled();
|
||||
|
||||
if (!interface) {
|
||||
printk(ACER_ERR "No or unsupported WMI interface, unable to "
|
||||
printk(ACER_INFO "No or unsupported WMI interface, unable to "
|
||||
"load\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -236,7 +236,6 @@ struct asus_laptop {
|
|||
u8 light_level; /* light sensor level */
|
||||
u8 light_switch; /* light sensor switch value */
|
||||
u16 event_count[128]; /* count for each event TODO make this better */
|
||||
u16 *keycode_map;
|
||||
};
|
||||
|
||||
static const struct key_entry asus_keymap[] = {
|
||||
|
@ -278,6 +277,7 @@ static const struct key_entry asus_keymap[] = {
|
|||
{KE_KEY, 0x99, { KEY_PHONE } },
|
||||
{KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
|
||||
{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
|
||||
{KE_KEY, 0xb5, { KEY_CALC } },
|
||||
{KE_END, 0},
|
||||
};
|
||||
|
||||
|
@ -639,29 +639,29 @@ static int asus_backlight_notify(struct asus_laptop *asus)
|
|||
static int asus_backlight_init(struct asus_laptop *asus)
|
||||
{
|
||||
struct backlight_device *bd;
|
||||
struct device *dev = &asus->platform_device->dev;
|
||||
struct backlight_properties props;
|
||||
|
||||
if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) &&
|
||||
!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) &&
|
||||
lcd_switch_handle) {
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = 15;
|
||||
if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
|
||||
acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) ||
|
||||
!lcd_switch_handle)
|
||||
return 0;
|
||||
|
||||
bd = backlight_device_register(ASUS_LAPTOP_FILE, dev,
|
||||
asus, &asusbl_ops, &props);
|
||||
if (IS_ERR(bd)) {
|
||||
pr_err("Could not register asus backlight device\n");
|
||||
asus->backlight_device = NULL;
|
||||
return PTR_ERR(bd);
|
||||
}
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = 15;
|
||||
|
||||
asus->backlight_device = bd;
|
||||
|
||||
bd->props.power = FB_BLANK_UNBLANK;
|
||||
bd->props.brightness = asus_read_brightness(bd);
|
||||
backlight_update_status(bd);
|
||||
bd = backlight_device_register(ASUS_LAPTOP_FILE,
|
||||
&asus->platform_device->dev, asus,
|
||||
&asusbl_ops, &props);
|
||||
if (IS_ERR(bd)) {
|
||||
pr_err("Could not register asus backlight device\n");
|
||||
asus->backlight_device = NULL;
|
||||
return PTR_ERR(bd);
|
||||
}
|
||||
|
||||
asus->backlight_device = bd;
|
||||
bd->props.brightness = asus_read_brightness(bd);
|
||||
bd->props.power = FB_BLANK_UNBLANK;
|
||||
backlight_update_status(bd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1065,9 +1065,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
|
|||
*/
|
||||
static int asus_gps_rfkill_set(void *data, bool blocked)
|
||||
{
|
||||
acpi_handle handle = data;
|
||||
struct asus_laptop *asus = data;
|
||||
|
||||
return asus_gps_switch(handle, !blocked);
|
||||
return asus_gps_switch(asus, !blocked);
|
||||
}
|
||||
|
||||
static const struct rfkill_ops asus_gps_rfkill_ops = {
|
||||
|
@ -1094,7 +1094,7 @@ static int asus_rfkill_init(struct asus_laptop *asus)
|
|||
|
||||
asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
|
||||
RFKILL_TYPE_GPS,
|
||||
&asus_gps_rfkill_ops, NULL);
|
||||
&asus_gps_rfkill_ops, asus);
|
||||
if (!asus->gps_rfkill)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1130,7 +1130,6 @@ static int asus_input_init(struct asus_laptop *asus)
|
|||
input->phys = ASUS_LAPTOP_FILE "/input0";
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = &asus->platform_device->dev;
|
||||
input_set_drvdata(input, asus);
|
||||
|
||||
error = sparse_keymap_setup(input, asus_keymap, NULL);
|
||||
if (error) {
|
||||
|
@ -1159,6 +1158,7 @@ static void asus_input_exit(struct asus_laptop *asus)
|
|||
sparse_keymap_free(asus->inputdev);
|
||||
input_unregister_device(asus->inputdev);
|
||||
}
|
||||
asus->inputdev = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1200,111 +1200,100 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
|
|||
|
||||
static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL);
|
||||
static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan);
|
||||
static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth,
|
||||
store_bluetooth);
|
||||
static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,
|
||||
show_bluetooth, store_bluetooth);
|
||||
static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp);
|
||||
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_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
|
||||
static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps);
|
||||
|
||||
static void asus_sysfs_exit(struct asus_laptop *asus)
|
||||
{
|
||||
struct platform_device *device = asus->platform_device;
|
||||
static struct attribute *asus_attributes[] = {
|
||||
&dev_attr_infos.attr,
|
||||
&dev_attr_wlan.attr,
|
||||
&dev_attr_bluetooth.attr,
|
||||
&dev_attr_display.attr,
|
||||
&dev_attr_ledd.attr,
|
||||
&dev_attr_ls_level.attr,
|
||||
&dev_attr_ls_switch.attr,
|
||||
&dev_attr_gps.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
device_remove_file(&device->dev, &dev_attr_infos);
|
||||
device_remove_file(&device->dev, &dev_attr_wlan);
|
||||
device_remove_file(&device->dev, &dev_attr_bluetooth);
|
||||
device_remove_file(&device->dev, &dev_attr_display);
|
||||
device_remove_file(&device->dev, &dev_attr_ledd);
|
||||
device_remove_file(&device->dev, &dev_attr_ls_switch);
|
||||
device_remove_file(&device->dev, &dev_attr_ls_level);
|
||||
device_remove_file(&device->dev, &dev_attr_gps);
|
||||
static mode_t asus_sysfs_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int idx)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct asus_laptop *asus = platform_get_drvdata(pdev);
|
||||
acpi_handle handle = asus->handle;
|
||||
bool supported;
|
||||
|
||||
if (attr == &dev_attr_wlan.attr) {
|
||||
supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
|
||||
|
||||
} else if (attr == &dev_attr_bluetooth.attr) {
|
||||
supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL);
|
||||
|
||||
} else if (attr == &dev_attr_display.attr) {
|
||||
supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL);
|
||||
|
||||
} else if (attr == &dev_attr_ledd.attr) {
|
||||
supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);
|
||||
|
||||
} else if (attr == &dev_attr_ls_switch.attr ||
|
||||
attr == &dev_attr_ls_level.attr) {
|
||||
supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
|
||||
!acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
|
||||
|
||||
} else if (attr == &dev_attr_gps.attr) {
|
||||
supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
|
||||
!acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
|
||||
!acpi_check_handle(handle, METHOD_GPS_STATUS, NULL);
|
||||
} else {
|
||||
supported = true;
|
||||
}
|
||||
|
||||
return supported ? attr->mode : 0;
|
||||
}
|
||||
|
||||
static int asus_sysfs_init(struct asus_laptop *asus)
|
||||
{
|
||||
struct platform_device *device = asus->platform_device;
|
||||
int err;
|
||||
|
||||
err = device_create_file(&device->dev, &dev_attr_infos);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) {
|
||||
err = device_create_file(&device->dev, &dev_attr_wlan);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) {
|
||||
err = device_create_file(&device->dev, &dev_attr_bluetooth);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) {
|
||||
err = device_create_file(&device->dev, &dev_attr_display);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) {
|
||||
err = device_create_file(&device->dev, &dev_attr_ledd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
|
||||
!acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
|
||||
err = device_create_file(&device->dev, &dev_attr_ls_switch);
|
||||
if (err)
|
||||
return err;
|
||||
err = device_create_file(&device->dev, &dev_attr_ls_level);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) &&
|
||||
!acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) &&
|
||||
!acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) {
|
||||
err = device_create_file(&device->dev, &dev_attr_gps);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
static const struct attribute_group asus_attr_group = {
|
||||
.is_visible = asus_sysfs_is_visible,
|
||||
.attrs = asus_attributes,
|
||||
};
|
||||
|
||||
static int asus_platform_init(struct asus_laptop *asus)
|
||||
{
|
||||
int err;
|
||||
int result;
|
||||
|
||||
asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1);
|
||||
if (!asus->platform_device)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(asus->platform_device, asus);
|
||||
|
||||
err = platform_device_add(asus->platform_device);
|
||||
if (err)
|
||||
result = platform_device_add(asus->platform_device);
|
||||
if (result)
|
||||
goto fail_platform_device;
|
||||
|
||||
err = asus_sysfs_init(asus);
|
||||
if (err)
|
||||
result = sysfs_create_group(&asus->platform_device->dev.kobj,
|
||||
&asus_attr_group);
|
||||
if (result)
|
||||
goto fail_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_sysfs:
|
||||
asus_sysfs_exit(asus);
|
||||
platform_device_del(asus->platform_device);
|
||||
fail_platform_device:
|
||||
platform_device_put(asus->platform_device);
|
||||
return err;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void asus_platform_exit(struct asus_laptop *asus)
|
||||
{
|
||||
asus_sysfs_exit(asus);
|
||||
sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group);
|
||||
platform_device_unregister(asus->platform_device);
|
||||
}
|
||||
|
||||
|
@ -1428,8 +1417,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
|
|||
return AE_OK;
|
||||
}
|
||||
|
||||
static bool asus_device_present;
|
||||
|
||||
static int __devinit asus_acpi_init(struct asus_laptop *asus)
|
||||
{
|
||||
int result = 0;
|
||||
|
@ -1474,6 +1461,8 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool asus_device_present;
|
||||
|
||||
static int __devinit asus_acpi_add(struct acpi_device *device)
|
||||
{
|
||||
struct asus_laptop *asus;
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/i8042.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "../../firmware/dcdbas.h"
|
||||
|
||||
#define BRIGHTNESS_TOKEN 0x7d
|
||||
|
@ -325,6 +327,75 @@ static const struct rfkill_ops dell_rfkill_ops = {
|
|||
.query = dell_rfkill_query,
|
||||
};
|
||||
|
||||
static struct dentry *dell_laptop_dir;
|
||||
|
||||
static int dell_debugfs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
int status;
|
||||
|
||||
get_buffer();
|
||||
dell_send_request(buffer, 17, 11);
|
||||
status = buffer->output[1];
|
||||
release_buffer();
|
||||
|
||||
seq_printf(s, "status:\t0x%X\n", status);
|
||||
seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
|
||||
status & BIT(0));
|
||||
seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
|
||||
(status & BIT(1)) >> 1);
|
||||
seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
|
||||
(status & BIT(2)) >> 2);
|
||||
seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
|
||||
(status & BIT(3)) >> 3);
|
||||
seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
|
||||
(status & BIT(4)) >> 4);
|
||||
seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
|
||||
(status & BIT(5)) >> 5);
|
||||
seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
|
||||
(status & BIT(8)) >> 8);
|
||||
seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
|
||||
(status & BIT(9)) >> 9);
|
||||
seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
|
||||
(status & BIT(10)) >> 10);
|
||||
seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
|
||||
(status & BIT(16)) >> 16);
|
||||
seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
|
||||
(status & BIT(17)) >> 17);
|
||||
seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
|
||||
(status & BIT(18)) >> 18);
|
||||
seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
|
||||
(status & BIT(19)) >> 19);
|
||||
|
||||
seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
|
||||
seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
|
||||
hwswitch_state & BIT(0));
|
||||
seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
|
||||
(hwswitch_state & BIT(1)) >> 1);
|
||||
seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
|
||||
(hwswitch_state & BIT(2)) >> 2);
|
||||
seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
|
||||
(hwswitch_state & BIT(7)) >> 7);
|
||||
seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
|
||||
(hwswitch_state & BIT(8)) >> 8);
|
||||
seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
|
||||
(hwswitch_state & BIT(15)) >> 15);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dell_debugfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dell_debugfs_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations dell_debugfs_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = dell_debugfs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void dell_update_rfkill(struct work_struct *ignored)
|
||||
{
|
||||
if (wifi_rfkill)
|
||||
|
@ -556,6 +627,11 @@ static int __init dell_init(void)
|
|||
goto fail_filter;
|
||||
}
|
||||
|
||||
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
|
||||
if (dell_laptop_dir != NULL)
|
||||
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
|
||||
&dell_debugfs_fops);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/* In the event of an ACPI backlight being available, don't
|
||||
* register the platform controller.
|
||||
|
@ -615,6 +691,7 @@ fail_platform_driver:
|
|||
|
||||
static void __exit dell_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(dell_laptop_dir);
|
||||
i8042_remove_filter(dell_laptop_i8042_filter);
|
||||
cancel_delayed_work_sync(&dell_rfkill_work);
|
||||
backlight_device_unregister(dell_backlight_device);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/slab.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>
|
||||
|
@ -44,78 +45,70 @@ static int acpi_video;
|
|||
|
||||
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
|
||||
|
||||
struct key_entry {
|
||||
char type; /* See KE_* below */
|
||||
u16 code;
|
||||
u16 keycode;
|
||||
};
|
||||
|
||||
enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
|
||||
|
||||
/*
|
||||
* Certain keys are flagged as KE_IGNORE. All of these are either
|
||||
* notifications (rather than requests for change) or are also sent
|
||||
* via the keyboard controller so should not be sent again.
|
||||
*/
|
||||
|
||||
static struct key_entry dell_legacy_wmi_keymap[] = {
|
||||
{KE_KEY, 0xe045, KEY_PROG1},
|
||||
{KE_KEY, 0xe009, KEY_EJECTCD},
|
||||
static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
|
||||
{ KE_KEY, 0xe045, { KEY_PROG1 } },
|
||||
{ KE_KEY, 0xe009, { KEY_EJECTCD } },
|
||||
|
||||
/* These also contain the brightness level at offset 6 */
|
||||
{KE_KEY, 0xe006, KEY_BRIGHTNESSUP},
|
||||
{KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN},
|
||||
{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
|
||||
|
||||
/* Battery health status button */
|
||||
{KE_KEY, 0xe007, KEY_BATTERY},
|
||||
{ KE_KEY, 0xe007, { KEY_BATTERY } },
|
||||
|
||||
/* This is actually for all radios. Although physically a
|
||||
* switch, the notification does not provide an indication of
|
||||
* state and so it should be reported as a key */
|
||||
{KE_KEY, 0xe008, KEY_WLAN},
|
||||
{ KE_KEY, 0xe008, { KEY_WLAN } },
|
||||
|
||||
/* The next device is at offset 6, the active devices are at
|
||||
offset 8 and the attached devices at offset 10 */
|
||||
{KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE},
|
||||
{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
|
||||
|
||||
{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
|
||||
{ KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
|
||||
|
||||
/* BIOS error detected */
|
||||
{KE_IGNORE, 0xe00d, KEY_RESERVED},
|
||||
{ KE_IGNORE, 0xe00d, { KEY_RESERVED } },
|
||||
|
||||
/* Wifi Catcher */
|
||||
{KE_KEY, 0xe011, KEY_PROG2},
|
||||
{ KE_KEY, 0xe011, {KEY_PROG2 } },
|
||||
|
||||
/* Ambient light sensor toggle */
|
||||
{KE_IGNORE, 0xe013, KEY_RESERVED},
|
||||
{ KE_IGNORE, 0xe013, { KEY_RESERVED } },
|
||||
|
||||
{KE_IGNORE, 0xe020, KEY_MUTE},
|
||||
{KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN},
|
||||
{KE_IGNORE, 0xe030, KEY_VOLUMEUP},
|
||||
{KE_IGNORE, 0xe033, KEY_KBDILLUMUP},
|
||||
{KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN},
|
||||
{KE_IGNORE, 0xe03a, KEY_CAPSLOCK},
|
||||
{KE_IGNORE, 0xe045, KEY_NUMLOCK},
|
||||
{KE_IGNORE, 0xe046, KEY_SCROLLLOCK},
|
||||
{KE_END, 0}
|
||||
{ KE_IGNORE, 0xe020, { KEY_MUTE } },
|
||||
{ KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
|
||||
{ KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
|
||||
{ KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
|
||||
{ KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
|
||||
{ KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
|
||||
{ KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
|
||||
{ KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static bool dell_new_hk_type;
|
||||
|
||||
struct dell_new_keymap_entry {
|
||||
struct dell_bios_keymap_entry {
|
||||
u16 scancode;
|
||||
u16 keycode;
|
||||
};
|
||||
|
||||
struct dell_hotkey_table {
|
||||
struct dell_bios_hotkey_table {
|
||||
struct dmi_header header;
|
||||
struct dell_new_keymap_entry keymap[];
|
||||
struct dell_bios_keymap_entry keymap[];
|
||||
|
||||
};
|
||||
|
||||
static struct key_entry *dell_new_wmi_keymap;
|
||||
static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
|
||||
|
||||
static u16 bios_to_linux_keycode[256] = {
|
||||
static const u16 bios_to_linux_keycode[256] __initconst = {
|
||||
|
||||
KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
|
||||
KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
|
||||
|
@ -138,68 +131,11 @@ static u16 bios_to_linux_keycode[256] = {
|
|||
KEY_PROG3
|
||||
};
|
||||
|
||||
|
||||
static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap;
|
||||
|
||||
static struct input_dev *dell_wmi_input_dev;
|
||||
|
||||
static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code)
|
||||
{
|
||||
struct key_entry *key;
|
||||
|
||||
for (key = dell_wmi_keymap; key->type != KE_END; key++)
|
||||
if (code == key->code)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode)
|
||||
{
|
||||
struct key_entry *key;
|
||||
|
||||
for (key = dell_wmi_keymap; key->type != KE_END; key++)
|
||||
if (key->type == KE_KEY && keycode == key->keycode)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dell_wmi_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
{
|
||||
struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode);
|
||||
|
||||
if (key && key->type == KE_KEY) {
|
||||
*keycode = key->keycode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dell_wmi_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int old_keycode;
|
||||
|
||||
key = dell_wmi_get_entry_by_scancode(scancode);
|
||||
if (key && key->type == KE_KEY) {
|
||||
old_keycode = key->keycode;
|
||||
key->keycode = keycode;
|
||||
set_bit(keycode, dev->keybit);
|
||||
if (!dell_wmi_get_entry_by_keycode(old_keycode))
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void dell_wmi_notify(u32 value, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
static struct key_entry *key;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
|
@ -212,8 +148,10 @@ static void dell_wmi_notify(u32 value, void *context)
|
|||
obj = (union acpi_object *)response.pointer;
|
||||
|
||||
if (obj && obj->type == ACPI_TYPE_BUFFER) {
|
||||
const struct key_entry *key;
|
||||
int reported_key;
|
||||
u16 *buffer_entry = (u16 *)obj->buffer.pointer;
|
||||
|
||||
if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
|
||||
printk(KERN_INFO "dell-wmi: Received unknown WMI event"
|
||||
" (0x%x)\n", buffer_entry[1]);
|
||||
|
@ -226,8 +164,8 @@ static void dell_wmi_notify(u32 value, void *context)
|
|||
else
|
||||
reported_key = (int)buffer_entry[1] & 0xffff;
|
||||
|
||||
key = dell_wmi_get_entry_by_scancode(reported_key);
|
||||
|
||||
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
|
||||
reported_key);
|
||||
if (!key) {
|
||||
printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
|
||||
reported_key);
|
||||
|
@ -237,92 +175,98 @@ static void dell_wmi_notify(u32 value, void *context)
|
|||
* come via ACPI */
|
||||
;
|
||||
} else {
|
||||
input_report_key(dell_wmi_input_dev, key->keycode, 1);
|
||||
input_sync(dell_wmi_input_dev);
|
||||
input_report_key(dell_wmi_input_dev, key->keycode, 0);
|
||||
input_sync(dell_wmi_input_dev);
|
||||
sparse_keymap_report_entry(dell_wmi_input_dev, key,
|
||||
1, true);
|
||||
}
|
||||
}
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
|
||||
static void setup_new_hk_map(const struct dmi_header *dm)
|
||||
static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
|
||||
{
|
||||
|
||||
int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
|
||||
sizeof(struct dell_bios_keymap_entry);
|
||||
struct key_entry *keymap;
|
||||
int i;
|
||||
int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry);
|
||||
struct dell_hotkey_table *table =
|
||||
container_of(dm, struct dell_hotkey_table, header);
|
||||
|
||||
dell_new_wmi_keymap = kzalloc((hotkey_num+1) *
|
||||
sizeof(struct key_entry), GFP_KERNEL);
|
||||
keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
|
||||
if (!keymap)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < hotkey_num; i++) {
|
||||
dell_new_wmi_keymap[i].type = KE_KEY;
|
||||
dell_new_wmi_keymap[i].code = table->keymap[i].scancode;
|
||||
dell_new_wmi_keymap[i].keycode =
|
||||
(table->keymap[i].keycode > 255) ? 0 :
|
||||
bios_to_linux_keycode[table->keymap[i].keycode];
|
||||
const struct dell_bios_keymap_entry *bios_entry =
|
||||
&dell_bios_hotkey_table->keymap[i];
|
||||
keymap[i].type = KE_KEY;
|
||||
keymap[i].code = bios_entry->scancode;
|
||||
keymap[i].keycode = bios_entry->keycode < 256 ?
|
||||
bios_to_linux_keycode[bios_entry->keycode] :
|
||||
KEY_RESERVED;
|
||||
}
|
||||
|
||||
dell_new_wmi_keymap[i].type = KE_END;
|
||||
dell_new_wmi_keymap[i].code = 0;
|
||||
dell_new_wmi_keymap[i].keycode = 0;
|
||||
|
||||
dell_wmi_keymap = dell_new_wmi_keymap;
|
||||
keymap[hotkey_num].type = KE_END;
|
||||
|
||||
return keymap;
|
||||
}
|
||||
|
||||
|
||||
static void find_hk_type(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
|
||||
if ((dm->type == 0xb2) && (dm->length > 6)) {
|
||||
dell_new_hk_type = true;
|
||||
setup_new_hk_map(dm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int __init dell_wmi_input_setup(void)
|
||||
{
|
||||
struct key_entry *key;
|
||||
int err;
|
||||
|
||||
dell_wmi_input_dev = input_allocate_device();
|
||||
|
||||
if (!dell_wmi_input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dell_wmi_input_dev->name = "Dell WMI hotkeys";
|
||||
dell_wmi_input_dev->phys = "wmi/input0";
|
||||
dell_wmi_input_dev->id.bustype = BUS_HOST;
|
||||
dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode;
|
||||
dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode;
|
||||
|
||||
for (key = dell_wmi_keymap; key->type != KE_END; key++) {
|
||||
switch (key->type) {
|
||||
case KE_KEY:
|
||||
set_bit(EV_KEY, dell_wmi_input_dev->evbit);
|
||||
set_bit(key->keycode, dell_wmi_input_dev->keybit);
|
||||
break;
|
||||
case KE_SW:
|
||||
set_bit(EV_SW, dell_wmi_input_dev->evbit);
|
||||
set_bit(key->keycode, dell_wmi_input_dev->swbit);
|
||||
break;
|
||||
if (dell_new_hk_type) {
|
||||
const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
|
||||
if (!keymap) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
|
||||
|
||||
/*
|
||||
* Sparse keymap library makes a copy of keymap so we
|
||||
* don't need the original one that was allocated.
|
||||
*/
|
||||
kfree(keymap);
|
||||
} else {
|
||||
err = sparse_keymap_setup(dell_wmi_input_dev,
|
||||
dell_wmi_legacy_keymap, NULL);
|
||||
}
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
err = input_register_device(dell_wmi_input_dev);
|
||||
|
||||
if (err) {
|
||||
input_free_device(dell_wmi_input_dev);
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
goto err_free_keymap;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(dell_wmi_input_dev);
|
||||
err_free_dev:
|
||||
input_free_device(dell_wmi_input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void dell_wmi_input_destroy(void)
|
||||
{
|
||||
sparse_keymap_free(dell_wmi_input_dev);
|
||||
input_unregister_device(dell_wmi_input_dev);
|
||||
}
|
||||
|
||||
static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
if (dm->type == 0xb2 && dm->length > 6) {
|
||||
dell_new_hk_type = true;
|
||||
dell_bios_hotkey_table =
|
||||
container_of(dm, struct dell_bios_hotkey_table, header);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init dell_wmi_init(void)
|
||||
|
@ -339,18 +283,13 @@ static int __init dell_wmi_init(void)
|
|||
acpi_video = acpi_video_backlight_support();
|
||||
|
||||
err = dell_wmi_input_setup();
|
||||
if (err) {
|
||||
if (dell_new_hk_type)
|
||||
kfree(dell_wmi_keymap);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
status = wmi_install_notify_handler(DELL_EVENT_GUID,
|
||||
dell_wmi_notify, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
input_unregister_device(dell_wmi_input_dev);
|
||||
if (dell_new_hk_type)
|
||||
kfree(dell_wmi_keymap);
|
||||
dell_wmi_input_destroy();
|
||||
printk(KERN_ERR
|
||||
"dell-wmi: Unable to register notify handler - %d\n",
|
||||
status);
|
||||
|
@ -359,14 +298,11 @@ static int __init dell_wmi_init(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
module_init(dell_wmi_init);
|
||||
|
||||
static void __exit dell_wmi_exit(void)
|
||||
{
|
||||
wmi_remove_notify_handler(DELL_EVENT_GUID);
|
||||
input_unregister_device(dell_wmi_input_dev);
|
||||
if (dell_new_hk_type)
|
||||
kfree(dell_wmi_keymap);
|
||||
dell_wmi_input_destroy();
|
||||
}
|
||||
|
||||
module_init(dell_wmi_init);
|
||||
module_exit(dell_wmi_exit);
|
||||
|
|
|
@ -165,6 +165,7 @@ struct eeepc_laptop {
|
|||
u16 event_count[128]; /* count for each event */
|
||||
|
||||
struct platform_device *platform_device;
|
||||
struct acpi_device *device; /* the device we are in */
|
||||
struct device *hwmon_device;
|
||||
struct backlight_device *backlight_device;
|
||||
|
||||
|
@ -1193,9 +1194,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
|
|||
eeepc->inputdev = input;
|
||||
return 0;
|
||||
|
||||
err_free_keymap:
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(input);
|
||||
err_free_dev:
|
||||
err_free_dev:
|
||||
input_free_device(input);
|
||||
return error;
|
||||
}
|
||||
|
@ -1206,6 +1207,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
|
|||
sparse_keymap_free(eeepc->inputdev);
|
||||
input_unregister_device(eeepc->inputdev);
|
||||
}
|
||||
eeepc->inputdev = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1326,16 +1328,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc)
|
|||
cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
|
||||
}
|
||||
|
||||
static int eeepc_acpi_init(struct eeepc_laptop *eeepc,
|
||||
struct acpi_device *device)
|
||||
static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
|
||||
{
|
||||
unsigned int init_flags;
|
||||
int result;
|
||||
|
||||
result = acpi_bus_get_status(device);
|
||||
result = acpi_bus_get_status(eeepc->device);
|
||||
if (result)
|
||||
return result;
|
||||
if (!device->status.present) {
|
||||
if (!eeepc->device->status.present) {
|
||||
pr_err("Hotkey device not present, aborting\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -1384,12 +1385,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device)
|
|||
strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
|
||||
strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
|
||||
device->driver_data = eeepc;
|
||||
eeepc->device = device;
|
||||
|
||||
eeepc->hotplug_disabled = hotplug_disabled;
|
||||
|
||||
eeepc_dmi_check(eeepc);
|
||||
|
||||
result = eeepc_acpi_init(eeepc, device);
|
||||
result = eeepc_acpi_init(eeepc);
|
||||
if (result)
|
||||
goto fail_platform;
|
||||
eeepc_enable_camera(eeepc);
|
||||
|
|
|
@ -57,6 +57,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
|
|||
|
||||
#define EEEPC_WMI_METHODID_DEVS 0x53564544
|
||||
#define EEEPC_WMI_METHODID_DSTS 0x53544344
|
||||
#define EEEPC_WMI_METHODID_CFVS 0x53564643
|
||||
|
||||
#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
|
||||
|
||||
|
@ -69,6 +70,11 @@ static const struct key_entry eeepc_wmi_keymap[] = {
|
|||
{ KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
|
||||
{ KE_KEY, 0xe1, { KEY_F14 } },
|
||||
{ KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
|
||||
{ KE_KEY, 0xe0, { KEY_PROG1 } },
|
||||
{ KE_KEY, 0x5c, { KEY_F15 } },
|
||||
{ KE_END, 0},
|
||||
};
|
||||
|
||||
|
@ -292,6 +298,49 @@ static void eeepc_wmi_notify(u32 value, void *context)
|
|||
kfree(obj);
|
||||
}
|
||||
|
||||
static int 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 void eeepc_wmi_sysfs_exit(struct platform_device *device)
|
||||
{
|
||||
device_remove_file(&device->dev, &dev_attr_cpufv);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_sysfs_init(struct platform_device *device)
|
||||
{
|
||||
int retval = -ENOMEM;
|
||||
|
||||
retval = device_create_file(&device->dev, &dev_attr_cpufv);
|
||||
if (retval)
|
||||
goto error_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
error_sysfs:
|
||||
eeepc_wmi_sysfs_exit(platform_device);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
@ -387,8 +436,14 @@ static int __init eeepc_wmi_init(void)
|
|||
goto del_dev;
|
||||
}
|
||||
|
||||
err = eeepc_wmi_sysfs_init(platform_device);
|
||||
if (err)
|
||||
goto del_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
del_sysfs:
|
||||
eeepc_wmi_sysfs_exit(platform_device);
|
||||
del_dev:
|
||||
platform_device_del(platform_device);
|
||||
put_dev:
|
||||
|
@ -403,6 +458,7 @@ static void __exit eeepc_wmi_exit(void)
|
|||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc_wmi_sysfs_exit(platform_device);
|
||||
eeepc = platform_get_drvdata(platform_device);
|
||||
platform_driver_unregister(&platform_driver);
|
||||
platform_device_unregister(platform_device);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System
|
||||
* hdaps.c - driver for IBM's Hard Drive Active Protection System
|
||||
*
|
||||
* Copyright (C) 2005 Robert Love <rml@novell.com>
|
||||
* Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/rfkill.h>
|
||||
|
@ -88,24 +89,16 @@ struct bios_return {
|
|||
u32 value;
|
||||
};
|
||||
|
||||
struct key_entry {
|
||||
char type; /* See KE_* below */
|
||||
u16 code;
|
||||
u16 keycode;
|
||||
};
|
||||
|
||||
enum { KE_KEY, KE_END };
|
||||
|
||||
static struct key_entry hp_wmi_keymap[] = {
|
||||
{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
|
||||
{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
|
||||
{KE_KEY, 0x20e6, KEY_PROG1},
|
||||
{KE_KEY, 0x20e8, KEY_MEDIA},
|
||||
{KE_KEY, 0x2142, KEY_MEDIA},
|
||||
{KE_KEY, 0x213b, KEY_INFO},
|
||||
{KE_KEY, 0x2169, KEY_DIRECTION},
|
||||
{KE_KEY, 0x231b, KEY_HELP},
|
||||
{KE_END, 0}
|
||||
static const struct key_entry hp_wmi_keymap[] = {
|
||||
{ KE_KEY, 0x02, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_KEY, 0x20e6, { KEY_PROG1 } },
|
||||
{ KE_KEY, 0x20e8, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x2142, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x213b, { KEY_INFO } },
|
||||
{ KE_KEY, 0x2169, { KEY_DIRECTION } },
|
||||
{ KE_KEY, 0x231b, { KEY_HELP } },
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static struct input_dev *hp_wmi_input_dev;
|
||||
|
@ -347,64 +340,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
|
|||
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
|
||||
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
|
||||
|
||||
static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code)
|
||||
{
|
||||
struct key_entry *key;
|
||||
|
||||
for (key = hp_wmi_keymap; key->type != KE_END; key++)
|
||||
if (code == key->code)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode)
|
||||
{
|
||||
struct key_entry *key;
|
||||
|
||||
for (key = hp_wmi_keymap; key->type != KE_END; key++)
|
||||
if (key->type == KE_KEY && keycode == key->keycode)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int hp_wmi_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
{
|
||||
struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
|
||||
|
||||
if (key && key->type == KE_KEY) {
|
||||
*keycode = key->keycode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int hp_wmi_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int old_keycode;
|
||||
|
||||
key = hp_wmi_get_entry_by_scancode(scancode);
|
||||
if (key && key->type == KE_KEY) {
|
||||
old_keycode = key->keycode;
|
||||
key->keycode = keycode;
|
||||
set_bit(keycode, dev->keybit);
|
||||
if (!hp_wmi_get_entry_by_keycode(old_keycode))
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void hp_wmi_notify(u32 value, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
static struct key_entry *key;
|
||||
union acpi_object *obj;
|
||||
u32 event_id, event_data;
|
||||
int key_code = 0, ret;
|
||||
|
@ -465,19 +403,9 @@ static void hp_wmi_notify(u32 value, void *context)
|
|||
sizeof(key_code));
|
||||
if (ret)
|
||||
break;
|
||||
key = hp_wmi_get_entry_by_scancode(key_code);
|
||||
if (key) {
|
||||
switch (key->type) {
|
||||
case KE_KEY:
|
||||
input_report_key(hp_wmi_input_dev,
|
||||
key->keycode, 1);
|
||||
input_sync(hp_wmi_input_dev);
|
||||
input_report_key(hp_wmi_input_dev,
|
||||
key->keycode, 0);
|
||||
input_sync(hp_wmi_input_dev);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
|
||||
if (!sparse_keymap_report_event(hp_wmi_input_dev,
|
||||
key_code, 1, true))
|
||||
printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n",
|
||||
key_code);
|
||||
break;
|
||||
|
@ -510,7 +438,7 @@ static void hp_wmi_notify(u32 value, void *context)
|
|||
|
||||
static int __init hp_wmi_input_setup(void)
|
||||
{
|
||||
struct key_entry *key;
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
hp_wmi_input_dev = input_allocate_device();
|
||||
|
@ -520,21 +448,14 @@ static int __init hp_wmi_input_setup(void)
|
|||
hp_wmi_input_dev->name = "HP WMI hotkeys";
|
||||
hp_wmi_input_dev->phys = "wmi/input0";
|
||||
hp_wmi_input_dev->id.bustype = BUS_HOST;
|
||||
hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
|
||||
hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
|
||||
|
||||
for (key = hp_wmi_keymap; key->type != KE_END; key++) {
|
||||
switch (key->type) {
|
||||
case KE_KEY:
|
||||
set_bit(EV_KEY, hp_wmi_input_dev->evbit);
|
||||
set_bit(key->keycode, hp_wmi_input_dev->keybit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
__set_bit(EV_SW, hp_wmi_input_dev->evbit);
|
||||
__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
|
||||
__set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
|
||||
|
||||
set_bit(EV_SW, hp_wmi_input_dev->evbit);
|
||||
set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
|
||||
set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
|
||||
err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
/* Set initial hardware state */
|
||||
input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
|
||||
|
@ -542,14 +463,32 @@ static int __init hp_wmi_input_setup(void)
|
|||
hp_wmi_tablet_state());
|
||||
input_sync(hp_wmi_input_dev);
|
||||
|
||||
err = input_register_device(hp_wmi_input_dev);
|
||||
|
||||
if (err) {
|
||||
input_free_device(hp_wmi_input_dev);
|
||||
return err;
|
||||
status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
err = -EIO;
|
||||
goto err_free_keymap;
|
||||
}
|
||||
|
||||
err = input_register_device(hp_wmi_input_dev);
|
||||
if (err)
|
||||
goto err_uninstall_notifier;
|
||||
|
||||
return 0;
|
||||
|
||||
err_uninstall_notifier:
|
||||
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(hp_wmi_input_dev);
|
||||
err_free_dev:
|
||||
input_free_device(hp_wmi_input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hp_wmi_input_destroy(void)
|
||||
{
|
||||
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
|
||||
sparse_keymap_free(hp_wmi_input_dev);
|
||||
input_unregister_device(hp_wmi_input_dev);
|
||||
}
|
||||
|
||||
static void cleanup_sysfs(struct platform_device *device)
|
||||
|
@ -704,15 +643,9 @@ static int __init hp_wmi_init(void)
|
|||
int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
|
||||
|
||||
if (event_capable) {
|
||||
err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
|
||||
hp_wmi_notify, NULL);
|
||||
if (ACPI_FAILURE(err))
|
||||
return -EINVAL;
|
||||
err = hp_wmi_input_setup();
|
||||
if (err) {
|
||||
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (bios_capable) {
|
||||
|
@ -739,20 +672,17 @@ err_device_add:
|
|||
err_device_alloc:
|
||||
platform_driver_unregister(&hp_wmi_driver);
|
||||
err_driver_reg:
|
||||
if (wmi_has_guid(HPWMI_EVENT_GUID)) {
|
||||
input_unregister_device(hp_wmi_input_dev);
|
||||
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
|
||||
}
|
||||
if (event_capable)
|
||||
hp_wmi_input_destroy();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit hp_wmi_exit(void)
|
||||
{
|
||||
if (wmi_has_guid(HPWMI_EVENT_GUID)) {
|
||||
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
|
||||
input_unregister_device(hp_wmi_input_dev);
|
||||
}
|
||||
if (wmi_has_guid(HPWMI_EVENT_GUID))
|
||||
hp_wmi_input_destroy();
|
||||
|
||||
if (hp_wmi_platform_dev) {
|
||||
platform_device_unregister(hp_wmi_platform_dev);
|
||||
platform_driver_unregister(&hp_wmi_driver);
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* IBM Real-Time Linux driver
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2010
|
||||
*
|
||||
* Author: Keith Mannthey <kmannth@us.ibm.com>
|
||||
* Vernon Mauery <vernux@us.ibm.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sysdev.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/bios_ebda.h>
|
||||
|
||||
static bool force;
|
||||
module_param(force, bool, 0);
|
||||
MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
|
||||
|
||||
static bool debug;
|
||||
module_param(debug, bool, 0644);
|
||||
MODULE_PARM_DESC(debug, "Show debug output");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>");
|
||||
MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>");
|
||||
|
||||
#define RTL_ADDR_TYPE_IO 1
|
||||
#define RTL_ADDR_TYPE_MMIO 2
|
||||
|
||||
#define RTL_CMD_ENTER_PRTM 1
|
||||
#define RTL_CMD_EXIT_PRTM 2
|
||||
|
||||
/* The RTL table as presented by the EBDA: */
|
||||
struct ibm_rtl_table {
|
||||
char signature[5]; /* signature should be "_RTL_" */
|
||||
u8 version;
|
||||
u8 rt_status;
|
||||
u8 command;
|
||||
u8 command_status;
|
||||
u8 cmd_address_type;
|
||||
u8 cmd_granularity;
|
||||
u8 cmd_offset;
|
||||
u16 reserve1;
|
||||
u32 cmd_port_address; /* platform dependent address */
|
||||
u32 cmd_port_value; /* platform dependent value */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* to locate "_RTL_" signature do a masked 5-byte integer compare */
|
||||
#define RTL_SIGNATURE 0x0000005f4c54525fULL
|
||||
#define RTL_MASK 0x000000ffffffffffULL
|
||||
|
||||
#define RTL_DEBUG(A, ...) do { \
|
||||
if (debug) \
|
||||
pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \
|
||||
} while (0)
|
||||
|
||||
static DEFINE_MUTEX(rtl_lock);
|
||||
static struct ibm_rtl_table __iomem *rtl_table;
|
||||
static void __iomem *ebda_map;
|
||||
static void __iomem *rtl_cmd_addr;
|
||||
static u8 rtl_cmd_type;
|
||||
static u8 rtl_cmd_width;
|
||||
|
||||
static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len)
|
||||
{
|
||||
if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
|
||||
return ioremap(addr, len);
|
||||
return ioport_map(addr, len);
|
||||
}
|
||||
|
||||
static void rtl_port_unmap(void __iomem *addr)
|
||||
{
|
||||
if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
|
||||
iounmap(addr);
|
||||
else
|
||||
ioport_unmap(addr);
|
||||
}
|
||||
|
||||
static int ibm_rtl_write(u8 value)
|
||||
{
|
||||
int ret = 0, count = 0;
|
||||
static u32 cmd_port_val;
|
||||
|
||||
RTL_DEBUG("%s(%d)\n", __FUNCTION__, value);
|
||||
|
||||
value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM;
|
||||
|
||||
mutex_lock(&rtl_lock);
|
||||
|
||||
if (ioread8(&rtl_table->rt_status) != value) {
|
||||
iowrite8(value, &rtl_table->command);
|
||||
|
||||
switch (rtl_cmd_width) {
|
||||
case 8:
|
||||
cmd_port_val = ioread8(&rtl_table->cmd_port_value);
|
||||
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
|
||||
iowrite8((u8)cmd_port_val, rtl_cmd_addr);
|
||||
break;
|
||||
case 16:
|
||||
cmd_port_val = ioread16(&rtl_table->cmd_port_value);
|
||||
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
|
||||
iowrite16((u16)cmd_port_val, rtl_cmd_addr);
|
||||
break;
|
||||
case 32:
|
||||
cmd_port_val = ioread32(&rtl_table->cmd_port_value);
|
||||
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
|
||||
iowrite32(cmd_port_val, rtl_cmd_addr);
|
||||
break;
|
||||
}
|
||||
|
||||
while (ioread8(&rtl_table->command)) {
|
||||
msleep(10);
|
||||
if (count++ > 500) {
|
||||
pr_err("ibm-rtl: Hardware not responding to "
|
||||
"mode switch request\n");
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ioread8(&rtl_table->command_status)) {
|
||||
RTL_DEBUG("command_status reports failed command\n");
|
||||
ret = -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&rtl_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t rtl_show_version(struct sysdev_class * dev,
|
||||
struct sysdev_class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version));
|
||||
}
|
||||
|
||||
static ssize_t rtl_show_state(struct sysdev_class *dev,
|
||||
struct sysdev_class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status));
|
||||
}
|
||||
|
||||
static ssize_t rtl_set_state(struct sysdev_class *dev,
|
||||
struct sysdev_class_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
if (count < 1 || count > 2)
|
||||
return -EINVAL;
|
||||
|
||||
switch (buf[0]) {
|
||||
case '0':
|
||||
ret = ibm_rtl_write(0);
|
||||
break;
|
||||
case '1':
|
||||
ret = ibm_rtl_write(1);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (ret >= 0)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct sysdev_class class_rtl = {
|
||||
.name = "ibm_rtl",
|
||||
};
|
||||
|
||||
static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL);
|
||||
static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state);
|
||||
|
||||
static struct sysdev_class_attribute *rtl_attributes[] = {
|
||||
&attr_version,
|
||||
&attr_state,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static int rtl_setup_sysfs(void) {
|
||||
int ret, i;
|
||||
ret = sysdev_class_register(&class_rtl);
|
||||
|
||||
if (!ret) {
|
||||
for (i = 0; rtl_attributes[i]; i ++)
|
||||
sysdev_class_create_file(&class_rtl, rtl_attributes[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rtl_teardown_sysfs(void) {
|
||||
int i;
|
||||
for (i = 0; rtl_attributes[i]; i ++)
|
||||
sysdev_class_remove_file(&class_rtl, rtl_attributes[i]);
|
||||
sysdev_class_unregister(&class_rtl);
|
||||
}
|
||||
|
||||
static int dmi_check_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
RTL_DEBUG("found IBM server '%s'\n", id->ident);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ibm_dmi_entry(NAME, TYPE) \
|
||||
{ \
|
||||
.ident = NAME, \
|
||||
.matches = { \
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \
|
||||
}, \
|
||||
.callback = dmi_check_cb \
|
||||
}
|
||||
|
||||
static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = {
|
||||
ibm_dmi_entry("BladeCenter LS21", "7971"),
|
||||
ibm_dmi_entry("BladeCenter LS22", "7901"),
|
||||
ibm_dmi_entry("BladeCenter HS21 XM", "7995"),
|
||||
ibm_dmi_entry("BladeCenter HS22", "7870"),
|
||||
ibm_dmi_entry("BladeCenter HS22V", "7871"),
|
||||
ibm_dmi_entry("System x3550 M2", "7946"),
|
||||
ibm_dmi_entry("System x3650 M2", "7947"),
|
||||
ibm_dmi_entry("System x3550 M3", "7944"),
|
||||
ibm_dmi_entry("System x3650 M3", "7945"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static int __init ibm_rtl_init(void) {
|
||||
unsigned long ebda_addr, ebda_size;
|
||||
unsigned int ebda_kb;
|
||||
int ret = -ENODEV, i;
|
||||
|
||||
if (force)
|
||||
pr_warning("ibm-rtl: module loaded by force\n");
|
||||
/* first ensure that we are running on IBM HW */
|
||||
else if (!dmi_check_system(ibm_rtl_dmi_table))
|
||||
return -ENODEV;
|
||||
|
||||
/* Get the address for the Extended BIOS Data Area */
|
||||
ebda_addr = get_bios_ebda();
|
||||
if (!ebda_addr) {
|
||||
RTL_DEBUG("no BIOS EBDA found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ebda_map = ioremap(ebda_addr, 4);
|
||||
if (!ebda_map)
|
||||
return -ENOMEM;
|
||||
|
||||
/* First word in the EDBA is the Size in KB */
|
||||
ebda_kb = ioread16(ebda_map);
|
||||
RTL_DEBUG("EBDA is %d kB\n", ebda_kb);
|
||||
|
||||
if (ebda_kb == 0)
|
||||
goto out;
|
||||
|
||||
iounmap(ebda_map);
|
||||
ebda_size = ebda_kb*1024;
|
||||
|
||||
/* Remap the whole table */
|
||||
ebda_map = ioremap(ebda_addr, ebda_size);
|
||||
if (!ebda_map)
|
||||
return -ENOMEM;
|
||||
|
||||
/* search for the _RTL_ signature at the start of the table */
|
||||
for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) {
|
||||
struct ibm_rtl_table __iomem * tmp;
|
||||
tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i);
|
||||
if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) {
|
||||
phys_addr_t addr;
|
||||
unsigned int plen;
|
||||
RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp);
|
||||
rtl_table = tmp;
|
||||
/* The address, value, width and offset are platform
|
||||
* dependent and found in the ibm_rtl_table */
|
||||
rtl_cmd_width = ioread8(&rtl_table->cmd_granularity);
|
||||
rtl_cmd_type = ioread8(&rtl_table->cmd_address_type);
|
||||
RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n",
|
||||
rtl_cmd_width, rtl_cmd_type);
|
||||
addr = ioread32(&rtl_table->cmd_port_address);
|
||||
RTL_DEBUG("addr = %#llx\n", addr);
|
||||
plen = rtl_cmd_width/sizeof(char);
|
||||
rtl_cmd_addr = rtl_port_map(addr, plen);
|
||||
RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr);
|
||||
if (!rtl_cmd_addr) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
ret = rtl_setup_sysfs();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
iounmap(ebda_map);
|
||||
rtl_port_unmap(rtl_cmd_addr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ibm_rtl_exit(void)
|
||||
{
|
||||
if (rtl_table) {
|
||||
RTL_DEBUG("cleaning up");
|
||||
/* do not leave the machine in SMI-free mode */
|
||||
ibm_rtl_write(0);
|
||||
/* unmap, unlink and remove all traces */
|
||||
rtl_teardown_sysfs();
|
||||
iounmap(ebda_map);
|
||||
rtl_port_unmap(rtl_cmd_addr);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(ibm_rtl_init);
|
||||
module_exit(ibm_rtl_exit);
|
|
@ -35,112 +35,162 @@
|
|||
#define IDEAPAD_DEV_KILLSW 4
|
||||
|
||||
struct ideapad_private {
|
||||
acpi_handle handle;
|
||||
struct rfkill *rfk[5];
|
||||
};
|
||||
} *ideapad_priv;
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
int cfgbit;
|
||||
int opcode;
|
||||
int type;
|
||||
} ideapad_rfk_data[] = {
|
||||
/* camera has no rfkill */
|
||||
{ "ideapad_wlan", RFKILL_TYPE_WLAN },
|
||||
{ "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH },
|
||||
{ "ideapad_3g", RFKILL_TYPE_WWAN },
|
||||
{ "ideapad_killsw", RFKILL_TYPE_WLAN }
|
||||
{ "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES },
|
||||
{ "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN },
|
||||
{ "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH },
|
||||
{ "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN },
|
||||
{ "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN }
|
||||
};
|
||||
|
||||
static int ideapad_dev_exists(int device)
|
||||
static bool no_bt_rfkill;
|
||||
module_param(no_bt_rfkill, bool, 0444);
|
||||
MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
|
||||
|
||||
/*
|
||||
* ACPI Helpers
|
||||
*/
|
||||
#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
|
||||
|
||||
static int read_method_int(acpi_handle handle, const char *method, int *val)
|
||||
{
|
||||
acpi_status status;
|
||||
union acpi_object in_param;
|
||||
struct acpi_object_list input = { 1, &in_param };
|
||||
struct acpi_buffer output;
|
||||
union acpi_object out_obj;
|
||||
unsigned long long result;
|
||||
|
||||
output.length = sizeof(out_obj);
|
||||
output.pointer = &out_obj;
|
||||
|
||||
in_param.type = ACPI_TYPE_INTEGER;
|
||||
in_param.integer.value = device + 1;
|
||||
|
||||
status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output);
|
||||
status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status);
|
||||
return -ENODEV;
|
||||
*val = -1;
|
||||
return -1;
|
||||
} else {
|
||||
*val = result;
|
||||
return 0;
|
||||
}
|
||||
if (out_obj.type != ACPI_TYPE_INTEGER) {
|
||||
printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
return out_obj.integer.value;
|
||||
}
|
||||
|
||||
static int ideapad_dev_get_state(int device)
|
||||
static int method_vpcr(acpi_handle handle, int cmd, int *ret)
|
||||
{
|
||||
acpi_status status;
|
||||
union acpi_object in_param;
|
||||
struct acpi_object_list input = { 1, &in_param };
|
||||
struct acpi_buffer output;
|
||||
union acpi_object out_obj;
|
||||
unsigned long long result;
|
||||
struct acpi_object_list params;
|
||||
union acpi_object in_obj;
|
||||
|
||||
output.length = sizeof(out_obj);
|
||||
output.pointer = &out_obj;
|
||||
params.count = 1;
|
||||
params.pointer = &in_obj;
|
||||
in_obj.type = ACPI_TYPE_INTEGER;
|
||||
in_obj.integer.value = cmd;
|
||||
|
||||
in_param.type = ACPI_TYPE_INTEGER;
|
||||
in_param.integer.value = device + 1;
|
||||
status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result);
|
||||
|
||||
status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status);
|
||||
return -ENODEV;
|
||||
*ret = -1;
|
||||
return -1;
|
||||
} else {
|
||||
*ret = result;
|
||||
return 0;
|
||||
}
|
||||
if (out_obj.type != ACPI_TYPE_INTEGER) {
|
||||
printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
return out_obj.integer.value;
|
||||
}
|
||||
|
||||
static int ideapad_dev_set_state(int device, int state)
|
||||
static int method_vpcw(acpi_handle handle, int cmd, int data)
|
||||
{
|
||||
struct acpi_object_list params;
|
||||
union acpi_object in_obj[2];
|
||||
acpi_status status;
|
||||
union acpi_object in_params[2];
|
||||
struct acpi_object_list input = { 2, in_params };
|
||||
|
||||
in_params[0].type = ACPI_TYPE_INTEGER;
|
||||
in_params[0].integer.value = device + 1;
|
||||
in_params[1].type = ACPI_TYPE_INTEGER;
|
||||
in_params[1].integer.value = state;
|
||||
params.count = 2;
|
||||
params.pointer = in_obj;
|
||||
in_obj[0].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[0].integer.value = cmd;
|
||||
in_obj[1].type = ACPI_TYPE_INTEGER;
|
||||
in_obj[1].integer.value = data;
|
||||
|
||||
status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status);
|
||||
return -ENODEV;
|
||||
}
|
||||
status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL);
|
||||
if (status != AE_OK)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
|
||||
{
|
||||
int val;
|
||||
unsigned long int end_jiffies;
|
||||
|
||||
if (method_vpcw(handle, 1, cmd))
|
||||
return -1;
|
||||
|
||||
for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
|
||||
time_before(jiffies, end_jiffies);) {
|
||||
schedule();
|
||||
if (method_vpcr(handle, 1, &val))
|
||||
return -1;
|
||||
if (val == 0) {
|
||||
if (method_vpcr(handle, 0, &val))
|
||||
return -1;
|
||||
*data = val;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pr_err("timeout in read_ec_cmd\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
|
||||
{
|
||||
int val;
|
||||
unsigned long int end_jiffies;
|
||||
|
||||
if (method_vpcw(handle, 0, data))
|
||||
return -1;
|
||||
if (method_vpcw(handle, 1, cmd))
|
||||
return -1;
|
||||
|
||||
for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
|
||||
time_before(jiffies, end_jiffies);) {
|
||||
schedule();
|
||||
if (method_vpcr(handle, 1, &val))
|
||||
return -1;
|
||||
if (val == 0)
|
||||
return 0;
|
||||
}
|
||||
pr_err("timeout in write_ec_cmd\n");
|
||||
return -1;
|
||||
}
|
||||
/* the above is ACPI helpers */
|
||||
|
||||
static ssize_t show_ideapad_cam(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA);
|
||||
if (state < 0)
|
||||
return state;
|
||||
struct ideapad_private *priv = dev_get_drvdata(dev);
|
||||
acpi_handle handle = priv->handle;
|
||||
unsigned long result;
|
||||
|
||||
return sprintf(buf, "%d\n", state);
|
||||
if (read_ec_data(handle, 0x1D, &result))
|
||||
return sprintf(buf, "-1\n");
|
||||
return sprintf(buf, "%lu\n", result);
|
||||
}
|
||||
|
||||
static ssize_t store_ideapad_cam(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(dev);
|
||||
acpi_handle handle = priv->handle;
|
||||
int ret, state;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
if (sscanf(buf, "%i", &state) != 1)
|
||||
return -EINVAL;
|
||||
ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state);
|
||||
ret = write_ec_cmd(handle, 0x1E, state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return count;
|
||||
|
@ -154,7 +204,10 @@ static int ideapad_rfk_set(void *data, bool blocked)
|
|||
|
||||
if (device == IDEAPAD_DEV_KILLSW)
|
||||
return -EINVAL;
|
||||
return ideapad_dev_set_state(device, !blocked);
|
||||
|
||||
return write_ec_cmd(ideapad_priv->handle,
|
||||
ideapad_rfk_data[device].opcode,
|
||||
!blocked);
|
||||
}
|
||||
|
||||
static struct rfkill_ops ideapad_rfk_ops = {
|
||||
|
@ -164,32 +217,47 @@ static struct rfkill_ops ideapad_rfk_ops = {
|
|||
static void ideapad_sync_rfk_state(struct acpi_device *adevice)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
|
||||
int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW);
|
||||
acpi_handle handle = priv->handle;
|
||||
unsigned long hw_blocked;
|
||||
int i;
|
||||
|
||||
rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked);
|
||||
for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
|
||||
if (read_ec_data(handle, 0x23, &hw_blocked))
|
||||
return;
|
||||
hw_blocked = !hw_blocked;
|
||||
|
||||
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
|
||||
if (priv->rfk[i])
|
||||
rfkill_set_hw_state(priv->rfk[i], hw_blocked);
|
||||
if (hw_blocked)
|
||||
return;
|
||||
|
||||
for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
|
||||
if (priv->rfk[i])
|
||||
rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i));
|
||||
}
|
||||
|
||||
static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
|
||||
int ret;
|
||||
unsigned long sw_blocked;
|
||||
|
||||
priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev,
|
||||
ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops,
|
||||
if (no_bt_rfkill &&
|
||||
(ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
|
||||
/* Force to enable bluetooth when no_bt_rfkill=1 */
|
||||
write_ec_cmd(ideapad_priv->handle,
|
||||
ideapad_rfk_data[dev].opcode, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
|
||||
ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
|
||||
(void *)(long)dev);
|
||||
if (!priv->rfk[dev])
|
||||
return -ENOMEM;
|
||||
|
||||
if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1,
|
||||
&sw_blocked)) {
|
||||
rfkill_init_sw_state(priv->rfk[dev], 0);
|
||||
} else {
|
||||
sw_blocked = !sw_blocked;
|
||||
rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
|
||||
}
|
||||
|
||||
ret = rfkill_register(priv->rfk[dev]);
|
||||
if (ret) {
|
||||
rfkill_destroy(priv->rfk[dev]);
|
||||
|
@ -217,14 +285,18 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
|
|||
|
||||
static int ideapad_acpi_add(struct acpi_device *adevice)
|
||||
{
|
||||
int i;
|
||||
int i, cfg;
|
||||
int devs_present[5];
|
||||
struct ideapad_private *priv;
|
||||
|
||||
if (read_method_int(adevice->handle, "_CFG", &cfg))
|
||||
return -ENODEV;
|
||||
|
||||
for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
|
||||
devs_present[i] = ideapad_dev_exists(i);
|
||||
if (devs_present[i] < 0)
|
||||
return devs_present[i];
|
||||
if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
|
||||
devs_present[i] = 1;
|
||||
else
|
||||
devs_present[i] = 0;
|
||||
}
|
||||
|
||||
/* The hardware switch is always present */
|
||||
|
@ -242,7 +314,9 @@ static int ideapad_acpi_add(struct acpi_device *adevice)
|
|||
}
|
||||
}
|
||||
|
||||
priv->handle = adevice->handle;
|
||||
dev_set_drvdata(&adevice->dev, priv);
|
||||
ideapad_priv = priv;
|
||||
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
|
||||
if (!devs_present[i])
|
||||
continue;
|
||||
|
@ -270,7 +344,21 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
|
|||
|
||||
static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
|
||||
{
|
||||
ideapad_sync_rfk_state(adevice);
|
||||
acpi_handle handle = adevice->handle;
|
||||
unsigned long vpc1, vpc2, vpc_bit;
|
||||
|
||||
if (read_ec_data(handle, 0x10, &vpc1))
|
||||
return;
|
||||
if (read_ec_data(handle, 0x1A, &vpc2))
|
||||
return;
|
||||
|
||||
vpc1 = (vpc2 << 8) | vpc1;
|
||||
for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
|
||||
if (test_bit(vpc_bit, &vpc1)) {
|
||||
if (vpc_bit == 9)
|
||||
ideapad_sync_rfk_state(adevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct acpi_driver ideapad_acpi_driver = {
|
|
@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip,
|
|||
|
||||
if (offset < 8)/* it is GPIO */
|
||||
rc = intel_scu_ipc_update_register(GPIO0 + offset,
|
||||
GPIO_DRV | GPIO_DOU | GPIO_DIR,
|
||||
GPIO_DRV | (value ? GPIO_DOU : 0));
|
||||
GPIO_DRV | (value ? GPIO_DOU : 0),
|
||||
GPIO_DRV | GPIO_DOU | GPIO_DIR);
|
||||
else if (offset < 16)/* it is GPOSW */
|
||||
rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
|
||||
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
|
||||
GPOSW_DRV | (value ? GPOSW_DOU : 0));
|
||||
GPOSW_DRV | (value ? GPOSW_DOU : 0),
|
||||
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
|
||||
else if (offset > 15 && offset < 24)/* it is GPO */
|
||||
rc = intel_scu_ipc_update_register(GPO,
|
||||
1 << (offset - 16),
|
||||
value ? 1 << (offset - 16) : 0);
|
||||
value ? 1 << (offset - 16) : 0,
|
||||
1 << (offset - 16));
|
||||
else {
|
||||
printk(KERN_ERR
|
||||
"%s: invalid PMIC GPIO pin %d!\n", __func__, offset);
|
||||
|
@ -179,16 +179,16 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
|||
{
|
||||
if (offset < 8)/* it is GPIO */
|
||||
intel_scu_ipc_update_register(GPIO0 + offset,
|
||||
GPIO_DRV | GPIO_DOU,
|
||||
GPIO_DRV | (value ? GPIO_DOU : 0));
|
||||
GPIO_DRV | (value ? GPIO_DOU : 0),
|
||||
GPIO_DRV | GPIO_DOU);
|
||||
else if (offset < 16)/* it is GPOSW */
|
||||
intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
|
||||
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
|
||||
GPOSW_DRV | (value ? GPOSW_DOU : 0));
|
||||
GPOSW_DRV | (value ? GPOSW_DOU : 0),
|
||||
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
|
||||
else if (offset > 15 && offset < 24) /* it is GPO */
|
||||
intel_scu_ipc_update_register(GPO,
|
||||
1 << (offset - 16),
|
||||
value ? 1 << (offset - 16) : 0);
|
||||
value ? 1 << (offset - 16) : 0,
|
||||
1 << (offset - 16));
|
||||
}
|
||||
|
||||
static int pmic_irq_type(unsigned irq, unsigned type)
|
||||
|
@ -197,7 +197,7 @@ static int pmic_irq_type(unsigned irq, unsigned type)
|
|||
u32 gpio = irq - pg->irq_base;
|
||||
unsigned long flags;
|
||||
|
||||
if (gpio > pg->chip.ngpio)
|
||||
if (gpio >= pg->chip.ngpio)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&pg->irqtypes.lock, flags);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/pm.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sfi.h>
|
||||
#include <asm/mrst.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@
|
|||
#include <acpi/acpi_bus.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
|
||||
|
||||
#ifndef ACPI_HOTKEY_COMPONENT
|
||||
|
@ -200,30 +201,29 @@ static struct acpi_driver acpi_pcc_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
#define KEYMAP_SIZE 11
|
||||
static const unsigned int initial_keymap[KEYMAP_SIZE] = {
|
||||
/* 0 */ KEY_RESERVED,
|
||||
/* 1 */ KEY_BRIGHTNESSDOWN,
|
||||
/* 2 */ KEY_BRIGHTNESSUP,
|
||||
/* 3 */ KEY_DISPLAYTOGGLE,
|
||||
/* 4 */ KEY_MUTE,
|
||||
/* 5 */ KEY_VOLUMEDOWN,
|
||||
/* 6 */ KEY_VOLUMEUP,
|
||||
/* 7 */ KEY_SLEEP,
|
||||
/* 8 */ KEY_PROG1, /* Change CPU boost */
|
||||
/* 9 */ KEY_BATTERY,
|
||||
/* 10 */ KEY_SUSPEND,
|
||||
static const struct key_entry panasonic_keymap[] = {
|
||||
{ KE_KEY, 0, { KEY_RESERVED } },
|
||||
{ KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_KEY, 2, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 3, { KEY_DISPLAYTOGGLE } },
|
||||
{ KE_KEY, 4, { KEY_MUTE } },
|
||||
{ KE_KEY, 5, { KEY_VOLUMEDOWN } },
|
||||
{ KE_KEY, 6, { KEY_VOLUMEUP } },
|
||||
{ KE_KEY, 7, { KEY_SLEEP } },
|
||||
{ KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
|
||||
{ KE_KEY, 9, { KEY_BATTERY } },
|
||||
{ KE_KEY, 10, { KEY_SUSPEND } },
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
struct pcc_acpi {
|
||||
acpi_handle handle;
|
||||
unsigned long num_sifr;
|
||||
int sticky_mode;
|
||||
u32 *sinf;
|
||||
u32 *sinf;
|
||||
struct acpi_device *device;
|
||||
struct input_dev *input_dev;
|
||||
struct backlight_device *backlight;
|
||||
unsigned int keymap[KEYMAP_SIZE];
|
||||
};
|
||||
|
||||
struct pcc_keyinput {
|
||||
|
@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device)
|
|||
}
|
||||
}
|
||||
|
||||
static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
|
||||
static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
|
@ -285,6 +285,7 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
|
|||
hkey = buffer.pointer;
|
||||
if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
|
||||
status = AE_ERROR;
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -298,12 +299,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
|
|||
for (i = 0; i < hkey->package.count; i++) {
|
||||
union acpi_object *element = &(hkey->package.elements[i]);
|
||||
if (likely(element->type == ACPI_TYPE_INTEGER)) {
|
||||
sinf[i] = element->integer.value;
|
||||
pcc->sinf[i] = element->integer.value;
|
||||
} else
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||
"Invalid HKEY.SINF data\n"));
|
||||
}
|
||||
sinf[hkey->package.count] = -1;
|
||||
pcc->sinf[hkey->package.count] = -1;
|
||||
|
||||
end:
|
||||
kfree(buffer.pointer);
|
||||
|
@ -321,7 +322,7 @@ static int bl_get(struct backlight_device *bd)
|
|||
{
|
||||
struct pcc_acpi *pcc = bl_get_data(bd);
|
||||
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return pcc->sinf[SINF_AC_CUR_BRIGHT];
|
||||
|
@ -333,7 +334,7 @@ static int bl_set_status(struct backlight_device *bd)
|
|||
int bright = bd->props.brightness;
|
||||
int rc;
|
||||
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
|
||||
|
@ -367,7 +368,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
|
|||
struct acpi_device *acpi = to_acpi_device(dev);
|
||||
struct pcc_acpi *pcc = acpi_driver_data(acpi);
|
||||
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
|
||||
|
@ -379,7 +380,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
|
|||
struct acpi_device *acpi = to_acpi_device(dev);
|
||||
struct pcc_acpi *pcc = acpi_driver_data(acpi);
|
||||
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
|
||||
|
@ -391,7 +392,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
|
|||
struct acpi_device *acpi = to_acpi_device(dev);
|
||||
struct pcc_acpi *pcc = acpi_driver_data(acpi);
|
||||
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
|
||||
|
@ -403,7 +404,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
|
|||
struct acpi_device *acpi = to_acpi_device(dev);
|
||||
struct pcc_acpi *pcc = acpi_driver_data(acpi);
|
||||
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc))
|
||||
return -EIO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
|
||||
|
@ -446,56 +447,10 @@ static struct attribute_group pcc_attr_group = {
|
|||
|
||||
/* hotkey input device driver */
|
||||
|
||||
static int pcc_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
{
|
||||
struct pcc_acpi *pcc = input_get_drvdata(dev);
|
||||
|
||||
if (scancode >= ARRAY_SIZE(pcc->keymap))
|
||||
return -EINVAL;
|
||||
|
||||
*keycode = pcc->keymap[scancode];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
|
||||
if (pcc->keymap[i] == keycode)
|
||||
return i+1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcc_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
{
|
||||
struct pcc_acpi *pcc = input_get_drvdata(dev);
|
||||
int oldkeycode;
|
||||
|
||||
if (scancode >= ARRAY_SIZE(pcc->keymap))
|
||||
return -EINVAL;
|
||||
|
||||
oldkeycode = pcc->keymap[scancode];
|
||||
pcc->keymap[scancode] = keycode;
|
||||
|
||||
set_bit(keycode, dev->keybit);
|
||||
|
||||
if (!keymap_get_by_keycode(pcc, oldkeycode))
|
||||
clear_bit(oldkeycode, dev->keybit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
|
||||
{
|
||||
struct input_dev *hotk_input_dev = pcc->input_dev;
|
||||
int rc;
|
||||
int key_code, hkey_num;
|
||||
unsigned long long result;
|
||||
|
||||
rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
|
||||
|
@ -508,25 +463,10 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
|
|||
|
||||
acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
|
||||
|
||||
hkey_num = result & 0xf;
|
||||
|
||||
if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) {
|
||||
if (!sparse_keymap_report_event(hotk_input_dev,
|
||||
result & 0xf, result & 0x80, false))
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||
"hotkey number out of range: %d\n",
|
||||
hkey_num));
|
||||
return;
|
||||
}
|
||||
|
||||
key_code = pcc->keymap[hkey_num];
|
||||
|
||||
if (key_code != KEY_RESERVED) {
|
||||
int pushed = (result & 0x80) ? TRUE : FALSE;
|
||||
|
||||
input_report_key(hotk_input_dev, key_code, pushed);
|
||||
input_sync(hotk_input_dev);
|
||||
}
|
||||
|
||||
return;
|
||||
"Unknown hotkey event: %d\n", result));
|
||||
}
|
||||
|
||||
static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
|
||||
|
@ -545,40 +485,55 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
|
|||
|
||||
static int acpi_pcc_init_input(struct pcc_acpi *pcc)
|
||||
{
|
||||
int i, rc;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
pcc->input_dev = input_allocate_device();
|
||||
if (!pcc->input_dev) {
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||
"Couldn't allocate input device for hotkey"));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pcc->input_dev->evbit[0] = BIT(EV_KEY);
|
||||
input_dev->name = ACPI_PCC_DRIVER_NAME;
|
||||
input_dev->phys = ACPI_PCC_INPUT_PHYS;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
|
||||
pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
|
||||
pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
|
||||
pcc->input_dev->id.bustype = BUS_HOST;
|
||||
pcc->input_dev->id.vendor = 0x0001;
|
||||
pcc->input_dev->id.product = 0x0001;
|
||||
pcc->input_dev->id.version = 0x0100;
|
||||
pcc->input_dev->getkeycode = pcc_getkeycode;
|
||||
pcc->input_dev->setkeycode = pcc_setkeycode;
|
||||
error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
|
||||
if (error) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||
"Unable to setup input device keymap\n"));
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
/* load initial keymap */
|
||||
memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||
"Unable to register input device\n"));
|
||||
goto err_free_keymap;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
|
||||
__set_bit(pcc->keymap[i], pcc->input_dev->keybit);
|
||||
__clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
|
||||
pcc->input_dev = input_dev;
|
||||
return 0;
|
||||
|
||||
input_set_drvdata(pcc->input_dev, pcc);
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(input_dev);
|
||||
err_free_dev:
|
||||
input_free_device(input_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
rc = input_register_device(pcc->input_dev);
|
||||
if (rc < 0)
|
||||
input_free_device(pcc->input_dev);
|
||||
|
||||
return rc;
|
||||
static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
|
||||
{
|
||||
sparse_keymap_free(pcc->input_dev);
|
||||
input_unregister_device(pcc->input_dev);
|
||||
/*
|
||||
* No need to input_free_device() since core input API refcounts
|
||||
* and free()s the device.
|
||||
*/
|
||||
}
|
||||
|
||||
/* kernel module interface */
|
||||
|
@ -636,12 +591,13 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
|
|||
if (result) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||
"Error installing keyinput handler\n"));
|
||||
goto out_hotkey;
|
||||
goto out_sinf;
|
||||
}
|
||||
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
|
||||
if (!acpi_pcc_retrieve_biosdata(pcc)) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||
"Couldn't retrieve BIOS data\n"));
|
||||
result = -EIO;
|
||||
goto out_input;
|
||||
}
|
||||
/* initialize backlight */
|
||||
|
@ -651,7 +607,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
|
|||
&pcc_backlight_ops, &props);
|
||||
if (IS_ERR(pcc->backlight)) {
|
||||
result = PTR_ERR(pcc->backlight);
|
||||
goto out_sinf;
|
||||
goto out_input;
|
||||
}
|
||||
|
||||
/* read the initial brightness setting from the hardware */
|
||||
|
@ -669,12 +625,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
|
|||
|
||||
out_backlight:
|
||||
backlight_device_unregister(pcc->backlight);
|
||||
out_input:
|
||||
acpi_pcc_destroy_input(pcc);
|
||||
out_sinf:
|
||||
kfree(pcc->sinf);
|
||||
out_input:
|
||||
input_unregister_device(pcc->input_dev);
|
||||
/* no need to input_free_device() since core input API refcount and
|
||||
* free()s the device */
|
||||
out_hotkey:
|
||||
kfree(pcc);
|
||||
|
||||
|
@ -709,9 +663,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
|
|||
|
||||
backlight_device_unregister(pcc->backlight);
|
||||
|
||||
input_unregister_device(pcc->input_dev);
|
||||
/* no need to input_free_device() since core input API refcount and
|
||||
* free()s the device */
|
||||
acpi_pcc_destroy_input(pcc);
|
||||
|
||||
kfree(pcc->sinf);
|
||||
kfree(pcc);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
|
||||
#define ACPI_TOPSTAR_CLASS "topstar"
|
||||
|
||||
|
@ -26,52 +27,37 @@ struct topstar_hkey {
|
|||
struct input_dev *inputdev;
|
||||
};
|
||||
|
||||
struct tps_key_entry {
|
||||
u8 code;
|
||||
u16 keycode;
|
||||
static const struct key_entry topstar_keymap[] = {
|
||||
{ KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_KEY, 0x83, { KEY_VOLUMEUP } },
|
||||
{ KE_KEY, 0x84, { KEY_VOLUMEDOWN } },
|
||||
{ KE_KEY, 0x85, { KEY_MUTE } },
|
||||
{ KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */
|
||||
{ KE_KEY, 0x88, { KEY_WLAN } },
|
||||
{ KE_KEY, 0x8a, { KEY_WWW } },
|
||||
{ KE_KEY, 0x8b, { KEY_MAIL } },
|
||||
{ KE_KEY, 0x8c, { KEY_MEDIA } },
|
||||
|
||||
/* Known non hotkey events don't handled or that we don't care yet */
|
||||
{ KE_IGNORE, 0x8e, },
|
||||
{ KE_IGNORE, 0x8f, },
|
||||
{ KE_IGNORE, 0x90, },
|
||||
|
||||
/*
|
||||
* 'G key' generate two event codes, convert to only
|
||||
* one event/key code for now, consider replacing by
|
||||
* a switch (3G switch - SW_3G?)
|
||||
*/
|
||||
{ KE_KEY, 0x96, { KEY_F14 } },
|
||||
{ KE_KEY, 0x97, { KEY_F14 } },
|
||||
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static struct tps_key_entry topstar_keymap[] = {
|
||||
{ 0x80, KEY_BRIGHTNESSUP },
|
||||
{ 0x81, KEY_BRIGHTNESSDOWN },
|
||||
{ 0x83, KEY_VOLUMEUP },
|
||||
{ 0x84, KEY_VOLUMEDOWN },
|
||||
{ 0x85, KEY_MUTE },
|
||||
{ 0x86, KEY_SWITCHVIDEOMODE },
|
||||
{ 0x87, KEY_F13 }, /* touchpad enable/disable key */
|
||||
{ 0x88, KEY_WLAN },
|
||||
{ 0x8a, KEY_WWW },
|
||||
{ 0x8b, KEY_MAIL },
|
||||
{ 0x8c, KEY_MEDIA },
|
||||
{ 0x96, KEY_F14 }, /* G key? */
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code)
|
||||
{
|
||||
struct tps_key_entry *key;
|
||||
|
||||
for (key = topstar_keymap; key->code; key++)
|
||||
if (code == key->code)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code)
|
||||
{
|
||||
struct tps_key_entry *key;
|
||||
|
||||
for (key = topstar_keymap; key->code; key++)
|
||||
if (code == key->keycode)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void acpi_topstar_notify(struct acpi_device *device, u32 event)
|
||||
{
|
||||
struct tps_key_entry *key;
|
||||
static bool dup_evnt[2];
|
||||
bool *dup;
|
||||
struct topstar_hkey *hkey = acpi_driver_data(device);
|
||||
|
@ -86,27 +72,8 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event)
|
|||
*dup = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 'G key' generate two event codes, convert to only
|
||||
* one event/key code for now (3G switch?)
|
||||
*/
|
||||
if (event == 0x97)
|
||||
event = 0x96;
|
||||
|
||||
key = tps_get_key_by_scancode(event);
|
||||
if (key) {
|
||||
input_report_key(hkey->inputdev, key->keycode, 1);
|
||||
input_sync(hkey->inputdev);
|
||||
input_report_key(hkey->inputdev, key->keycode, 0);
|
||||
input_sync(hkey->inputdev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Known non hotkey events don't handled or that we don't care yet */
|
||||
if (event == 0x8e || event == 0x8f || event == 0x90)
|
||||
return;
|
||||
|
||||
pr_info("unknown event = 0x%02x\n", event);
|
||||
if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
|
||||
pr_info("unknown event = 0x%02x\n", event);
|
||||
}
|
||||
|
||||
static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
|
||||
|
@ -127,62 +94,41 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int topstar_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
{
|
||||
struct tps_key_entry *key = tps_get_key_by_scancode(scancode);
|
||||
|
||||
if (!key)
|
||||
return -EINVAL;
|
||||
|
||||
*keycode = key->keycode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int topstar_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
{
|
||||
struct tps_key_entry *key;
|
||||
int old_keycode;
|
||||
|
||||
key = tps_get_key_by_scancode(scancode);
|
||||
|
||||
if (!key)
|
||||
return -EINVAL;
|
||||
|
||||
old_keycode = key->keycode;
|
||||
key->keycode = keycode;
|
||||
set_bit(keycode, dev->keybit);
|
||||
if (!tps_get_key_by_keycode(old_keycode))
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
|
||||
{
|
||||
struct tps_key_entry *key;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
hkey->inputdev = input_allocate_device();
|
||||
if (!hkey->inputdev) {
|
||||
input = input_allocate_device();
|
||||
if (!input) {
|
||||
pr_err("Unable to allocate input device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
hkey->inputdev->name = "Topstar Laptop extra buttons";
|
||||
hkey->inputdev->phys = "topstar/input0";
|
||||
hkey->inputdev->id.bustype = BUS_HOST;
|
||||
hkey->inputdev->getkeycode = topstar_getkeycode;
|
||||
hkey->inputdev->setkeycode = topstar_setkeycode;
|
||||
for (key = topstar_keymap; key->code; key++) {
|
||||
set_bit(EV_KEY, hkey->inputdev->evbit);
|
||||
set_bit(key->keycode, hkey->inputdev->keybit);
|
||||
}
|
||||
if (input_register_device(hkey->inputdev)) {
|
||||
pr_err("Unable to register input device\n");
|
||||
input_free_device(hkey->inputdev);
|
||||
return -ENODEV;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input->name = "Topstar Laptop extra buttons";
|
||||
input->phys = "topstar/input0";
|
||||
input->id.bustype = BUS_HOST;
|
||||
|
||||
error = sparse_keymap_setup(input, topstar_keymap, NULL);
|
||||
if (error) {
|
||||
pr_err("Unable to setup input device keymap\n");
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
pr_err("Unable to register input device\n");
|
||||
goto err_free_keymap;
|
||||
}
|
||||
|
||||
hkey->inputdev = input;
|
||||
return 0;
|
||||
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(input);
|
||||
err_free_dev:
|
||||
input_free_device(input);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int acpi_topstar_add(struct acpi_device *device)
|
||||
|
@ -216,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device, int type)
|
|||
|
||||
acpi_topstar_fncx_switch(device, false);
|
||||
|
||||
sparse_keymap_free(tps_hkey->inputdev);
|
||||
input_unregister_device(tps_hkey->inputdev);
|
||||
kfree(tps_hkey);
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -121,36 +122,28 @@ static const struct acpi_device_id toshiba_device_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
|
||||
|
||||
struct key_entry {
|
||||
char type;
|
||||
u16 code;
|
||||
u16 keycode;
|
||||
};
|
||||
|
||||
enum {KE_KEY, KE_END};
|
||||
|
||||
static struct key_entry toshiba_acpi_keymap[] = {
|
||||
{KE_KEY, 0x101, KEY_MUTE},
|
||||
{KE_KEY, 0x102, KEY_ZOOMOUT},
|
||||
{KE_KEY, 0x103, KEY_ZOOMIN},
|
||||
{KE_KEY, 0x13b, KEY_COFFEE},
|
||||
{KE_KEY, 0x13c, KEY_BATTERY},
|
||||
{KE_KEY, 0x13d, KEY_SLEEP},
|
||||
{KE_KEY, 0x13e, KEY_SUSPEND},
|
||||
{KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
|
||||
{KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
|
||||
{KE_KEY, 0x141, KEY_BRIGHTNESSUP},
|
||||
{KE_KEY, 0x142, KEY_WLAN},
|
||||
{KE_KEY, 0x143, KEY_PROG1},
|
||||
{KE_KEY, 0xb05, KEY_PROG2},
|
||||
{KE_KEY, 0xb06, KEY_WWW},
|
||||
{KE_KEY, 0xb07, KEY_MAIL},
|
||||
{KE_KEY, 0xb30, KEY_STOP},
|
||||
{KE_KEY, 0xb31, KEY_PREVIOUSSONG},
|
||||
{KE_KEY, 0xb32, KEY_NEXTSONG},
|
||||
{KE_KEY, 0xb33, KEY_PLAYPAUSE},
|
||||
{KE_KEY, 0xb5a, KEY_MEDIA},
|
||||
{KE_END, 0, 0},
|
||||
static const struct key_entry toshiba_acpi_keymap[] __initconst = {
|
||||
{ KE_KEY, 0x101, { KEY_MUTE } },
|
||||
{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
|
||||
{ KE_KEY, 0x103, { KEY_ZOOMIN } },
|
||||
{ KE_KEY, 0x13b, { KEY_COFFEE } },
|
||||
{ KE_KEY, 0x13c, { KEY_BATTERY } },
|
||||
{ KE_KEY, 0x13d, { KEY_SLEEP } },
|
||||
{ KE_KEY, 0x13e, { KEY_SUSPEND } },
|
||||
{ KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 0x142, { KEY_WLAN } },
|
||||
{ KE_KEY, 0x143, { KEY_PROG1 } },
|
||||
{ KE_KEY, 0xb05, { KEY_PROG2 } },
|
||||
{ KE_KEY, 0xb06, { KEY_WWW } },
|
||||
{ KE_KEY, 0xb07, { KEY_MAIL } },
|
||||
{ KE_KEY, 0xb30, { KEY_STOP } },
|
||||
{ KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
|
||||
{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
|
||||
{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
|
||||
{ KE_KEY, 0xb5a, { KEY_MEDIA } },
|
||||
{ KE_END, 0 },
|
||||
};
|
||||
|
||||
/* utility
|
||||
|
@ -852,64 +845,9 @@ static struct backlight_ops toshiba_backlight_data = {
|
|||
.update_status = set_lcd_status,
|
||||
};
|
||||
|
||||
static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code)
|
||||
{
|
||||
struct key_entry *key;
|
||||
|
||||
for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
|
||||
if (code == key->code)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code)
|
||||
{
|
||||
struct key_entry *key;
|
||||
|
||||
for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
|
||||
if (code == key->keycode && key->type == KE_KEY)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int toshiba_acpi_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
{
|
||||
struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
|
||||
|
||||
if (key && key->type == KE_KEY) {
|
||||
*keycode = key->keycode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int toshiba_acpi_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int old_keycode;
|
||||
|
||||
key = toshiba_acpi_get_entry_by_scancode(scancode);
|
||||
if (key && key->type == KE_KEY) {
|
||||
old_keycode = key->keycode;
|
||||
key->keycode = keycode;
|
||||
set_bit(keycode, dev->keybit);
|
||||
if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
|
||||
{
|
||||
u32 hci_result, value;
|
||||
struct key_entry *key;
|
||||
|
||||
if (event != 0x80)
|
||||
return;
|
||||
|
@ -922,19 +860,11 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
|
|||
if (value & 0x80)
|
||||
continue;
|
||||
|
||||
key = toshiba_acpi_get_entry_by_scancode
|
||||
(value);
|
||||
if (!key) {
|
||||
if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
|
||||
value, 1, true)) {
|
||||
printk(MY_INFO "Unknown key %x\n",
|
||||
value);
|
||||
continue;
|
||||
}
|
||||
input_report_key(toshiba_acpi.hotkey_dev,
|
||||
key->keycode, 1);
|
||||
input_sync(toshiba_acpi.hotkey_dev);
|
||||
input_report_key(toshiba_acpi.hotkey_dev,
|
||||
key->keycode, 0);
|
||||
input_sync(toshiba_acpi.hotkey_dev);
|
||||
} else if (hci_result == HCI_NOT_SUPPORTED) {
|
||||
/* This is a workaround for an unresolved issue on
|
||||
* some machines where system events sporadically
|
||||
|
@ -945,34 +875,17 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
|
|||
} while (hci_result != HCI_EMPTY);
|
||||
}
|
||||
|
||||
static int toshiba_acpi_setup_keyboard(char *device)
|
||||
static int __init toshiba_acpi_setup_keyboard(char *device)
|
||||
{
|
||||
acpi_status status;
|
||||
acpi_handle handle;
|
||||
int result;
|
||||
const struct key_entry *key;
|
||||
int error;
|
||||
|
||||
status = acpi_get_handle(NULL, device, &handle);
|
||||
status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(MY_INFO "Unable to get notification device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
toshiba_acpi.handle = handle;
|
||||
|
||||
status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(MY_INFO "Unable to enable hotkeys\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
|
||||
toshiba_acpi_notify, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(MY_INFO "Unable to install hotkey notification\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
toshiba_acpi.hotkey_dev = input_allocate_device();
|
||||
if (!toshiba_acpi.hotkey_dev) {
|
||||
printk(MY_INFO "Unable to register input device\n");
|
||||
|
@ -982,27 +895,54 @@ static int toshiba_acpi_setup_keyboard(char *device)
|
|||
toshiba_acpi.hotkey_dev->name = "Toshiba input device";
|
||||
toshiba_acpi.hotkey_dev->phys = device;
|
||||
toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
|
||||
toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
|
||||
toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
|
||||
|
||||
for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
|
||||
set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
|
||||
set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
|
||||
error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
|
||||
toshiba_acpi_keymap, NULL);
|
||||
if (error)
|
||||
goto err_free_dev;
|
||||
|
||||
status = acpi_install_notify_handler(toshiba_acpi.handle,
|
||||
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(MY_INFO "Unable to install hotkey notification\n");
|
||||
error = -ENODEV;
|
||||
goto err_free_keymap;
|
||||
}
|
||||
|
||||
result = input_register_device(toshiba_acpi.hotkey_dev);
|
||||
if (result) {
|
||||
status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(MY_INFO "Unable to enable hotkeys\n");
|
||||
error = -ENODEV;
|
||||
goto err_remove_notify;
|
||||
}
|
||||
|
||||
error = input_register_device(toshiba_acpi.hotkey_dev);
|
||||
if (error) {
|
||||
printk(MY_INFO "Unable to register input device\n");
|
||||
return result;
|
||||
goto err_remove_notify;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_notify:
|
||||
acpi_remove_notify_handler(toshiba_acpi.handle,
|
||||
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(toshiba_acpi.hotkey_dev);
|
||||
err_free_dev:
|
||||
input_free_device(toshiba_acpi.hotkey_dev);
|
||||
toshiba_acpi.hotkey_dev = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void toshiba_acpi_exit(void)
|
||||
{
|
||||
if (toshiba_acpi.hotkey_dev)
|
||||
if (toshiba_acpi.hotkey_dev) {
|
||||
acpi_remove_notify_handler(toshiba_acpi.handle,
|
||||
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
|
||||
sparse_keymap_free(toshiba_acpi.hotkey_dev);
|
||||
input_unregister_device(toshiba_acpi.hotkey_dev);
|
||||
}
|
||||
|
||||
if (toshiba_acpi.bt_rfk) {
|
||||
rfkill_unregister(toshiba_acpi.bt_rfk);
|
||||
|
@ -1017,9 +957,6 @@ static void toshiba_acpi_exit(void)
|
|||
if (toshiba_proc_dir)
|
||||
remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
|
||||
|
||||
acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
|
||||
toshiba_acpi_notify);
|
||||
|
||||
if (toshiba_acpi.illumination_installed)
|
||||
led_classdev_unregister(&toshiba_led);
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
|
@ -44,9 +46,8 @@ MODULE_LICENSE("GPL");
|
|||
|
||||
#define ACPI_WMI_CLASS "wmi"
|
||||
|
||||
#define PREFIX "ACPI: WMI: "
|
||||
|
||||
static DEFINE_MUTEX(wmi_data_lock);
|
||||
static LIST_HEAD(wmi_block_list);
|
||||
|
||||
struct guid_block {
|
||||
char guid[16];
|
||||
|
@ -67,10 +68,9 @@ struct wmi_block {
|
|||
acpi_handle handle;
|
||||
wmi_notify_handler handler;
|
||||
void *handler_data;
|
||||
struct device *dev;
|
||||
struct device dev;
|
||||
};
|
||||
|
||||
static struct wmi_block wmi_blocks;
|
||||
|
||||
/*
|
||||
* If the GUID data block is marked as expensive, we must enable and
|
||||
|
@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = {
|
|||
.add = acpi_wmi_add,
|
||||
.remove = acpi_wmi_remove,
|
||||
.notify = acpi_wmi_notify,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = {
|
|||
*/
|
||||
static int wmi_parse_hexbyte(const u8 *src)
|
||||
{
|
||||
unsigned int x; /* For correct wrapping */
|
||||
int h;
|
||||
int value;
|
||||
|
||||
/* high part */
|
||||
x = src[0];
|
||||
if (x - '0' <= '9' - '0') {
|
||||
h = x - '0';
|
||||
} else if (x - 'a' <= 'f' - 'a') {
|
||||
h = x - 'a' + 10;
|
||||
} else if (x - 'A' <= 'F' - 'A') {
|
||||
h = x - 'A' + 10;
|
||||
} else {
|
||||
h = value = hex_to_bin(src[0]);
|
||||
if (value < 0)
|
||||
return -1;
|
||||
}
|
||||
h <<= 4;
|
||||
|
||||
/* low part */
|
||||
x = src[1];
|
||||
if (x - '0' <= '9' - '0')
|
||||
return h | (x - '0');
|
||||
if (x - 'a' <= 'f' - 'a')
|
||||
return h | (x - 'a' + 10);
|
||||
if (x - 'A' <= 'F' - 'A')
|
||||
return h | (x - 'A' + 10);
|
||||
value = hex_to_bin(src[1]);
|
||||
if (value >= 0)
|
||||
return (h << 4) | value;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -232,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out)
|
|||
for (i = 10; i <= 15; i++)
|
||||
out += sprintf(out, "%02X", in[i] & 0xFF);
|
||||
|
||||
out = '\0';
|
||||
*out = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
|
|||
wmi_parse_guid(guid_string, tmp);
|
||||
wmi_swap_bytes(tmp, guid_input);
|
||||
|
||||
list_for_each(p, &wmi_blocks.list) {
|
||||
list_for_each(p, &wmi_block_list) {
|
||||
wblock = list_entry(p, struct wmi_block, list);
|
||||
block = &wblock->gblock;
|
||||
|
||||
|
@ -487,30 +475,29 @@ const struct acpi_buffer *in)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(wmi_set_block);
|
||||
|
||||
static void wmi_dump_wdg(struct guid_block *g)
|
||||
static void wmi_dump_wdg(const struct guid_block *g)
|
||||
{
|
||||
char guid_string[37];
|
||||
|
||||
wmi_gtoa(g->guid, guid_string);
|
||||
printk(KERN_INFO PREFIX "%s:\n", guid_string);
|
||||
printk(KERN_INFO PREFIX "\tobject_id: %c%c\n",
|
||||
g->object_id[0], g->object_id[1]);
|
||||
printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id);
|
||||
printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved);
|
||||
printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count);
|
||||
printk(KERN_INFO PREFIX "\tflags: %#x", g->flags);
|
||||
|
||||
pr_info("%s:\n", guid_string);
|
||||
pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
|
||||
pr_info("\tnotify_id: %02X\n", g->notify_id);
|
||||
pr_info("\treserved: %02X\n", g->reserved);
|
||||
pr_info("\tinstance_count: %d\n", g->instance_count);
|
||||
pr_info("\tflags: %#x ", g->flags);
|
||||
if (g->flags) {
|
||||
printk(" ");
|
||||
if (g->flags & ACPI_WMI_EXPENSIVE)
|
||||
printk("ACPI_WMI_EXPENSIVE ");
|
||||
pr_cont("ACPI_WMI_EXPENSIVE ");
|
||||
if (g->flags & ACPI_WMI_METHOD)
|
||||
printk("ACPI_WMI_METHOD ");
|
||||
pr_cont("ACPI_WMI_METHOD ");
|
||||
if (g->flags & ACPI_WMI_STRING)
|
||||
printk("ACPI_WMI_STRING ");
|
||||
pr_cont("ACPI_WMI_STRING ");
|
||||
if (g->flags & ACPI_WMI_EVENT)
|
||||
printk("ACPI_WMI_EVENT ");
|
||||
pr_cont("ACPI_WMI_EVENT ");
|
||||
}
|
||||
printk("\n");
|
||||
pr_cont("\n");
|
||||
|
||||
}
|
||||
|
||||
|
@ -522,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context)
|
|||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
printk(KERN_INFO "wmi: bad event status 0x%x\n", status);
|
||||
pr_info("bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -531,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context)
|
|||
if (!obj)
|
||||
return;
|
||||
|
||||
printk(KERN_INFO PREFIX "DEBUG Event ");
|
||||
pr_info("DEBUG Event ");
|
||||
switch(obj->type) {
|
||||
case ACPI_TYPE_BUFFER:
|
||||
printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
|
||||
pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
|
||||
break;
|
||||
case ACPI_TYPE_STRING:
|
||||
printk("STRING_TYPE - %s\n", obj->string.pointer);
|
||||
pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
|
||||
break;
|
||||
case ACPI_TYPE_INTEGER:
|
||||
printk("INTEGER_TYPE - %llu\n", obj->integer.value);
|
||||
pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
|
||||
break;
|
||||
case ACPI_TYPE_PACKAGE:
|
||||
printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
|
||||
pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
|
||||
break;
|
||||
default:
|
||||
printk("object type 0x%X\n", obj->type);
|
||||
pr_cont("object type 0x%X\n", obj->type);
|
||||
}
|
||||
kfree(obj);
|
||||
}
|
||||
|
@ -633,7 +620,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
|
|||
params[0].type = ACPI_TYPE_INTEGER;
|
||||
params[0].integer.value = event;
|
||||
|
||||
list_for_each(p, &wmi_blocks.list) {
|
||||
list_for_each(p, &wmi_block_list) {
|
||||
wblock = list_entry(p, struct wmi_block, list);
|
||||
gblock = &wblock->gblock;
|
||||
|
||||
|
@ -662,7 +649,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid);
|
|||
/*
|
||||
* sysfs interface
|
||||
*/
|
||||
static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
char guid_string[37];
|
||||
|
@ -676,7 +663,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return sprintf(buf, "wmi:%s\n", guid_string);
|
||||
}
|
||||
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
||||
|
||||
static struct device_attribute wmi_dev_attrs[] = {
|
||||
__ATTR_RO(modalias),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
|
@ -702,108 +693,71 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
|
||||
static void wmi_dev_free(struct device *dev)
|
||||
{
|
||||
kfree(dev);
|
||||
struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev);
|
||||
|
||||
kfree(wmi_block);
|
||||
}
|
||||
|
||||
static struct class wmi_class = {
|
||||
.name = "wmi",
|
||||
.dev_release = wmi_dev_free,
|
||||
.dev_uevent = wmi_dev_uevent,
|
||||
.dev_attrs = wmi_dev_attrs,
|
||||
};
|
||||
|
||||
static int wmi_create_devs(void)
|
||||
static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
|
||||
acpi_handle handle)
|
||||
{
|
||||
int result;
|
||||
char guid_string[37];
|
||||
struct guid_block *gblock;
|
||||
struct wmi_block *wblock;
|
||||
struct list_head *p;
|
||||
struct device *guid_dev;
|
||||
int error;
|
||||
char guid_string[37];
|
||||
|
||||
/* Create devices for all the GUIDs */
|
||||
list_for_each(p, &wmi_blocks.list) {
|
||||
wblock = list_entry(p, struct wmi_block, list);
|
||||
|
||||
guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
|
||||
if (!guid_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
wblock->dev = guid_dev;
|
||||
|
||||
guid_dev->class = &wmi_class;
|
||||
dev_set_drvdata(guid_dev, wblock);
|
||||
|
||||
gblock = &wblock->gblock;
|
||||
|
||||
wmi_gtoa(gblock->guid, guid_string);
|
||||
dev_set_name(guid_dev, guid_string);
|
||||
|
||||
result = device_register(guid_dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = device_create_file(guid_dev, &dev_attr_modalias);
|
||||
if (result)
|
||||
return result;
|
||||
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
|
||||
if (!wblock) {
|
||||
error = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
wblock->handle = handle;
|
||||
wblock->gblock = *gblock;
|
||||
|
||||
wblock->dev.class = &wmi_class;
|
||||
|
||||
wmi_gtoa(gblock->guid, guid_string);
|
||||
dev_set_name(&wblock->dev, guid_string);
|
||||
|
||||
dev_set_drvdata(&wblock->dev, wblock);
|
||||
|
||||
error = device_register(&wblock->dev);
|
||||
if (error)
|
||||
goto err_free;
|
||||
|
||||
list_add_tail(&wblock->list, &wmi_block_list);
|
||||
return wblock;
|
||||
|
||||
err_free:
|
||||
kfree(wblock);
|
||||
err_out:
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
static void wmi_remove_devs(void)
|
||||
static void wmi_free_devices(void)
|
||||
{
|
||||
struct guid_block *gblock;
|
||||
struct wmi_block *wblock;
|
||||
struct list_head *p;
|
||||
struct device *guid_dev;
|
||||
struct wmi_block *wblock, *next;
|
||||
|
||||
/* Delete devices for all the GUIDs */
|
||||
list_for_each(p, &wmi_blocks.list) {
|
||||
wblock = list_entry(p, struct wmi_block, list);
|
||||
|
||||
guid_dev = wblock->dev;
|
||||
gblock = &wblock->gblock;
|
||||
|
||||
device_remove_file(guid_dev, &dev_attr_modalias);
|
||||
|
||||
device_unregister(guid_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void wmi_class_exit(void)
|
||||
{
|
||||
wmi_remove_devs();
|
||||
class_unregister(&wmi_class);
|
||||
}
|
||||
|
||||
static int wmi_class_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = class_register(&wmi_class);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = wmi_create_devs();
|
||||
if (ret)
|
||||
wmi_class_exit();
|
||||
|
||||
return ret;
|
||||
list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
|
||||
device_unregister(&wblock->dev);
|
||||
}
|
||||
|
||||
static bool guid_already_parsed(const char *guid_string)
|
||||
{
|
||||
struct guid_block *gblock;
|
||||
struct wmi_block *wblock;
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &wmi_blocks.list) {
|
||||
wblock = list_entry(p, struct wmi_block, list);
|
||||
gblock = &wblock->gblock;
|
||||
|
||||
if (strncmp(gblock->guid, guid_string, 16) == 0)
|
||||
list_for_each_entry(wblock, &wmi_block_list, list)
|
||||
if (strncmp(wblock->gblock.guid, guid_string, 16) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -814,30 +768,29 @@ static acpi_status parse_wdg(acpi_handle handle)
|
|||
{
|
||||
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
union acpi_object *obj;
|
||||
struct guid_block *gblock;
|
||||
const struct guid_block *gblock;
|
||||
struct wmi_block *wblock;
|
||||
char guid_string[37];
|
||||
acpi_status status;
|
||||
int retval;
|
||||
u32 i, total;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
return -ENXIO;
|
||||
|
||||
obj = (union acpi_object *) out.pointer;
|
||||
if (!obj)
|
||||
return -ENXIO;
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER)
|
||||
return AE_ERROR;
|
||||
|
||||
total = obj->buffer.length / sizeof(struct guid_block);
|
||||
|
||||
gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
|
||||
if (!gblock) {
|
||||
status = AE_NO_MEMORY;
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
retval = -ENXIO;
|
||||
goto out_free_pointer;
|
||||
}
|
||||
|
||||
gblock = (const struct guid_block *)obj->buffer.pointer;
|
||||
total = obj->buffer.length / sizeof(struct guid_block);
|
||||
|
||||
for (i = 0; i < total; i++) {
|
||||
/*
|
||||
Some WMI devices, like those for nVidia hooks, have a
|
||||
|
@ -848,34 +801,32 @@ static acpi_status parse_wdg(acpi_handle handle)
|
|||
*/
|
||||
if (guid_already_parsed(gblock[i].guid) == true) {
|
||||
wmi_gtoa(gblock[i].guid, guid_string);
|
||||
printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
|
||||
guid_string);
|
||||
pr_info("Skipping duplicate GUID %s\n", guid_string);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (debug_dump_wdg)
|
||||
wmi_dump_wdg(&gblock[i]);
|
||||
|
||||
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
|
||||
if (!wblock) {
|
||||
status = AE_NO_MEMORY;
|
||||
goto out_free_gblock;
|
||||
wblock = wmi_create_device(&gblock[i], handle);
|
||||
if (IS_ERR(wblock)) {
|
||||
retval = PTR_ERR(wblock);
|
||||
wmi_free_devices();
|
||||
break;
|
||||
}
|
||||
|
||||
wblock->gblock = gblock[i];
|
||||
wblock->handle = handle;
|
||||
if (debug_event) {
|
||||
wblock->handler = wmi_notify_debug;
|
||||
status = wmi_method_enable(wblock, 1);
|
||||
wmi_method_enable(wblock, 1);
|
||||
}
|
||||
list_add_tail(&wblock->list, &wmi_blocks.list);
|
||||
}
|
||||
|
||||
out_free_gblock:
|
||||
kfree(gblock);
|
||||
retval = 0;
|
||||
|
||||
out_free_pointer:
|
||||
kfree(out.pointer);
|
||||
|
||||
return status;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -929,7 +880,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
|
|||
struct list_head *p;
|
||||
char guid_string[37];
|
||||
|
||||
list_for_each(p, &wmi_blocks.list) {
|
||||
list_for_each(p, &wmi_block_list) {
|
||||
wblock = list_entry(p, struct wmi_block, list);
|
||||
block = &wblock->gblock;
|
||||
|
||||
|
@ -939,8 +890,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
|
|||
wblock->handler(event, wblock->handler_data);
|
||||
if (debug_event) {
|
||||
wmi_gtoa(wblock->gblock.guid, guid_string);
|
||||
printk(KERN_INFO PREFIX "DEBUG Event GUID:"
|
||||
" %s\n", guid_string);
|
||||
pr_info("DEBUG Event GUID: %s\n", guid_string);
|
||||
}
|
||||
|
||||
acpi_bus_generate_netlink_event(
|
||||
|
@ -955,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
|
|||
{
|
||||
acpi_remove_address_space_handler(device->handle,
|
||||
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
|
||||
wmi_free_devices();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -962,68 +913,57 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
|
|||
static int acpi_wmi_add(struct acpi_device *device)
|
||||
{
|
||||
acpi_status status;
|
||||
int result = 0;
|
||||
int error;
|
||||
|
||||
status = acpi_install_address_space_handler(device->handle,
|
||||
ACPI_ADR_SPACE_EC,
|
||||
&acpi_wmi_ec_space_handler,
|
||||
NULL, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
status = parse_wdg(device->handle);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_ERR PREFIX "Error installing EC region handler\n");
|
||||
pr_err("Error installing EC region handler\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return result;
|
||||
error = parse_wdg(device->handle);
|
||||
if (error) {
|
||||
acpi_remove_address_space_handler(device->handle,
|
||||
ACPI_ADR_SPACE_EC,
|
||||
&acpi_wmi_ec_space_handler);
|
||||
pr_err("Failed to parse WDG method\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init acpi_wmi_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
INIT_LIST_HEAD(&wmi_blocks.list);
|
||||
int error;
|
||||
|
||||
if (acpi_disabled)
|
||||
return -ENODEV;
|
||||
|
||||
result = acpi_bus_register_driver(&acpi_wmi_driver);
|
||||
error = class_register(&wmi_class);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (result < 0) {
|
||||
printk(KERN_INFO PREFIX "Error loading mapper\n");
|
||||
return -ENODEV;
|
||||
error = acpi_bus_register_driver(&acpi_wmi_driver);
|
||||
if (error) {
|
||||
pr_err("Error loading mapper\n");
|
||||
class_unregister(&wmi_class);
|
||||
return error;
|
||||
}
|
||||
|
||||
result = wmi_class_init();
|
||||
if (result) {
|
||||
acpi_bus_unregister_driver(&acpi_wmi_driver);
|
||||
return result;
|
||||
}
|
||||
|
||||
printk(KERN_INFO PREFIX "Mapper loaded\n");
|
||||
|
||||
return result;
|
||||
pr_info("Mapper loaded\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit acpi_wmi_exit(void)
|
||||
{
|
||||
struct list_head *p, *tmp;
|
||||
struct wmi_block *wblock;
|
||||
|
||||
wmi_class_exit();
|
||||
|
||||
acpi_bus_unregister_driver(&acpi_wmi_driver);
|
||||
class_unregister(&wmi_class);
|
||||
|
||||
list_for_each_safe(p, tmp, &wmi_blocks.list) {
|
||||
wblock = list_entry(p, struct wmi_block, list);
|
||||
|
||||
list_del(p);
|
||||
kfree(wblock);
|
||||
}
|
||||
|
||||
printk(KERN_INFO PREFIX "Mapper unloaded\n");
|
||||
pr_info("Mapper unloaded\n");
|
||||
}
|
||||
|
||||
subsys_initcall(acpi_wmi_init);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Support for rfkill through the OLPC XO-1 laptop embedded controller
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rfkill.h>
|
||||
|
||||
#include <asm/olpc.h>
|
||||
|
||||
static int rfkill_set_block(void *data, bool blocked)
|
||||
{
|
||||
unsigned char cmd;
|
||||
if (blocked)
|
||||
cmd = EC_WLAN_ENTER_RESET;
|
||||
else
|
||||
cmd = EC_WLAN_LEAVE_RESET;
|
||||
|
||||
return olpc_ec_cmd(cmd, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static const struct rfkill_ops rfkill_ops = {
|
||||
.set_block = rfkill_set_block,
|
||||
};
|
||||
|
||||
static int __devinit xo1_rfkill_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rfkill *rfk;
|
||||
int r;
|
||||
|
||||
rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN,
|
||||
&rfkill_ops, NULL);
|
||||
if (!rfk)
|
||||
return -ENOMEM;
|
||||
|
||||
r = rfkill_register(rfk);
|
||||
if (r) {
|
||||
rfkill_destroy(rfk);
|
||||
return r;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rfk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit xo1_rfkill_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rfkill *rfk = platform_get_drvdata(pdev);
|
||||
rfkill_unregister(rfk);
|
||||
rfkill_destroy(rfk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver xo1_rfkill_driver = {
|
||||
.driver = {
|
||||
.name = "xo1-rfkill",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = xo1_rfkill_probe,
|
||||
.remove = __devexit_p(xo1_rfkill_remove),
|
||||
};
|
||||
|
||||
static int __init xo1_rfkill_init(void)
|
||||
{
|
||||
return platform_driver_register(&xo1_rfkill_driver);
|
||||
}
|
||||
|
||||
static void __exit xo1_rfkill_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&xo1_rfkill_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:xo1-rfkill");
|
||||
|
||||
module_init(xo1_rfkill_init);
|
||||
module_exit(xo1_rfkill_exit);
|
Loading…
Reference in New Issue