root/drivers/staging/greybus/audio_manager_module.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Greybus operations
 *
 * Copyright 2015-2016 Google Inc.
 */

#include <linux/slab.h>

#include "audio_manager.h"
#include "audio_manager_private.h"

#define to_gb_audio_module_attr(x)      \
                container_of(x, struct gb_audio_manager_module_attribute, attr)

static inline struct gb_audio_manager_module *to_gb_audio_module(struct kobject *kobj)
{
        return container_of(kobj, struct gb_audio_manager_module, kobj);
}

struct gb_audio_manager_module_attribute {
        struct attribute attr;
        ssize_t (*show)(struct gb_audio_manager_module *module,
                        struct gb_audio_manager_module_attribute *attr,
                        char *buf);
        ssize_t (*store)(struct gb_audio_manager_module *module,
                         struct gb_audio_manager_module_attribute *attr,
                         const char *buf, size_t count);
};

static ssize_t gb_audio_module_attr_show(struct kobject *kobj,
                                         struct attribute *attr, char *buf)
{
        struct gb_audio_manager_module_attribute *attribute;
        struct gb_audio_manager_module *module;

        attribute = to_gb_audio_module_attr(attr);
        module = to_gb_audio_module(kobj);

        if (!attribute->show)
                return -EIO;

        return attribute->show(module, attribute, buf);
}

static ssize_t gb_audio_module_attr_store(struct kobject *kobj,
                                          struct attribute *attr,
                                          const char *buf, size_t len)
{
        struct gb_audio_manager_module_attribute *attribute;
        struct gb_audio_manager_module *module;

        attribute = to_gb_audio_module_attr(attr);
        module = to_gb_audio_module(kobj);

        if (!attribute->store)
                return -EIO;

        return attribute->store(module, attribute, buf, len);
}

static const struct sysfs_ops gb_audio_module_sysfs_ops = {
        .show = gb_audio_module_attr_show,
        .store = gb_audio_module_attr_store,
};

static void gb_audio_module_release(struct kobject *kobj)
{
        struct gb_audio_manager_module *module = to_gb_audio_module(kobj);

        pr_info("Destroying audio module #%d\n", module->id);
        /* TODO -> delete from list */
        kfree(module);
}

static ssize_t gb_audio_module_name_show(struct gb_audio_manager_module *module,
                                         struct gb_audio_manager_module_attribute *attr, char *buf)
{
        return sprintf(buf, "%s", module->desc.name);
}

static struct gb_audio_manager_module_attribute gb_audio_module_name_attribute =
        __ATTR(name, 0664, gb_audio_module_name_show, NULL);

static ssize_t gb_audio_module_vid_show(struct gb_audio_manager_module *module,
                                        struct gb_audio_manager_module_attribute *attr, char *buf)
{
        return sprintf(buf, "%d", module->desc.vid);
}

static struct gb_audio_manager_module_attribute gb_audio_module_vid_attribute =
        __ATTR(vid, 0664, gb_audio_module_vid_show, NULL);

static ssize_t gb_audio_module_pid_show(struct gb_audio_manager_module *module,
                                        struct gb_audio_manager_module_attribute *attr, char *buf)
{
        return sprintf(buf, "%d", module->desc.pid);
}

static struct gb_audio_manager_module_attribute gb_audio_module_pid_attribute =
        __ATTR(pid, 0664, gb_audio_module_pid_show, NULL);

static ssize_t gb_audio_module_intf_id_show(struct gb_audio_manager_module *module,
                                            struct gb_audio_manager_module_attribute *attr,
                                            char *buf)
{
        return sprintf(buf, "%d", module->desc.intf_id);
}

static struct gb_audio_manager_module_attribute
                                        gb_audio_module_intf_id_attribute =
        __ATTR(intf_id, 0664, gb_audio_module_intf_id_show, NULL);

static ssize_t gb_audio_module_ip_devices_show(struct gb_audio_manager_module *module,
                                               struct gb_audio_manager_module_attribute *attr,
                                               char *buf)
{
        return sprintf(buf, "0x%X", module->desc.ip_devices);
}

static struct gb_audio_manager_module_attribute
                                        gb_audio_module_ip_devices_attribute =
        __ATTR(ip_devices, 0664, gb_audio_module_ip_devices_show, NULL);

static ssize_t gb_audio_module_op_devices_show(struct gb_audio_manager_module *module,
                                               struct gb_audio_manager_module_attribute *attr,
                                               char *buf)
{
        return sprintf(buf, "0x%X", module->desc.op_devices);
}

static struct gb_audio_manager_module_attribute
                                        gb_audio_module_op_devices_attribute =
        __ATTR(op_devices, 0664, gb_audio_module_op_devices_show, NULL);

static struct attribute *gb_audio_module_default_attrs[] = {
        &gb_audio_module_name_attribute.attr,
        &gb_audio_module_vid_attribute.attr,
        &gb_audio_module_pid_attribute.attr,
        &gb_audio_module_intf_id_attribute.attr,
        &gb_audio_module_ip_devices_attribute.attr,
        &gb_audio_module_op_devices_attribute.attr,
        NULL,   /* need to NULL terminate the list of attributes */
};
ATTRIBUTE_GROUPS(gb_audio_module_default);

static const struct kobj_type gb_audio_module_type = {
        .sysfs_ops = &gb_audio_module_sysfs_ops,
        .release = gb_audio_module_release,
        .default_groups = gb_audio_module_default_groups,
};

static void send_add_uevent(struct gb_audio_manager_module *module)
{
        char name_string[128];
        char vid_string[64];
        char pid_string[64];
        char intf_id_string[64];
        char ip_devices_string[64];
        char op_devices_string[64];

        char *envp[] = {
                name_string,
                vid_string,
                pid_string,
                intf_id_string,
                ip_devices_string,
                op_devices_string,
                NULL
        };

        snprintf(name_string, 128, "NAME=%s", module->desc.name);
        snprintf(vid_string, 64, "VID=%d", module->desc.vid);
        snprintf(pid_string, 64, "PID=%d", module->desc.pid);
        snprintf(intf_id_string, 64, "INTF_ID=%d", module->desc.intf_id);
        snprintf(ip_devices_string, 64, "I/P DEVICES=0x%X",
                 module->desc.ip_devices);
        snprintf(op_devices_string, 64, "O/P DEVICES=0x%X",
                 module->desc.op_devices);

        kobject_uevent_env(&module->kobj, KOBJ_ADD, envp);
}

int gb_audio_manager_module_create(struct gb_audio_manager_module **module,
                                   struct kset *manager_kset,
                                   int id, struct gb_audio_manager_module_descriptor *desc)
{
        int err;
        struct gb_audio_manager_module *m;

        m = kzalloc_obj(*m, GFP_ATOMIC);
        if (!m)
                return -ENOMEM;

        /* Initialize the node */
        INIT_LIST_HEAD(&m->list);

        /* Set the module id */
        m->id = id;

        /* Copy the provided descriptor */
        memcpy(&m->desc, desc, sizeof(*desc));

        /* set the kset */
        m->kobj.kset = manager_kset;

        /*
         * Initialize and add the kobject to the kernel.  All the default files
         * will be created here.  As we have already specified a kset for this
         * kobject, we don't have to set a parent for the kobject, the kobject
         * will be placed beneath that kset automatically.
         */
        err = kobject_init_and_add(&m->kobj, &gb_audio_module_type, NULL, "%d",
                                   id);
        if (err) {
                pr_err("failed initializing kobject for audio module #%d\n", id);
                kobject_put(&m->kobj);
                return err;
        }

        /*
         * Notify the object was created
         */
        send_add_uevent(m);

        *module = m;
        pr_info("Created audio module #%d\n", id);
        return 0;
}

void gb_audio_manager_module_dump(struct gb_audio_manager_module *module)
{
        pr_info("audio module #%d name=%s vid=%d pid=%d intf_id=%d i/p devices=0x%X o/p devices=0x%X\n",
                module->id,
                module->desc.name,
                module->desc.vid,
                module->desc.pid,
                module->desc.intf_id,
                module->desc.ip_devices,
                module->desc.op_devices);
}