#include #include #include "leds.h" #include "hui_internal.h" static void led_release(struct kobject *kobj) { struct led *led = to_led_obj(kobj); kfree(led); } struct led_attribute { struct attribute attr; ssize_t (*show)(struct led *led, char *buf); ssize_t (*store)(struct led *led, const char *buf, size_t count); }; #define to_led_attr(x) container_of(x, struct led_attribute, attr) static ssize_t led_attr_show(struct kobject *kobj, struct attribute *attribute, char *buf) { struct led_attribute *attr = to_led_attr(attribute); struct led *led = to_led_obj(kobj); ssize_t ret; if (!attr->show) return -EIO; mutex_lock(&hui_update_mutex); ret = attr->show(led, buf); mutex_unlock(&hui_update_mutex); return ret; } static ssize_t led_attr_store(struct kobject *kobj, struct attribute *attribute, const char *buf, size_t count) { struct led_attribute *attr = to_led_attr(attribute); struct led *led = to_led_obj(kobj); ssize_t ret; if (!attr->store) return -EIO; mutex_lock(&hui_update_mutex); ret = attr->store(led, buf, count); mutex_unlock(&hui_update_mutex); return ret; } static const struct sysfs_ops led_sysfs_ops = { .show = led_attr_show, .store = led_attr_store, }; static ssize_t type_show(struct led *led, char *buf) { return sprintf(buf, "%s\n", led->type->name); } static ssize_t type_store(struct led *led, const char *buf, size_t count) { const struct led_type *type; type = lookup_led_type(buf); if (!type) return -EINVAL; led->type = type; hui_schedule_update(); return count; } static struct led_attribute led_type_attribute = __ATTR(type, 0644, type_show, type_store); static inline ssize_t format_blink_code(char *buf, struct blink_code code) { return sprintf(buf, "%d %d %d #%02X%02X%02X\n", code.type, code.params[0], code.params[1], code.color.c[0], code.color.c[1], code.color.c[2]); } static ssize_t code_show(struct led *led, char *buf) { return format_blink_code(buf, led->current_code); } static struct led_attribute code_attribute = __ATTR(code, 0444, code_show, NULL); static ssize_t forced_code_show(struct led *led, char *buf) { if (led->forced_code.type == blink_invalid) return sprintf(buf, "\n"); return format_blink_code(buf, led->forced_code); } enum code_part { CODE_PART_TYPE, CODE_PART_PARAM1, CODE_PART_PARAM2, CODE_PART_COLOR, CODE_PART_MAX, }; static ssize_t forced_code_store(struct led *led, const char *buf, size_t count) { struct blink_code code = { 0 }; enum code_part part; char part_buf[16]; size_t orig_count = count; int err, i; if (sysfs_streq(buf, "") || sysfs_streq(buf, "none") || sysfs_streq(buf, "")) { code.type = blink_invalid; led->forced_code = code; hui_schedule_update(); return count; } // Store the original color code.color = led->current_code.color; code.color.brightness = 0xFF; // This is a bit of odd logic to parse the blink code. // // We need to seperate the input string in space separated lists, that // we can convert to integers. Additionally we want to make the params // optinal, so we may skip them // for (part = CODE_PART_TYPE; part < CODE_PART_MAX; part++) { memset(part_buf, 0, sizeof(part_buf)); for (; *buf == ' '; buf++, count--) ; // We've got the type, so we reuse the old color and can be // done. if (part > CODE_PART_TYPE && strchr(" \r\n", *buf)) break; // Copy a part from the buffer for (i = 0; count > 0 && *buf && !strchr(" \r\n", *buf); i++, buf++, count--) part_buf[i] = *buf; if (part == CODE_PART_TYPE) { err = kstrtou8(part_buf, 10, &code.type); if (err) return err; if (code.type >= BLINK_CODE_MAX) return -EINVAL; } if ((part == CODE_PART_PARAM1 || part == CODE_PART_PARAM2) && part_buf[0] == '#') { part = CODE_PART_COLOR; } if (part == CODE_PART_PARAM1 || part == CODE_PART_PARAM2) { u8 param; err = kstrtou8(part_buf, 10, ¶m); if (err) return err; code.params[part - CODE_PART_PARAM1] = param; } if (part == CODE_PART_COLOR) { u32 color; if (part_buf[0] == '#') { memmove(part_buf, part_buf + 1, sizeof(part_buf) - 1); } err = kstrtou32(part_buf, 16, &color); if (err) return err; // Only use lower 24 bits if (color & 0xFF000000) return -EINVAL; // Extract lower 24 bits // TODO: maybe we can just memcpy here? for (i = 0; i < 3; i++) { code.color.c[i] = (color >> (16 - i * 8)) & 0xFF; } } } if (*buf != '\n' && *buf != '\0') return -EINVAL; led->forced_code = code; hui_schedule_update(); return orig_count; } static struct led_attribute forced_code_attribute = __ATTR(forced_code, 0644, forced_code_show, forced_code_store); static ssize_t location_show(struct led *led, char *buf) { if (led->options.location == 0) return sprintf(buf, "\n"); return sprintf(buf, "%d\n", led->options.location); } static ssize_t location_store(struct led *led, const char *buf, size_t count) { int err; u8 location; if (sysfs_streq(buf, "") || sysfs_streq(buf, "none") || sysfs_streq(buf, "")) { led->options.location = 0; hui_schedule_update(); return count; } err = kstrtou8(buf, 10, &location); if (err) return err; led->options.location = location; hui_schedule_update(); return count; } static struct led_attribute location_attribute = __ATTR(location, 0644, location_show, location_store); static ssize_t dimmable_show(struct led *led, char *buf) { return sprintf(buf, "%d\n", led->dimmable); } static ssize_t dimmable_store(struct led *led, const char *buf, size_t count) { if (sysfs_streq(buf, "1") || sysfs_streq(buf, "true")) led->dimmable = true; else if (sysfs_streq(buf, "0") || sysfs_streq(buf, "false")) led->dimmable = false; else return -EINVAL; hui_schedule_update(); return count; } static struct led_attribute dimmable_attribute = __ATTR(dimmable, 0644, dimmable_show, dimmable_store); static ssize_t brightness_min_show(struct led *led, char *buf) { return sprintf(buf, "%d\n", led->brightness_min); } static ssize_t brightness_min_store(struct led *led, const char *buf, size_t count) { int err; u8 b; err = kstrtou8(buf, 10, &b); if (err) return err; led->brightness_min = b; hui_schedule_update(); return count; } static struct led_attribute brightness_min_attribute = __ATTR(brightness_min, 0644, brightness_min_show, brightness_min_store); static ssize_t brightness_max_show(struct led *led, char *buf) { return sprintf(buf, "%d\n", led->brightness_max); } static ssize_t brightness_max_store(struct led *led, const char *buf, size_t count) { int err; u8 b; err = kstrtou8(buf, 10, &b); if (err) return err; led->brightness_max = b; hui_schedule_update(); return count; } static struct led_attribute brightness_max_attribute = __ATTR(brightness_max, 0644, brightness_max_show, brightness_max_store); static struct attribute *led_default_attrs[] = { &led_type_attribute.attr, &code_attribute.attr, &forced_code_attribute.attr, &location_attribute.attr, &dimmable_attribute.attr, &brightness_min_attribute.attr, &brightness_max_attribute.attr, NULL, }; struct kobj_type led_ktype = { .sysfs_ops = &led_sysfs_ops, .release = led_release, .default_attrs = led_default_attrs, };