// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include "rt-flashlight.h" static const char * const flashlight_type_string[] = { [FLASHLIGHT_TYPE_XENON] = "Xenon", [FLASHLIGHT_TYPE_LED] = "LED", [FLASHLIFHT_TYPE_BULB] = "Bulb", }; static const char * const flashlight_mode_string[] = { [FLASHLIGHT_MODE_OFF] = "Off", [FLASHLIGHT_MODE_TORCH] = "Torch", [FLASHLIGHT_MODE_FLASH] = "Flash", [FLASHLIGHT_MODE_MIXED] = "Mixed", [FLASHLIGHT_MODE_DUAL_FLASH] = "Flash", [FLASHLIGHT_MODE_DUAL_TORCH] = "Torch", [FLASHLIGHT_MODE_DUAL_OFF] = "Off", }; static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); return scnprintf(buf, PAGE_SIZE, "%s\n", flashlight_dev->props.alias_name ? flashlight_dev->props.alias_name : "anonymous"); } static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); int size = ARRAY_SIZE(flashlight_type_string); if ((flashlight_dev->props.type < 0) || (flashlight_dev->props.type >= size)) return -EINVAL; return scnprintf(buf, PAGE_SIZE, "%s\n", flashlight_type_string[flashlight_dev->props.type]); } static ssize_t mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); int size = ARRAY_SIZE(flashlight_mode_string); if ((flashlight_dev->props.mode < 0) || (flashlight_dev->props.mode >= size)) return -EINVAL; return scnprintf(buf, PAGE_SIZE, "%s\n", flashlight_mode_string[flashlight_dev->props.mode]); } static ssize_t torch_max_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", flashlight_dev->props.torch_max_brightness); } static ssize_t strobe_max_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", flashlight_dev->props.strobe_max_brightness); } static ssize_t color_temperature_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", flashlight_dev->props.color_temperature); } static ssize_t strobe_delay_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", flashlight_dev->props.strobe_delay); } static ssize_t strobe_timeout_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc; struct flashlight_device *flashlight_dev = to_flashlight_device(dev); long timeout; rc = kstrtol(buf, 0, &timeout); if (rc) return rc; rc = flashlight_dev->ops->set_strobe_timeout(flashlight_dev, timeout); if (rc == 0) rc = count; return rc; } static ssize_t strobe_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", flashlight_dev->props.strobe_timeout); } static ssize_t torch_brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc; struct flashlight_device *flashlight_dev = to_flashlight_device(dev); long brightness; rc = kstrtol(buf, 0, &brightness); if (rc) return rc; rc = -ENXIO; mutex_lock(&flashlight_dev->ops_lock); if (flashlight_dev->ops && flashlight_dev->ops->set_torch_brightness) { if (brightness > flashlight_dev->props.torch_max_brightness) rc = -EINVAL; else { pr_debug("flashlight: set torch brightness to %ld\n", brightness); flashlight_dev->props.torch_brightness = brightness; flashlight_dev->ops->set_torch_brightness( flashlight_dev, brightness); rc = count; } } mutex_unlock(&flashlight_dev->ops_lock); return rc; } static ssize_t torch_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", flashlight_dev->props.torch_brightness); } static ssize_t strobe_brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc; struct flashlight_device *flashlight_dev = to_flashlight_device(dev); long brightness; rc = kstrtol(buf, 0, &brightness); if (rc) return rc; rc = -ENXIO; mutex_lock(&flashlight_dev->ops_lock); if (flashlight_dev->ops && flashlight_dev->ops->set_strobe_brightness) { if (brightness > flashlight_dev->props.strobe_max_brightness) rc = -EINVAL; else { pr_debug("flashlight: set strobe brightness to %ld\n", brightness); flashlight_dev->props.strobe_brightness = brightness; flashlight_dev->ops->set_strobe_brightness( flashlight_dev, brightness); rc = count; } } mutex_unlock(&flashlight_dev->ops_lock); return rc; } static ssize_t strobe_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", flashlight_dev->props.strobe_brightness); } static struct class *flashlight_class; static int flashlight_suspend(struct device *dev) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); pm_message_t state = {}; if (flashlight_dev->ops) flashlight_dev->ops->suspend(flashlight_dev, state); return 0; } static int flashlight_resume(struct device *dev) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); if (flashlight_dev->ops) flashlight_dev->ops->resume(flashlight_dev); return 0; } static void flashlight_device_release(struct device *dev) { struct flashlight_device *flashlight_dev = to_flashlight_device(dev); kfree(flashlight_dev); } static DEVICE_ATTR_RO(name); static DEVICE_ATTR_RO(type); static DEVICE_ATTR_RO(mode); static DEVICE_ATTR_RO(torch_max_brightness); static DEVICE_ATTR_RO(strobe_max_brightness); static DEVICE_ATTR_RO(color_temperature); static DEVICE_ATTR_RO(strobe_delay); static DEVICE_ATTR_RW(strobe_timeout); static DEVICE_ATTR_RW(torch_brightness); static DEVICE_ATTR_RW(strobe_brightness); static struct attribute *flashlight_class_attrs[] = { &dev_attr_name.attr, &dev_attr_type.attr, &dev_attr_mode.attr, &dev_attr_torch_max_brightness.attr, &dev_attr_strobe_max_brightness.attr, &dev_attr_color_temperature.attr, &dev_attr_strobe_delay.attr, &dev_attr_strobe_timeout.attr, &dev_attr_torch_brightness.attr, &dev_attr_strobe_brightness.attr, NULL, }; static const struct attribute_group flashlight_group = { .attrs = flashlight_class_attrs, }; static const struct attribute_group *flashlight_groups[] = { &flashlight_group, NULL, }; /** * flashlight_device_register - create and register a new object of * flashlight_device class. * @name: the name of the new object(must be the same as the name of the * respective framebuffer device). * @parent: a pointer to the parent device * @devdata: an optional pointer to be stored for private driver use. The * methods may retrieve it by using flashlight_get_data(flashlight_dev). * @ops: the flashlight operations structure. * * Creates and registers new flashlight device. Returns either an * ERR_PTR() or a pointer to the newly allocated device. */ struct flashlight_device *flashlight_device_register(const char *name, struct device *parent, void *devdata, const struct flashlight_ops *ops, const struct flashlight_properties *props) { struct flashlight_device *flashlight_dev; int rc; pr_debug("%s: name=%s\n", __func__, name); flashlight_dev = kzalloc(sizeof(*flashlight_dev), GFP_KERNEL); if (!flashlight_dev) return ERR_PTR(-ENOMEM); mutex_init(&flashlight_dev->ops_lock); flashlight_dev->dev.class = flashlight_class; flashlight_dev->dev.parent = parent; flashlight_dev->dev.release = flashlight_device_release; dev_set_name(&flashlight_dev->dev, name); dev_set_drvdata(&flashlight_dev->dev, devdata); /* Copy properties */ if (props) { memcpy(&flashlight_dev->props, props, sizeof(struct flashlight_properties)); } rc = device_register(&flashlight_dev->dev); if (rc) { kfree(flashlight_dev); return ERR_PTR(rc); } flashlight_dev->ops = ops; return flashlight_dev; } EXPORT_SYMBOL(flashlight_device_register); /** * flashlight_device_unregister - unregisters a flashlight device object. * @flashlight_dev: the flashlight device object to be unregistered and freed. * * Unregisters a previously registered via flashlight_device_register object. */ void flashlight_device_unregister(struct flashlight_device *flashlight_dev) { if (!flashlight_dev) return; mutex_lock(&flashlight_dev->ops_lock); flashlight_dev->ops = NULL; mutex_unlock(&flashlight_dev->ops_lock); device_unregister(&flashlight_dev->dev); } EXPORT_SYMBOL(flashlight_device_unregister); int flashlight_list_color_temperature( struct flashlight_device *flashlight_dev, int selector) { if (flashlight_dev->ops && flashlight_dev->ops->list_color_temperature) { return flashlight_dev->ops->list_color_temperature( flashlight_dev, selector); } return -EINVAL; } EXPORT_SYMBOL(flashlight_list_color_temperature); int flashlight_set_color_temperature( struct flashlight_device *flashlight_dev, int minK, int maxK) { int selector = 0; int rc; if ((flashlight_dev->ops == NULL) || (flashlight_dev->ops->set_color_temperature == NULL)) return -EINVAL; for (selector = 0; ; selector++) { rc = flashlight_list_color_temperature(flashlight_dev, selector); if (rc < 0) return rc; if (rc >= minK && rc <= maxK) { mutex_lock(&flashlight_dev->ops_lock); rc = flashlight_dev->ops->set_color_temperature( flashlight_dev, rc); mutex_unlock(&flashlight_dev->ops_lock); if (rc == 0) flashlight_dev->props.color_temperature = rc; return rc; } } return -EINVAL; } EXPORT_SYMBOL(flashlight_set_color_temperature); int flashlight_set_torch_brightness( struct flashlight_device *flashlight_dev, int brightness_level) { int rc; if ((flashlight_dev->ops == NULL) || (flashlight_dev->ops->set_torch_brightness == NULL)) return -EINVAL; if (brightness_level > flashlight_dev->props.torch_max_brightness) return -EINVAL; mutex_lock(&flashlight_dev->ops_lock); rc = flashlight_dev->ops->set_torch_brightness(flashlight_dev, brightness_level); mutex_unlock(&flashlight_dev->ops_lock); if (rc < 0) return rc; flashlight_dev->props.torch_brightness = brightness_level; return rc; } EXPORT_SYMBOL(flashlight_set_torch_brightness); int flashlight_set_strobe_brightness( struct flashlight_device *flashlight_dev, int brightness_level) { int rc; if ((flashlight_dev->ops == NULL) || (flashlight_dev->ops->set_strobe_brightness == NULL)) return -EINVAL; if (brightness_level > flashlight_dev->props.strobe_max_brightness) return -EINVAL; mutex_lock(&flashlight_dev->ops_lock); rc = flashlight_dev->ops->set_strobe_brightness(flashlight_dev, brightness_level); mutex_unlock(&flashlight_dev->ops_lock); if (rc < 0) return rc; flashlight_dev->props.strobe_brightness = brightness_level; return rc; } EXPORT_SYMBOL(flashlight_set_strobe_brightness); int flashlight_list_strobe_timeout( struct flashlight_device *flashlight_dev, int selector) { if (flashlight_dev->ops && flashlight_dev->ops->list_strobe_timeout) { return flashlight_dev->ops->list_strobe_timeout(flashlight_dev, selector); } return -EINVAL; } EXPORT_SYMBOL(flashlight_list_strobe_timeout); int flashlight_set_strobe_timeout( struct flashlight_device *flashlight_dev, int min_ms, int max_ms) { int selector = 0; int rc = -EINVAL; int timeout; if ((flashlight_dev->ops == NULL) || (flashlight_dev->ops->set_strobe_timeout == NULL)) return -EINVAL; for (selector = 0; ; selector++) { timeout = flashlight_list_strobe_timeout(flashlight_dev, selector); if (timeout < 0) return timeout; if (timeout >= min_ms && timeout <= max_ms) { mutex_lock(&flashlight_dev->ops_lock); rc = flashlight_dev->ops->set_strobe_timeout( flashlight_dev, timeout); mutex_unlock(&flashlight_dev->ops_lock); if (rc == 0) flashlight_dev->props.strobe_timeout = timeout; return rc; } } return -EINVAL; } EXPORT_SYMBOL(flashlight_set_strobe_timeout); int flashlight_set_mode(struct flashlight_device *flashlight_dev, int mode) { int rc; if (mode >= FLASHLIGHT_MODE_MAX || mode < 0) return -EINVAL; if ((flashlight_dev->ops == NULL) || (flashlight_dev->ops->set_mode == NULL)) { flashlight_dev->props.mode = mode; return 0; } mutex_lock(&flashlight_dev->ops_lock); rc = flashlight_dev->ops->set_mode(flashlight_dev, mode); mutex_unlock(&flashlight_dev->ops_lock); if (rc < 0) return rc; flashlight_dev->props.mode = mode; return rc; } EXPORT_SYMBOL(flashlight_set_mode); int flashlight_strobe(struct flashlight_device *flashlight_dev) { if (flashlight_dev->props.mode == FLASHLIGHT_MODE_FLASH || flashlight_dev->props.mode == FLASHLIGHT_MODE_MIXED) { if (flashlight_dev->ops == NULL || flashlight_dev->ops->strobe == NULL) return -EINVAL; return flashlight_dev->ops->strobe(flashlight_dev); } return -EINVAL; } EXPORT_SYMBOL(flashlight_strobe); int flashlight_is_ready(struct flashlight_device *flashlight_dev) { if (flashlight_dev->ops == NULL || flashlight_dev->ops->is_ready == NULL) return -EINVAL; return flashlight_dev->ops->is_ready(flashlight_dev); } EXPORT_SYMBOL(flashlight_is_ready); static int flashlight_match_device_by_name(struct device *dev, const void *data) { const char *name = data; return strcmp(dev_name(dev), name) == 0; } struct flashlight_device *find_flashlight_by_name(const char *name) { struct device *dev; if (!name) return (struct flashlight_device *)NULL; dev = class_find_device(flashlight_class, NULL, name, flashlight_match_device_by_name); return dev ? to_flashlight_device(dev) : NULL; } EXPORT_SYMBOL(find_flashlight_by_name); int flashlight_strobe_charge(struct flashlight_device *flashlight_dev, flashlight_charge_event_cb cb, void *data, int start) { if (flashlight_dev->ops->strobe_charge) return flashlight_dev->ops->strobe_charge(flashlight_dev, cb, data, start); if (flashlight_dev->props.type == FLASHLIGHT_TYPE_LED) { if (cb) cb(data, 0); return 0; } return -EINVAL; } EXPORT_SYMBOL(flashlight_strobe_charge); static SIMPLE_DEV_PM_OPS(flashlight_pm_ops, flashlight_suspend, flashlight_resume); static void __exit flashlight_class_exit(void) { class_destroy(flashlight_class); } static int __init flashlight_class_init(void) { flashlight_class = class_create(THIS_MODULE, "flashlight"); if (IS_ERR(flashlight_class)) { pr_info("Unable to create flashlight class; errno = %ld\n", PTR_ERR(flashlight_class)); return PTR_ERR(flashlight_class); } flashlight_class->dev_groups = flashlight_groups; flashlight_class->pm = &flashlight_pm_ops; return 0; } subsys_initcall(flashlight_class_init); module_exit(flashlight_class_exit); MODULE_DESCRIPTION("Flashlight Class Device"); MODULE_AUTHOR("Patrick Chang "); MODULE_VERSION("1.0.2_G"); MODULE_LICENSE("GPL");