applied to acpi-test so it can get into linux-next I'll wait for an Ack from Jonathan before pushing upstream, however. thanks, -- Len Brown, Intel Open Source Technology Center On Wed, 31 Dec 2008, Tony Vroon wrote: > The FUNC interface in the Fujitsu-Siemens DSDT was unused until now. It exposes > state information that is now reported in additional platform files (whether the > radios are killed by the hardware switch or operational, whether the machine is > docked and whether the lid is open). > Support for the backlight class is now extended with the ability to power the > backlight on & off. Optional support for the LED class allows the keyboard > headlamps found on the U810 netbook and the Fujitsu logo illumination on the > P8010 notebook to be turned on & off. > > This was fed through checkpatch.pl and tested on the S6420, P8010 & U810 platforms. > > (Awaiting sign-off by Jonathan, which means the S7020 platform will also be tested) > > Signed-off-by: Stephen Gildea <stepheng+linux@xxxxxxxxxx> > Tested-by: Stephen Gildea <stepheng+linux@xxxxxxxxxx> > Tested-by: Julian Brown <jules@xxxxxxxxxxxxxxxxxxxxxxx> > Signed-off-by: Tony Vroon <tony@xxxxxxxx> > > --- linux-2.6/drivers/misc/fujitsu-laptop.c.orig 2008-12-29 16:21:36.000000000 +0000 > +++ linux-2.6/drivers/misc/fujitsu-laptop.c 2008-12-31 18:01:22.000000000 +0000 > @@ -3,6 +3,7 @@ > /* > Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@xxxxxxxxxxxxxxxxxxxxxxx> > Copyright (C) 2008 Peter Gruber <nokos@xxxxxxx> > + Copyright (C) 2008 Tony Vroon <tony@xxxxxxxx> > Based on earlier work: > Copyright (C) 2003 Shane Spencer <shane@xxxxxxxxxxx> > Adrian Yee <brewt-fujitsu@xxxxxxxxx> > @@ -65,8 +66,11 @@ > #include <linux/kfifo.h> > #include <linux/video_output.h> > #include <linux/platform_device.h> > +#ifdef CONFIG_LEDS_CLASS > +#include <linux/leds.h> > +#endif > > -#define FUJITSU_DRIVER_VERSION "0.4.3" > +#define FUJITSU_DRIVER_VERSION "0.5.0" > > #define FUJITSU_LCD_N_LEVELS 8 > > @@ -83,6 +87,24 @@ > #define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 > #define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 > > +/* FUNC interface - command values */ > +#define FUNC_RFKILL 0x1000 > +#define FUNC_LEDS 0x1001 > +#define FUNC_BUTTONS 0x1002 > +#define FUNC_BACKLIGHT 0x1004 > + > +/* FUNC interface - responses */ > +#define UNSUPPORTED_CMD 0x80000000 > + > +#ifdef CONFIG_LEDS_CLASS > +/* FUNC interface - LED control */ > +#define FUNC_LED_OFF 0x1 > +#define FUNC_LED_ON 0x30001 > +#define KEYBOARD_LAMPS 0x100 > +#define LOGOLAMP_POWERON 0x2000 > +#define LOGOLAMP_ALWAYS 0x4000 > +#endif > + > /* Hotkey details */ > #define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ > #define KEY2_CODE 0x411 > @@ -145,8 +167,9 @@ > struct platform_device *pf_device; > struct kfifo *fifo; > spinlock_t fifo_lock; > - > - unsigned int irb; /* info about the pressed buttons */ > + int rfkill_state; > + int logolamp_registered; > + int kblamps_registered; > }; > > static struct fujitsu_hotkey_t *fujitsu_hotkey; > @@ -154,12 +177,139 @@ > static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, > void *data); > > +#ifdef CONFIG_LEDS_CLASS > +static enum led_brightness logolamp_get(struct led_classdev *cdev); > +static void logolamp_set(struct led_classdev *cdev, > + enum led_brightness brightness); > + > +struct led_classdev logolamp_led = { > + .name = "fujitsu::logolamp", > + .brightness_get = logolamp_get, > + .brightness_set = logolamp_set > +}; > + > +static enum led_brightness kblamps_get(struct led_classdev *cdev); > +static void kblamps_set(struct led_classdev *cdev, > + enum led_brightness brightness); > + > +struct led_classdev kblamps_led = { > + .name = "fujitsu::kblamps", > + .brightness_get = kblamps_get, > + .brightness_set = kblamps_set > +}; > +#endif > + > #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG > static u32 dbg_level = 0x03; > #endif > > static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data); > > +/* Fujitsu ACPI interface function */ > + > +static int call_fext_func(int cmd, int arg0, int arg1, int arg2) > +{ > + acpi_status status = AE_OK; > + union acpi_object params[4] = { > + { .type = ACPI_TYPE_INTEGER }, > + { .type = ACPI_TYPE_INTEGER }, > + { .type = ACPI_TYPE_INTEGER }, > + { .type = ACPI_TYPE_INTEGER } > + }; > + struct acpi_object_list arg_list = { 4, ¶ms[0] }; > + struct acpi_buffer output; > + union acpi_object out_obj; > + acpi_handle handle = NULL; > + > + status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle); > + if (ACPI_FAILURE(status)) { > + vdbg_printk(FUJLAPTOP_DBG_ERROR, > + "FUNC interface is not present\n"); > + return -ENODEV; > + } > + > + params[0].integer.value = cmd; > + params[1].integer.value = arg0; > + params[2].integer.value = arg1; > + params[3].integer.value = arg2; > + > + output.length = sizeof(out_obj); > + output.pointer = &out_obj; > + > + status = acpi_evaluate_object(handle, NULL, &arg_list, &output); > + if (ACPI_FAILURE(status)) { > + vdbg_printk(FUJLAPTOP_DBG_WARN, > + "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n", > + cmd, arg0, arg1, arg2); > + return -ENODEV; > + } > + > + if (out_obj.type != ACPI_TYPE_INTEGER) { > + vdbg_printk(FUJLAPTOP_DBG_WARN, > + "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not " > + "return an integer\n", > + cmd, arg0, arg1, arg2); > + return -ENODEV; > + } > + > + vdbg_printk(FUJLAPTOP_DBG_TRACE, > + "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", > + cmd, arg0, arg1, arg2, (int)out_obj.integer.value); > + return out_obj.integer.value; > +} > + > +#ifdef CONFIG_LEDS_CLASS > +/* LED class callbacks */ > + > +static void logolamp_set(struct led_classdev *cdev, > + enum led_brightness brightness) > +{ > + if (brightness >= LED_FULL) { > + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); > + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON); > + } else if (brightness >= LED_HALF) { > + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); > + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF); > + } else { > + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF); > + } > +} > + > +static void kblamps_set(struct led_classdev *cdev, > + enum led_brightness brightness) > +{ > + if (brightness >= LED_FULL) > + call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON); > + else > + call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); > +} > + > +static enum led_brightness logolamp_get(struct led_classdev *cdev) > +{ > + enum led_brightness brightness = LED_OFF; > + int poweron, always; > + > + poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); > + if (poweron == FUNC_LED_ON) { > + brightness = LED_HALF; > + always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); > + if (always == FUNC_LED_ON) > + brightness = LED_FULL; > + } > + return brightness; > +} > + > +static enum led_brightness kblamps_get(struct led_classdev *cdev) > +{ > + enum led_brightness brightness = LED_OFF; > + > + if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) > + brightness = LED_FULL; > + > + return brightness; > +} > +#endif > + > /* Hardware access for LCD brightness control */ > > static int set_lcd_level(int level) > @@ -297,10 +447,25 @@ > > static int bl_update_status(struct backlight_device *b) > { > + int ret; > + if (b->props.power == 4) > + ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3); > + else > + ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0); > + if (ret != 0) > + vdbg_printk(FUJLAPTOP_DBG_ERROR, > + "Unable to adjust backlight power, error code %i\n", > + ret); > + > if (use_alt_lcd_levels) > - return set_lcd_level_alt(b->props.brightness); > + ret = set_lcd_level_alt(b->props.brightness); > else > - return set_lcd_level(b->props.brightness); > + ret = set_lcd_level(b->props.brightness); > + if (ret != 0) > + vdbg_printk(FUJLAPTOP_DBG_ERROR, > + "Unable to adjust LCD brightness, error code %i\n", > + ret); > + return ret; > } > > static struct backlight_ops fujitsubl_ops = { > @@ -382,42 +547,64 @@ > return count; > } > > -/* Hardware access for hotkey device */ > - > -static int get_irb(void) > +static ssize_t > +ignore_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t count) > { > - unsigned long long state = 0; > - acpi_status status = AE_OK; > - > - vdbg_printk(FUJLAPTOP_DBG_TRACE, "Get irb\n"); > - > - status = > - acpi_evaluate_integer(fujitsu_hotkey->acpi_handle, "GIRB", NULL, > - &state); > - if (status < 0) > - return status; > + return count; > +} > > - fujitsu_hotkey->irb = state; > +static ssize_t > +show_lid_state(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) > + return sprintf(buf, "unknown\n"); > + if (fujitsu_hotkey->rfkill_state & 0x100) > + return sprintf(buf, "open\n"); > + else > + return sprintf(buf, "closed\n"); > +} > > - return fujitsu_hotkey->irb; > +static ssize_t > +show_dock_state(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) > + return sprintf(buf, "unknown\n"); > + if (fujitsu_hotkey->rfkill_state & 0x200) > + return sprintf(buf, "docked\n"); > + else > + return sprintf(buf, "undocked\n"); > } > > static ssize_t > -ignore_store(struct device *dev, > - struct device_attribute *attr, const char *buf, size_t count) > +show_radios_state(struct device *dev, > + struct device_attribute *attr, char *buf) > { > - return count; > + if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) > + return sprintf(buf, "unknown\n"); > + if (fujitsu_hotkey->rfkill_state & 0x20) > + return sprintf(buf, "on\n"); > + else > + return sprintf(buf, "killed\n"); > } > > static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store); > static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed, > ignore_store); > static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); > +static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store); > +static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store); > +static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store); > > static struct attribute *fujitsupf_attributes[] = { > &dev_attr_brightness_changed.attr, > &dev_attr_max_brightness.attr, > &dev_attr_lcd_level.attr, > + &dev_attr_lid.attr, > + &dev_attr_dock.attr, > + &dev_attr_radios.attr, > NULL > }; > > @@ -771,7 +958,8 @@ > input->id.bustype = BUS_HOST; > input->id.product = 0x06; > input->dev.parent = &device->dev; > - input->evbit[0] = BIT(EV_KEY); > + > + set_bit(EV_KEY, input->evbit); > set_bit(fujitsu->keycode1, input->keybit); > set_bit(fujitsu->keycode2, input->keybit); > set_bit(fujitsu->keycode3, input->keybit); > @@ -803,10 +991,44 @@ > printk(KERN_ERR "_INI Method failed\n"); > } > > - i = 0; /* Discard hotkey ringbuffer */ > - while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ; > + i = 0; > + while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 > + && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) > + ; /* No action, result is discarded */ > vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); > > + fujitsu_hotkey->rfkill_state = > + call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); > + > + /* Suspect this is a keymap of the application panel, print it */ > + printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", > + call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); > + > + #ifdef CONFIG_LEDS_CLASS > + if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { > + result = led_classdev_register(&fujitsu->pf_device->dev, > + &logolamp_led); > + if (result == 0) { > + fujitsu_hotkey->logolamp_registered = 1; > + } else { > + printk(KERN_ERR "fujitsu-laptop: Could not register " > + "LED handler for logo lamp, error %i\n", result); > + } > + } > + > + if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && > + (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { > + result = led_classdev_register(&fujitsu->pf_device->dev, > + &kblamps_led); > + if (result == 0) { > + fujitsu_hotkey->kblamps_registered = 1; > + } else { > + printk(KERN_ERR "fujitsu-laptop: Could not register " > + "LED handler for keyboard lamps, error %i\n", result); > + } > + } > + #endif > + > return result; > > end: > @@ -852,16 +1074,15 @@ > > input = fujitsu_hotkey->input; > > - vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n"); > + fujitsu_hotkey->rfkill_state = > + call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); > > switch (event) { > case ACPI_FUJITSU_NOTIFY_CODE1: > i = 0; > - while ((irb = get_irb()) != 0 > - && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { > - vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n", > - irb); > - > + while ((irb = > + call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 > + && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { > switch (irb & 0x4ff) { > case KEY1_CODE: > keycode = fujitsu->keycode1; > @@ -1035,6 +1256,15 @@ > goto fail_hotkey1; > } > > + /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */ > + > + if (!acpi_video_backlight_support()) { > + if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) > + fujitsu->bl_device->props.power = 4; > + else > + fujitsu->bl_device->props.power = 0; > + } > + > printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION > " successfully loaded.\n"); > > @@ -1074,6 +1304,14 @@ > > static void __exit fujitsu_cleanup(void) > { > + #ifdef CONFIG_LEDS_CLASS > + if (fujitsu_hotkey->logolamp_registered != 0) > + led_classdev_unregister(&logolamp_led); > + > + if (fujitsu_hotkey->kblamps_registered != 0) > + led_classdev_unregister(&kblamps_led); > + #endif > + > sysfs_remove_group(&fujitsu->pf_device->dev.kobj, > &fujitsupf_attribute_group); > platform_device_unregister(fujitsu->pf_device); > @@ -1108,12 +1346,13 @@ > MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); > #endif > > -MODULE_AUTHOR("Jonathan Woithe, Peter Gruber"); > +MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon"); > MODULE_DESCRIPTION("Fujitsu laptop extras support"); > MODULE_VERSION(FUJITSU_DRIVER_VERSION); > MODULE_LICENSE("GPL"); > > MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); > +MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); > MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); > > static struct pnp_device_id pnp_ids[] = { > -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html