+/* Brightness notify */
+
+static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct input_dev *input;
+ int keycode;
+ int oldb, newb;
+
+ input = fujitsu->input;
+
+ switch (event) {
+ case ACPI_FUJITSU_NOTIFY_CODE1:
+ keycode = 0;
+ oldb = fujitsu->brightness_level;
+ get_lcd_level(); /* the alt version always yields changed */
+ 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 (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)
+ 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_DEC_BRIGHTNESS,
+ 0);
+ keycode = KEY_BRIGHTNESSDOWN;
+ }
+ } else {
+ keycode = KEY_UNKNOWN;
+ }
+ break;
+ default:
+ keycode = KEY_UNKNOWN;
+ vdbg_printk(FUJLAPTOP_DBG_WARN,
+ "unsupported event [0x%x]\n", event);
+ break;
+ }
+
+ if (keycode != 0) {
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+ }
+
+ return;
+}
+
+/* ACPI device for hotkey handling */
+
+static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
+{
+ acpi_status status;
+ acpi_handle handle;
+ int result = 0;
+ int state = 0;
+ struct input_dev *input;
+ int error;
+ int i;
+
+ if (!device)
+ return -EINVAL;
+
+ fujitsu_hotkey->acpi_handle = device->handle;
+ sprintf(acpi_device_name(device), "%s",
+ ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
+ sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
+ acpi_driver_data(device) = fujitsu_hotkey;
+
+ status = acpi_install_notify_handler(device->handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_fujitsu_hotkey_notify,
+ fujitsu_hotkey);
+
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "Error installing notify handler\n");
+ error = -ENODEV;
+ goto err_stop;
+ }
+
+ /* kfifo */
+ spin_lock_init(&fujitsu_hotkey->fifo_lock);
+ fujitsu_hotkey->fifo =
+ kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL,
+ &fujitsu_hotkey->fifo_lock);
+ if (IS_ERR(fujitsu_hotkey->fifo)) {
+ printk(KERN_ERR "kfifo_alloc failed\n");
+ error = PTR_ERR(fujitsu_hotkey->fifo);
+ goto err_stop;
+ }
+
+ fujitsu_hotkey->input = input = input_allocate_device();
+ if (!input) {
+ error = -ENOMEM;
+ goto err_uninstall_notify;
+ }
+
+ snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
+ "%s/video/input0", acpi_device_hid(device));
+
+ input->name = acpi_device_name(device);
+ input->phys = fujitsu_hotkey->phys;
+ input->id.bustype = BUS_HOST;
+ input->id.product = 0x06;
+ input->dev.parent = &device->dev;
+ input->evbit[0] = BIT(EV_KEY);
+ set_bit(KEY_SCREENLOCK, input->keybit);
+ set_bit(KEY_MEDIA, input->keybit);
+ set_bit(KEY_EMAIL, input->keybit);
+ set_bit(KEY_SUSPEND, input->keybit);
+ set_bit(KEY_UNKNOWN, input->keybit);
+
+ error = input_register_device(input);
+ if (error)
+ goto err_free_input_dev;
+
+ result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state);
+ if (result) {
+ printk(KERN_ERR "Error reading power state\n");
+ goto end;
+ }
+
+ printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
+ acpi_device_name(device), acpi_device_bid(device),
+ !device->power.state ? "on" : "off");
+
+ fujitsu_hotkey->dev = device;
+
+ if (ACPI_SUCCESS
+ (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
+ vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
+ if (ACPI_FAILURE
+ (acpi_evaluate_object
+ (device->handle, METHOD_NAME__INI, NULL, NULL)))
+ printk(KERN_ERR "_INI Method failed\n");
+ }
+
+ i = 0; /* Discard hotkey ringbuffer */
+ while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ;
+ vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
+
+ return result;
+
+end:
+err_free_input_dev:
+ input_free_device(input);
+err_uninstall_notify:
+ acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+ acpi_fujitsu_hotkey_notify);
+ kfifo_free(fujitsu_hotkey->fifo);
+err_stop:
+
+ return result;
+}
+
+static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type)
+{
+ acpi_status status;
+ struct fujitsu_hotkey_t *fujitsu_hotkey = NULL;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+
+ fujitsu_hotkey = acpi_driver_data(device);
+
+ status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_fujitsu_hotkey_notify);
+
+ fujitsu_hotkey->acpi_handle = NULL;
+
+ kfifo_free(fujitsu_hotkey->fifo);
+
+ return 0;
+}
+
+static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
+ void *data)
+{
+ struct input_dev *input;
+ int keycode, keycode_r;
+ unsigned int irb = 1;
+ int i, status;
+
+ input = fujitsu_hotkey->input;
+
+ vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n");
+
+ 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);
+
+ switch (irb & 0x4ff) {
+ case LOCK_KEY:
+ keycode = KEY_SCREENLOCK;
+ break;
+ case DISPLAY_KEY:
+ keycode = KEY_MEDIA;
+ break;
+ case ENERGY_KEY:
+ keycode = KEY_EMAIL;
+ break;
+ case REST_KEY:
+ keycode = KEY_SUSPEND;
+ break;
+ case 0:
+ keycode = 0;
+ break;
+ default:
+ vdbg_printk(FUJLAPTOP_DBG_WARN,
+ "Unknown GIRB result [%x]\n", irb);
+ keycode = -1;
+ break;
+ }
+ if (keycode > 0) {
+ vdbg_printk(FUJLAPTOP_DBG_TRACE,
+ "Push keycode into ringbuffer [%d]\n",
+ keycode);
+ status = kfifo_put(fujitsu_hotkey->fifo,
+ (unsigned char *)&keycode,
+ sizeof(keycode));
+ if (status != sizeof(keycode)) {
+ vdbg_printk(FUJLAPTOP_DBG_WARN,
+ "Could not push keycode [0x%x]\n",
+ keycode);
+ } else {
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ }
+ } else if (keycode == 0) {
+ while ((status =
+ kfifo_get
+ (fujitsu_hotkey->fifo, (unsigned char *)
+ &keycode_r,
+ sizeof
+ (keycode_r))) == sizeof(keycode_r)) {
+ input_report_key(input, keycode_r, 0);
+ input_sync(input);
+ vdbg_printk(FUJLAPTOP_DBG_TRACE,
+ "Pop keycode from ringbuffer [%d]\n",
+ keycode_r);
+ }
+ }
+ }
+
+ break;
+ default:
+ keycode = KEY_UNKNOWN;
+ vdbg_printk(FUJLAPTOP_DBG_WARN,
+ "Unsupported event [0x%x]\n", event);
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+ break;
+ }
+
+ return;
+}
+
+/* Initialization */
+