Loading drivers/misc/fujitsu-laptop.c +293 −126 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ /* Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> Copyright (C) 2008 Peter Gruber <nokos@gmx.net> Copyright (C) 2008 Tony Vroon <tony@linx.net> Based on earlier work: Copyright (C) 2003 Shane Spencer <shane@bogomip.com> Adrian Yee <brewt-fujitsu@brewt.org> Loading Loading @@ -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 Loading @@ -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 Loading Loading @@ -133,7 +155,6 @@ struct fujitsu_t { static struct fujitsu_t *fujitsu; static int use_alt_lcd_levels = -1; static int disable_brightness_keys = -1; static int disable_brightness_adjust = -1; /* Device used to access other hotkeys on the laptop */ Loading @@ -145,8 +166,9 @@ struct fujitsu_hotkey_t { 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; Loading @@ -154,12 +176,139 @@ static struct fujitsu_hotkey_t *fujitsu_hotkey; 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) Loading Loading @@ -263,44 +412,34 @@ static int get_max_brightness(void) return fujitsu->max_brightness; } static int get_lcd_level_alt(void) { unsigned long long state = 0; acpi_status status = AE_OK; vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLS\n"); status = acpi_evaluate_integer(fujitsu->acpi_handle, "GBLS", NULL, &state); if (status < 0) return status; fujitsu->brightness_level = state & 0x0fffffff; if (state & 0x80000000) fujitsu->brightness_changed = 1; else fujitsu->brightness_changed = 0; return fujitsu->brightness_level; } /* Backlight device stuff */ static int bl_get_brightness(struct backlight_device *b) { if (use_alt_lcd_levels) return get_lcd_level_alt(); else return get_lcd_level(); } 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 = { Loading Loading @@ -344,9 +483,6 @@ static ssize_t show_lcd_level(struct device *dev, int ret; if (use_alt_lcd_levels) ret = get_lcd_level_alt(); else ret = get_lcd_level(); if (ret < 0) return ret; Loading @@ -372,9 +508,6 @@ static ssize_t store_lcd_level(struct device *dev, if (ret < 0) return ret; if (use_alt_lcd_levels) ret = get_lcd_level_alt(); else ret = get_lcd_level(); if (ret < 0) return ret; Loading @@ -382,42 +515,64 @@ static ssize_t store_lcd_level(struct device *dev, 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 }; Loading @@ -435,24 +590,16 @@ static struct platform_driver fujitsupf_driver = { static void dmi_check_cb_common(const struct dmi_system_id *id) { acpi_handle handle; int have_blnf; printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n", id->ident); have_blnf = ACPI_SUCCESS (acpi_get_handle(NULL, "\\_SB.PCI0.GFX0.LCD.BLNF", &handle)); if (use_alt_lcd_levels == -1) { vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting usealt\n"); if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) use_alt_lcd_levels = 1; } if (disable_brightness_keys == -1) { vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting disable_keys\n"); disable_brightness_keys = have_blnf ? 1 : 0; } if (disable_brightness_adjust == -1) { vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting disable_adjust\n"); disable_brightness_adjust = have_blnf ? 0 : 1; else use_alt_lcd_levels = 0; vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as " "%i\n", use_alt_lcd_levels); } } Loading Loading @@ -581,18 +728,13 @@ static int acpi_fujitsu_add(struct acpi_device *device) /* do config (detect defaults) */ use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0; disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0; disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0; vdbg_printk(FUJLAPTOP_DBG_INFO, "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n", use_alt_lcd_levels, disable_brightness_keys, disable_brightness_adjust); "config: [alt interface: %d], [adjust disable: %d]\n", use_alt_lcd_levels, disable_brightness_adjust); if (get_max_brightness() <= 0) fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS; if (use_alt_lcd_levels) get_lcd_level_alt(); else get_lcd_level(); return result; Loading Loading @@ -644,43 +786,23 @@ static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) case ACPI_FUJITSU_NOTIFY_CODE1: keycode = 0; oldb = fujitsu->brightness_level; get_lcd_level(); /* the alt version always yields changed */ get_lcd_level(); newb = fujitsu->brightness_level; vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i (%i)]\n", oldb, newb, fujitsu->brightness_changed); if (oldb == newb && fujitsu->brightness_changed) { keycode = 0; if (disable_brightness_keys != 1) { if (oldb == 0) { acpi_bus_generate_proc_event (fujitsu->dev, ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSDOWN; } else if (oldb == (fujitsu->max_brightness) - 1) { acpi_bus_generate_proc_event (fujitsu->dev, ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSUP; } } } else if (oldb < newb) { if (oldb < newb) { if (disable_brightness_adjust != 1) { if (use_alt_lcd_levels) set_lcd_level_alt(newb); else set_lcd_level(newb); } if (disable_brightness_keys != 1) { acpi_bus_generate_proc_event(fujitsu->dev, ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSUP; } } else if (oldb > newb) { if (disable_brightness_adjust != 1) { if (use_alt_lcd_levels) Loading @@ -688,14 +810,10 @@ static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) else set_lcd_level(newb); } if (disable_brightness_keys != 1) { acpi_bus_generate_proc_event(fujitsu->dev, ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSDOWN; } } else { keycode = KEY_UNKNOWN; } break; default: keycode = KEY_UNKNOWN; Loading Loading @@ -771,7 +889,8 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) 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); Loading Loading @@ -803,10 +922,44 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) 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: Loading Loading @@ -852,16 +1005,15 @@ static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, 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 while ((irb = call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n", irb); switch (irb & 0x4ff) { case KEY1_CODE: keycode = fujitsu->keycode1; Loading Loading @@ -1035,6 +1187,15 @@ static int __init fujitsu_init(void) 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"); Loading Loading @@ -1074,6 +1235,14 @@ fail_acpi: 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); Loading @@ -1098,9 +1267,6 @@ module_exit(fujitsu_cleanup); module_param(use_alt_lcd_levels, uint, 0644); MODULE_PARM_DESC(use_alt_lcd_levels, "Use alternative interface for lcd_levels (needed for Lifebook s6410)."); module_param(disable_brightness_keys, uint, 0644); MODULE_PARM_DESC(disable_brightness_keys, "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device)."); module_param(disable_brightness_adjust, uint, 0644); MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment ."); #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG Loading @@ -1108,12 +1274,13 @@ module_param_named(debug, dbg_level, uint, 0644); 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[] = { Loading Loading
drivers/misc/fujitsu-laptop.c +293 −126 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ /* Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> Copyright (C) 2008 Peter Gruber <nokos@gmx.net> Copyright (C) 2008 Tony Vroon <tony@linx.net> Based on earlier work: Copyright (C) 2003 Shane Spencer <shane@bogomip.com> Adrian Yee <brewt-fujitsu@brewt.org> Loading Loading @@ -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 Loading @@ -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 Loading Loading @@ -133,7 +155,6 @@ struct fujitsu_t { static struct fujitsu_t *fujitsu; static int use_alt_lcd_levels = -1; static int disable_brightness_keys = -1; static int disable_brightness_adjust = -1; /* Device used to access other hotkeys on the laptop */ Loading @@ -145,8 +166,9 @@ struct fujitsu_hotkey_t { 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; Loading @@ -154,12 +176,139 @@ static struct fujitsu_hotkey_t *fujitsu_hotkey; 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) Loading Loading @@ -263,44 +412,34 @@ static int get_max_brightness(void) return fujitsu->max_brightness; } static int get_lcd_level_alt(void) { unsigned long long state = 0; acpi_status status = AE_OK; vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLS\n"); status = acpi_evaluate_integer(fujitsu->acpi_handle, "GBLS", NULL, &state); if (status < 0) return status; fujitsu->brightness_level = state & 0x0fffffff; if (state & 0x80000000) fujitsu->brightness_changed = 1; else fujitsu->brightness_changed = 0; return fujitsu->brightness_level; } /* Backlight device stuff */ static int bl_get_brightness(struct backlight_device *b) { if (use_alt_lcd_levels) return get_lcd_level_alt(); else return get_lcd_level(); } 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 = { Loading Loading @@ -344,9 +483,6 @@ static ssize_t show_lcd_level(struct device *dev, int ret; if (use_alt_lcd_levels) ret = get_lcd_level_alt(); else ret = get_lcd_level(); if (ret < 0) return ret; Loading @@ -372,9 +508,6 @@ static ssize_t store_lcd_level(struct device *dev, if (ret < 0) return ret; if (use_alt_lcd_levels) ret = get_lcd_level_alt(); else ret = get_lcd_level(); if (ret < 0) return ret; Loading @@ -382,42 +515,64 @@ static ssize_t store_lcd_level(struct device *dev, 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 }; Loading @@ -435,24 +590,16 @@ static struct platform_driver fujitsupf_driver = { static void dmi_check_cb_common(const struct dmi_system_id *id) { acpi_handle handle; int have_blnf; printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n", id->ident); have_blnf = ACPI_SUCCESS (acpi_get_handle(NULL, "\\_SB.PCI0.GFX0.LCD.BLNF", &handle)); if (use_alt_lcd_levels == -1) { vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting usealt\n"); if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) use_alt_lcd_levels = 1; } if (disable_brightness_keys == -1) { vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting disable_keys\n"); disable_brightness_keys = have_blnf ? 1 : 0; } if (disable_brightness_adjust == -1) { vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting disable_adjust\n"); disable_brightness_adjust = have_blnf ? 0 : 1; else use_alt_lcd_levels = 0; vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as " "%i\n", use_alt_lcd_levels); } } Loading Loading @@ -581,18 +728,13 @@ static int acpi_fujitsu_add(struct acpi_device *device) /* do config (detect defaults) */ use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0; disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0; disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0; vdbg_printk(FUJLAPTOP_DBG_INFO, "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n", use_alt_lcd_levels, disable_brightness_keys, disable_brightness_adjust); "config: [alt interface: %d], [adjust disable: %d]\n", use_alt_lcd_levels, disable_brightness_adjust); if (get_max_brightness() <= 0) fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS; if (use_alt_lcd_levels) get_lcd_level_alt(); else get_lcd_level(); return result; Loading Loading @@ -644,43 +786,23 @@ static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) case ACPI_FUJITSU_NOTIFY_CODE1: keycode = 0; oldb = fujitsu->brightness_level; get_lcd_level(); /* the alt version always yields changed */ get_lcd_level(); newb = fujitsu->brightness_level; vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i (%i)]\n", oldb, newb, fujitsu->brightness_changed); if (oldb == newb && fujitsu->brightness_changed) { keycode = 0; if (disable_brightness_keys != 1) { if (oldb == 0) { acpi_bus_generate_proc_event (fujitsu->dev, ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSDOWN; } else if (oldb == (fujitsu->max_brightness) - 1) { acpi_bus_generate_proc_event (fujitsu->dev, ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSUP; } } } else if (oldb < newb) { if (oldb < newb) { if (disable_brightness_adjust != 1) { if (use_alt_lcd_levels) set_lcd_level_alt(newb); else set_lcd_level(newb); } if (disable_brightness_keys != 1) { acpi_bus_generate_proc_event(fujitsu->dev, ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSUP; } } else if (oldb > newb) { if (disable_brightness_adjust != 1) { if (use_alt_lcd_levels) Loading @@ -688,14 +810,10 @@ static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) else set_lcd_level(newb); } if (disable_brightness_keys != 1) { acpi_bus_generate_proc_event(fujitsu->dev, ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSDOWN; } } else { keycode = KEY_UNKNOWN; } break; default: keycode = KEY_UNKNOWN; Loading Loading @@ -771,7 +889,8 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) 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); Loading Loading @@ -803,10 +922,44 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) 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: Loading Loading @@ -852,16 +1005,15 @@ static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, 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 while ((irb = call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n", irb); switch (irb & 0x4ff) { case KEY1_CODE: keycode = fujitsu->keycode1; Loading Loading @@ -1035,6 +1187,15 @@ static int __init fujitsu_init(void) 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"); Loading Loading @@ -1074,6 +1235,14 @@ fail_acpi: 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); Loading @@ -1098,9 +1267,6 @@ module_exit(fujitsu_cleanup); module_param(use_alt_lcd_levels, uint, 0644); MODULE_PARM_DESC(use_alt_lcd_levels, "Use alternative interface for lcd_levels (needed for Lifebook s6410)."); module_param(disable_brightness_keys, uint, 0644); MODULE_PARM_DESC(disable_brightness_keys, "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device)."); module_param(disable_brightness_adjust, uint, 0644); MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment ."); #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG Loading @@ -1108,12 +1274,13 @@ module_param_named(debug, dbg_level, uint, 0644); 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[] = { Loading