| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright 2020 Google LLC. |
| */ |
| |
| #include <linux/idr.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| |
| #include "icr-core.h" |
| |
| static struct class *icr_class; |
| static DEFINE_IDA(icr_instance_ida); |
| |
| static ssize_t open_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct icr_data *data = dev_get_drvdata(dev); |
| |
| return data->ops->open_show(dev->parent, attr, buf); |
| } |
| |
| static ssize_t open_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| struct icr_data *data = dev_get_drvdata(dev); |
| |
| return data->ops->open_store(dev->parent, attr, buf, count); |
| } |
| |
| static DEVICE_ATTR_RW(open); |
| static struct attribute *icr_attrs[] = { |
| &dev_attr_open.attr, |
| NULL |
| }; |
| ATTRIBUTE_GROUPS(icr); |
| |
| struct device *icr_core_register(struct device *dev, const struct icr_ops *ops) |
| { |
| struct device *new_dev; |
| struct icr_data *icr_data; |
| int ret; |
| |
| icr_data = kmalloc(sizeof(*icr_data), GFP_KERNEL); |
| if (!icr_data) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| |
| icr_data->id = ida_simple_get(&icr_instance_ida, 0, 0, GFP_KERNEL); |
| if (icr_data->id < 0) { |
| pr_err("Failed to allocate id\n"); |
| ret = icr_data->id; |
| goto out_free_icr_data; |
| } |
| |
| icr_data->ops = ops; |
| |
| new_dev = device_create_with_groups(icr_class, dev, 0, icr_data, icr_groups, "icr%d", |
| icr_data->id); |
| if (IS_ERR(new_dev)) { |
| ret = PTR_ERR(new_dev); |
| goto out_free_id; |
| } |
| |
| return new_dev; |
| |
| out_free_id: |
| ida_simple_remove(&icr_instance_ida, icr_data->id); |
| out_free_icr_data: |
| kfree(icr_data); |
| out: |
| return ERR_PTR(ret); |
| } |
| EXPORT_SYMBOL_GPL(icr_core_register); |
| |
| void icr_core_unregister(struct device *icr_dev) |
| { |
| struct icr_data *data = dev_get_drvdata(icr_dev); |
| |
| ida_simple_remove(&icr_instance_ida, data->id); |
| device_unregister(icr_dev); |
| kfree(data); |
| } |
| EXPORT_SYMBOL_GPL(icr_core_unregister); |
| |
| int __init icr_core_init(void) |
| { |
| icr_class = class_create(THIS_MODULE, "icr"); |
| if (IS_ERR(icr_class)) { |
| pr_err("Failed to create icr class\n"); |
| return PTR_ERR(icr_class); |
| } |
| |
| return 0; |
| } |
| |
| void __exit icr_core_exit(void) |
| { |
| class_destroy(icr_class); |
| icr_class = NULL; |
| } |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_DESCRIPTION("ICR core module"); |
| module_init(icr_core_init); |
| module_exit(icr_core_exit); |