text: /* Copyright 2018 The Chromium OS Authors. All rights reserved.
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "console.h"
  #include "ec_commands.h"
  #include "hooks.h"
  #include "host_command.h"
  #include "keyboard_backlight.h"
  #include "lid_switch.h"
  #include "timer.h"
  #include "util.h"
  
  #define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args)
  #define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args)
  
  static struct kblight_conf kblight;
  static int current_percent;
  
  void __attribute__((weak)) board_kblight_init(void)
  { }
  
  static int kblight_init(void)
  {
  	if (!kblight.drv || !kblight.drv->init)
  		return EC_ERROR_UNIMPLEMENTED;
  	return kblight.drv->init();
  }
  
  static void kblight_set_deferred(void)
  {
  	if (!kblight.drv || !kblight.drv->set)
  		return;
  	kblight.drv->set(current_percent);
  }
  DECLARE_DEFERRED(kblight_set_deferred);
  
  /*
   * APIs
   */
  int kblight_set(int percent)
  {
  	if (current_percent < 0 || 100 < current_percent)
  		return EC_ERROR_INVAL;
  	current_percent = percent;
  	/* Need to defer i2c in case it's called from an interrupt handler. */
  	hook_call_deferred(&kblight_set_deferred_data, 0);
  	return EC_SUCCESS;
  }
  
  int kblight_get(void)
  {
  	return current_percent;
  }
  
  int kblight_enable(int enable)
  {
  	if (!kblight.drv || !kblight.drv->enable)
  		return -1;
  	return kblight.drv->enable(enable);
  }
  
  int kblight_register(const struct kblight_drv *drv)
  {
  	kblight.drv = drv;
  	CPRINTS("kblight registered");
  	return EC_SUCCESS;
  }
  
  /*
   * Hooks
   */
  static void keyboard_backlight_init(void)
  {
  	/* Uses PWM by default. Can be customized by board_kblight_init */
  	kblight_register(&kblight_pwm);
  	board_kblight_init();
  	if (kblight_init())
  		CPRINTS("kblight init failed");
  }
  DECLARE_HOOK(HOOK_INIT, keyboard_backlight_init, HOOK_PRIO_DEFAULT);
  
  static void kblight_suspend(void)
  {
  	kblight_enable(0);
  }
  DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, kblight_suspend, HOOK_PRIO_DEFAULT);
  
  static void kblight_resume(void)
  {
  	if (lid_is_open()) {
  		kblight_enable(1);
  		kblight_set(current_percent);
  	}
  }
  DECLARE_HOOK(HOOK_CHIPSET_RESUME, kblight_resume, HOOK_PRIO_DEFAULT);
  
  static void kblight_lid_change(void)
  {
  	kblight_enable(lid_is_open());
  }
  DECLARE_HOOK(HOOK_LID_CHANGE, kblight_lid_change, HOOK_PRIO_DEFAULT);
  
  /*
   * Console and host commands
   */
  static int cc_kblight(int argc, char **argv)
  {
  	if (argc >= 2) {
  		char *e;
  		int i = strtoi(argv[1], &e, 0);
  		if (*e)
  			return EC_ERROR_PARAM1;
  		if (kblight_set(i))
  			return EC_ERROR_PARAM1;
  	}
  	ccprintf("Keyboard backlight: %d%%\n", kblight_get());
  	return EC_SUCCESS;
  }
  DECLARE_CONSOLE_COMMAND(kblight, cc_kblight,
  			"percent",
  			"Get/set keyboard backlight");
  
  int hc_get_keyboard_backlight(struct host_cmd_handler_args *args)
  {
  	struct ec_response_pwm_get_keyboard_backlight *r = args->response;
  
  	r->percent = kblight_get();
  	r->enabled = 1;			/* Deprecated */
  	args->response_size = sizeof(*r);
  
  	return EC_RES_SUCCESS;
  }
  DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT,
  		     hc_get_keyboard_backlight,
  		     EC_VER_MASK(0));
  
  int hc_set_keyboard_backlight(struct host_cmd_handler_args *args)
  {
  	const struct ec_params_pwm_set_keyboard_backlight *p = args->params;
  
  	if (kblight_set(p->percent))
  		return EC_RES_ERROR;
  	return EC_RES_SUCCESS;
  }
  DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT,
  		     hc_set_keyboard_backlight,
  		     EC_VER_MASK(0));
  