+#define TPACPI_LED_NUMLEDS 8
+static struct tpacpi_led_classdev *tpacpi_leds;
+static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
+static const char const *tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
+ /* there's a limit of 19 chars + NULL before 2.6.26 */
+ "tpacpi::power",
+ "tpacpi:orange:batt",
+ "tpacpi:green:batt",
+ "tpacpi::dock_active",
+ "tpacpi::bay_active",
+ "tpacpi::dock_batt",
+ "tpacpi::unknown_led",
+ "tpacpi::standby",
+};
+
+static int led_get_status(unsigned int led)
+{
+ int status;
+ enum led_status_t led_s;
+
+ switch (led_supported) {
+ case TPACPI_LED_570:
+ if (!acpi_evalf(ec_handle,
+ &status, "GLED", "dd", 1 << led))
+ return -EIO;
+ led_s = (status == 0)?
+ TPACPI_LED_OFF :
+ ((status == 1)?
+ TPACPI_LED_ON :
+ TPACPI_LED_BLINK);
+ tpacpi_led_state_cache[led] = led_s;
+ return led_s;
+ default:
+ return -ENXIO;
+ }
+
+ /* not reached */
+}
+
+static int led_set_status(unsigned int led, enum led_status_t ledstatus)
+{
+ /* off, on, blink. Index is led_status_t */
+ static const int const led_sled_arg1[] = { 0, 1, 3 };
+ static const int const led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
+ static const int const led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
+ static const int const led_led_arg1[] = { 0, 0x80, 0xc0 };
+
+ int rc = 0;
+
+ switch (led_supported) {
+ case TPACPI_LED_570:
+ /* 570 */
+ led = 1 << led;
+ if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+ led, led_sled_arg1[ledstatus]))
+ rc = -EIO;
+ break;
+ case TPACPI_LED_OLD:
+ /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
+ led = 1 << led;
+ rc = ec_write(TPACPI_LED_EC_HLMS, led);
+ if (rc >= 0)
+ rc = ec_write(TPACPI_LED_EC_HLBL,
+ led * led_exp_hlbl[ledstatus]);
+ if (rc >= 0)
+ rc = ec_write(TPACPI_LED_EC_HLCL,
+ led * led_exp_hlcl[ledstatus]);
+ break;
+ case TPACPI_LED_NEW:
+ /* all others */
+ if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+ led, led_led_arg1[ledstatus]))
+ rc = -EIO;
+ break;
+ default:
+ rc = -ENXIO;
+ }
+
+ if (!rc)
+ tpacpi_led_state_cache[led] = ledstatus;
+
+ return rc;
+}
+
+static void led_sysfs_set_status(unsigned int led,
+ enum led_brightness brightness)
+{
+ led_set_status(led,
+ (brightness == LED_OFF) ?
+ TPACPI_LED_OFF :
+ (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
+ TPACPI_LED_BLINK : TPACPI_LED_ON);
+}
+
+static void led_set_status_worker(struct work_struct *work)
+{
+ struct tpacpi_led_classdev *data =
+ container_of(work, struct tpacpi_led_classdev, work);
+
+ if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
+ led_sysfs_set_status(data->led, data->new_brightness);
+}
+
+static void led_sysfs_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct tpacpi_led_classdev *data = container_of(led_cdev,
+ struct tpacpi_led_classdev, led_classdev);
+
+ data->new_brightness = brightness;
+ queue_work(tpacpi_wq, &data->work);
+}
+
+static int led_sysfs_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct tpacpi_led_classdev *data = container_of(led_cdev,
+ struct tpacpi_led_classdev, led_classdev);
+
+ /* Can we choose the flash rate? */
+ if (*delay_on == 0 && *delay_off == 0) {
+ /* yes. set them to the hardware blink rate (1 Hz) */
+ *delay_on = 500; /* ms */
+ *delay_off = 500; /* ms */
+ } else if ((*delay_on != 500) || (*delay_off != 500))
+ return -EINVAL;
+
+ data->new_brightness = TPACPI_LED_BLINK;
+ queue_work(tpacpi_wq, &data->work);
+
+ return 0;
+}
+
+static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
+{
+ int rc;
+
+ struct tpacpi_led_classdev *data = container_of(led_cdev,
+ struct tpacpi_led_classdev, led_classdev);
+
+ rc = led_get_status(data->led);
+
+ if (rc == TPACPI_LED_OFF || rc < 0)
+ rc = LED_OFF; /* no error handling in led class :( */
+ else
+ rc = LED_FULL;
+
+ return rc;
+}
+
+static void led_exit(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+ if (tpacpi_leds[i].led_classdev.name)
+ led_classdev_unregister(&tpacpi_leds[i].led_classdev);
+ }
+
+ kfree(tpacpi_leds);
+ tpacpi_leds = NULL;
+}
+