#include #include #include #include #include #include #include "hui_internal.h" struct hui *hui_kobj; struct hui_attribute { struct attribute attr; ssize_t (*show)(struct hui *hui, char *buf); ssize_t (*store)(struct hui *hui, const char *buf, size_t count); }; static void hui_release(struct kobject *kobj) { kfree(kobj); } static ssize_t hui_attr_show(struct kobject *kobj, struct attribute *attribute, char *buf) { struct colored_attribute *attr = to_colored_attr(attribute); struct hui *obj = (void *)kobj; ssize_t ret; if (!attr->show) return -EIO; mutex_lock(&hui_update_mutex); ret = attr->show(&obj->obj, buf); mutex_unlock(&hui_update_mutex); return ret; } static ssize_t hui_attr_store(struct kobject *kobj, struct attribute *attribute, const char *buf, size_t count) { struct colored_attribute *attr = to_colored_attr(attribute); struct hui *obj = (void *)kobj; ssize_t ret; if (!attr->store) return -EIO; mutex_lock(&hui_update_mutex); ret = attr->store(&obj->obj, buf, count); mutex_unlock(&hui_update_mutex); return ret; } static ssize_t brightness_show(struct hui *hui, char *buf) { return sprintf(buf, "%d\n", hui->brightness); } static ssize_t brightness_store(struct hui *hui, const char *buf, size_t count) { int err; u8 b; err = kstrtou8(buf, 10, &b); if (err) return err; if (b > 100) return -EINVAL; hui->brightness = b; hui_schedule_update(); return count; } static struct hui_attribute brightness_attribute = __ATTR(brightness, 0644, brightness_show, brightness_store); static struct attribute *hui_default_attrs[] = { &brightness_attribute.attr, NULL, }; static const struct sysfs_ops hui_sysfs_ops = { .show = hui_attr_show, .store = hui_attr_store, }; struct kobj_type hui_ktype = { .sysfs_ops = &hui_sysfs_ops, .release = hui_release, .default_attrs = hui_default_attrs, }; static const struct of_device_id hui_match[] = { { .compatible = "avm,hui" }, {}, }; MODULE_DEVICE_TABLE(of, hui_match); static int hui_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pinctrl *pinctrl; pinctrl = devm_pinctrl_get(dev); /* * hui does not need the pinctrl but this is a nice shortcut, so that * sub-drivers don't need to */ if (!IS_ERR(pinctrl)) { struct pinctrl_state *state; state = pinctrl_lookup_state(pinctrl, PINCTRL_STATE_DEFAULT); if (!IS_ERR(state)) { pinctrl_select_state(pinctrl, state); } } /* * Probe our subnodes to populate the actual led/button drivers */ return of_platform_populate(dev->of_node, hui_match, NULL, dev); } static struct platform_driver hui_plat = { .probe = hui_probe, .driver = { .name = "avm,hui", .of_match_table = hui_match, }, }; static struct proc_dir_entry *avm_hui_proc; __init int hui_sysfs_init(void) { int i, err; struct device_node *node; avm_hui_proc = proc_mkdir("avm/hui", NULL); if (!avm_hui_proc) { return -ENOMEM; } hui_kobj = kzalloc(sizeof(struct colored_kobject), GFP_KERNEL); if (!hui_kobj) return -ENOMEM; for (i = 0; i < LED_NUM_COLOR_NAMES; i++) { hui_kobj->obj.colors[i].brightness = 0xFF; hui_kobj->obj.colors[i].c[0] = 0xFF; hui_kobj->obj.colors[i].c[1] = 0xFF; hui_kobj->obj.colors[i].c[2] = 0xFF; } hui_kobj->brightness = 100; err = kobject_init_and_add(&hui_kobj->obj.kobj, &hui_ktype, kernel_kobj, "hui"); if (err) { kfree(hui_kobj); return err; } node = of_find_compatible_node(NULL, NULL, "avm,hui"); if (node) { colored_kobject_init_from_dt(&hui_kobj->obj, node); } err = sysfs_create_group(&hui_kobj->obj.kobj, &colored_attribute_group); if (err) { kobject_put(&hui_kobj->obj.kobj); return err; } err = platform_driver_register(&hui_plat); if (err) { kobject_put(&hui_kobj->obj.kobj); return err; } return 0; } void hui_sysfs_exit(void) { platform_driver_unregister(&hui_plat); kobject_put(&hui_kobj->obj.kobj); proc_remove(avm_hui_proc); } static inline ssize_t color_show(struct colored_kobject *obj, enum led_color_name name, char *buf) { struct led_color color = obj->colors[name]; if (!led_color_valid(color)) return -ENOENT; return sprintf(buf, "%02X%02X%02X\n", color.c[0], color.c[1], color.c[2]); } static inline ssize_t color_store(struct colored_kobject *kobj, enum led_color_name name, const char *buf, size_t count) { int i, err; u32 raw_color; if (sysfs_streq(buf, "") || sysfs_streq(buf, "none") || sysfs_streq(buf, "")) { kobj->colors[name].c[0] = 0; kobj->colors[name].c[1] = 0; kobj->colors[name].c[2] = 0; hui_schedule_update(); return count; } err = kstrtou32(buf, 16, &raw_color); if (err) return err; // Only use lower 24 bits if (raw_color & 0xFF000000) return -EINVAL; // Extract lower 24 bits for (i = 0; i < 3; i++) { kobj->colors[name].c[i] = (raw_color >> (16 - i * 8)) & 0xFF; } hui_schedule_update(); return count; } #define COLOR(name) \ static ssize_t color_show_##name(struct colored_kobject *obj, \ char *buf) \ { \ return color_show(obj, led_color_name_##name, buf); \ } \ static ssize_t color_store_##name(struct colored_kobject *obj, \ const char *buf, size_t count) \ { \ return color_store(obj, led_color_name_##name, buf, count); \ } \ static struct colored_attribute color_attribute_##name = \ __ATTR(name, 0644, color_show_##name, color_store_##name); #include "../def/colors.h" const struct attribute_group colored_attribute_group = { .name = "colors", .attrs = (struct attribute *[]){ #define COLOR(name) &color_attribute_##name.attr, #include "../def/colors.h" NULL, }, }; struct name_to_idx { const char *name; enum led_color_name idx; }; static const struct name_to_idx name_to_idx[] = { #define COLOR(_name) { .name = #_name, .idx = led_color_name_##_name }, #include "../def/colors.h" }; int colored_kobject_init_from_dt(struct colored_kobject *kobj, struct device_node *node) { struct device_node *color_node; int i; color_node = of_get_child_by_name(node, "colors"); // Simply not present if (!color_node) return 0; for (i = 0; i < ARRAY_SIZE(name_to_idx); i++) { struct led_color *color; int count, j; u32 temp; count = of_property_count_u32_elems(color_node, name_to_idx[i].name); if (count <= 0 || count > 3) continue; color = kobj->colors + name_to_idx[i].idx; color->brightness = 0xFF; memset(color->c, 0, 3); for (j = 0; j < count; j++) { of_property_read_u32_index( color_node, name_to_idx[i].name, j, &temp); color->c[j] = temp; } } of_node_put(color_node); return 0; }