| /** |
| * Copyright 2021 Google LLC. |
| */ |
| |
| #include <linux/device.h> |
| #include <linux/idr.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/sysfs.h> |
| |
| #include "ssr-core.h" |
| |
| static struct class *ssr_class; |
| static DEFINE_IDA(ssr_instance_idr); |
| |
| static ssize_t relay_open_store(struct device *dev, struct device_attribute *attr, |
| const char* buf, size_t count) { |
| struct ssr_data *data = dev_get_drvdata(dev); |
| |
| return data->ops->relay_open_store(dev->parent, buf, count); |
| } |
| |
| #define DEFINE_SSR_DEVICE_SHOW_FUNCTION(_name) \ |
| ssize_t _name##_show(struct device *dev, struct device_attribute *attr, char* buf) { \ |
| struct ssr_data *data = dev_get_drvdata(dev); \ |
| return data->ops->_name##_show(dev->parent, buf); \ |
| } |
| |
| static DEFINE_SSR_DEVICE_SHOW_FUNCTION(relay_state); |
| static DEFINE_SSR_DEVICE_SHOW_FUNCTION(hv_vdd); |
| static DEFINE_SSR_DEVICE_SHOW_FUNCTION(acin1_vdd); |
| static DEFINE_SSR_DEVICE_SHOW_FUNCTION(acin2_vdd); |
| |
| static DEVICE_ATTR_WO(relay_open); |
| static DEVICE_ATTR_RO(relay_state); |
| static DEVICE_ATTR_RO(hv_vdd); |
| static DEVICE_ATTR_RO(acin1_vdd); |
| static DEVICE_ATTR_RO(acin2_vdd); |
| static struct attribute *ssr_attrs[] = { |
| &dev_attr_relay_open.attr, |
| &dev_attr_relay_state.attr, |
| &dev_attr_hv_vdd.attr, |
| &dev_attr_acin1_vdd.attr, |
| &dev_attr_acin2_vdd.attr, |
| NULL, |
| }; |
| ATTRIBUTE_GROUPS(ssr); |
| |
| struct device *ssr_device_register(struct device *dev, const struct ssr_ops *ops) { |
| struct ssr_data *data; |
| struct device *new_dev; |
| int ret; |
| |
| data = kmalloc(sizeof(struct ssr_data), GFP_KERNEL); |
| if (!data) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| data->id = ida_alloc(&ssr_instance_idr, GFP_KERNEL); |
| if (data->id < 0) { |
| ret = data->id; |
| goto err_free_data; |
| } |
| |
| data->ops = ops; |
| |
| new_dev = device_create_with_groups(ssr_class, dev, 0, data, |
| ssr_groups, "ssr%d", data->id); |
| if (IS_ERR(new_dev)) { |
| ret = PTR_ERR(new_dev); |
| goto err_free_id; |
| } |
| return new_dev; |
| |
| err_free_id: |
| ida_free(&ssr_instance_idr, data->id); |
| err_free_data: |
| kfree(data); |
| err: |
| return ERR_PTR(ret); |
| } |
| EXPORT_SYMBOL_GPL(ssr_device_register); |
| |
| void ssr_device_unregister(struct device *ssr_dev) { |
| struct ssr_data *data = dev_get_drvdata(ssr_dev); |
| |
| ida_free(&ssr_instance_idr, data->id); |
| device_unregister(ssr_dev); |
| kfree(data); |
| } |
| EXPORT_SYMBOL_GPL(ssr_device_unregister); |
| |
| int __init ssr_core_init(void) { |
| ssr_class = class_create(THIS_MODULE, "ssr"); |
| if (IS_ERR(ssr_class)) { |
| pr_err("Failed to create ssr class %ld\n", PTR_ERR(ssr_class)); |
| return PTR_ERR(ssr_class); |
| } |
| |
| return 0; |
| } |
| |
| void __exit ssr_core_exit(void) { |
| class_destroy(ssr_class); |
| ssr_class = NULL; |
| } |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_DESCRIPTION("SSR core module"); |
| module_init(ssr_core_init); |
| module_exit(ssr_core_exit); |