greybus: audio: Add Audio Manager
This is a simple module that keeps a list of connected GB audio modules. Whenever a device is attached, an appropriate uevent is sent to userspace: UDEV [4941.803215] add /kernel/gb_audio_manager/0 (gb_audio_manager) ACTION=add CPORT=99 DEVICES=0x10 DEVPATH=/kernel/gb_audio_manager/0 NAME=naim PID=64 SEQNUM=1828 SLOT=2 SUBSYSTEM=gb_audio_manager USEC_INITIALIZED=802416 VID=128 And whenever removed: UDEV [4941.836588] remove /kernel/gb_audio_manager/0 (gb_audio_manager) ACTION=remove DEVPATH=/kernel/gb_audio_manager/0 SEQNUM=1833 SUBSYSTEM=gb_audio_manager USEC_INITIALIZED=835681 The API consists of functions for adding, removing and inspecting added device module descriptions (struct gb_audio_module): int gb_audio_manager_add(struct gb_audio_module_descriptor *desc); int gb_audio_manager_remove(int id); int gb_audio_manager_remove_all(void); struct gb_audio_module* gb_audio_manager_get_module(int id); void gb_audio_manager_put_module(struct gb_audio_module *module); int gb_audio_manager_dump_module(int id); void gb_audio_manager_dump_all(void); Devices can be inspected through sysfs in /sys/kernel/gb_audio_manager/{id}/* If GB_AUDIO_MANAGER_SYSFS is exported as 'true', managing devices can be done via the SYSFS as well. For instance: echo name=naim slot=2 vid=128 pid=64 cport=99 devices=0x10 > /sys/kernel/gb_audio_manager/add echo all > /sys/kernel/gb_audio_manager/dump echo 2 > /sys/kernel/gb_audio_manager/dump echo 2 > /sys/kernel/gb_audio_manager/remove Signed-off-by: Svetlin Ankov <ankov_svetlin@projectara.com> Signed-off-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Родитель
4dbf505640
Коммит
8db00736d3
|
@ -32,6 +32,8 @@ gb-arche-y := arche-platform.o arche-apb-ctrl.o
|
|||
gb-audio-codec-y := audio_codec.o
|
||||
gb-audio-gb-y := audio_gb.o
|
||||
gb-audio-apbridgea-y := audio_apbridgea.o
|
||||
gb-audio-manager-y += audio_manager.o
|
||||
gb-audio-manager-y += audio_manager_module.o
|
||||
gb-camera-y := camera.o
|
||||
|
||||
obj-m += greybus.o
|
||||
|
@ -48,6 +50,7 @@ obj-m += gb-audio-codec.o
|
|||
obj-m += gb-camera.o
|
||||
obj-m += gb-audio-gb.o
|
||||
obj-m += gb-audio-apbridgea.o
|
||||
obj-m += gb-audio-manager.o
|
||||
|
||||
KERNELVER ?= $(shell uname -r)
|
||||
KERNELDIR ?= /lib/modules/$(KERNELVER)/build
|
||||
|
@ -89,6 +92,12 @@ ccflags-y := -Wall
|
|||
# needed for trace events
|
||||
ccflags-y += -I$(src)
|
||||
|
||||
GB_AUDIO_MANAGER_SYSFS ?= true
|
||||
ifeq ($(GB_AUDIO_MANAGER_SYSFS),true)
|
||||
gb-audio-manager-y += audio_manager_sysfs.o
|
||||
ccflags-y += -DGB_AUDIO_MANAGER_SYSFS
|
||||
endif
|
||||
|
||||
all: module
|
||||
|
||||
tools::
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Greybus operations
|
||||
*
|
||||
* Copyright 2015-2016 Google Inc.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rwlock.h>
|
||||
|
||||
#include "audio_manager.h"
|
||||
#include "audio_manager_private.h"
|
||||
|
||||
static struct kset *manager_kset;
|
||||
|
||||
static LIST_HEAD(modules_list);
|
||||
static DEFINE_RWLOCK(modules_lock);
|
||||
|
||||
static int current_module_id;
|
||||
|
||||
/* helpers */
|
||||
static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id)
|
||||
{
|
||||
struct gb_audio_manager_module *module;
|
||||
|
||||
if (id < 0)
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(module, &modules_list, list) {
|
||||
if (module->id == id)
|
||||
return module;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* public API */
|
||||
int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc)
|
||||
{
|
||||
struct gb_audio_manager_module *module;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
err = gb_audio_manager_module_create(&module, manager_kset,
|
||||
current_module_id++, desc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Add it to the list */
|
||||
write_lock_irqsave(&modules_lock, flags);
|
||||
list_add_tail(&module->list, &modules_list);
|
||||
write_unlock_irqrestore(&modules_lock, flags);
|
||||
|
||||
return module->id;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gb_audio_manager_add);
|
||||
|
||||
int gb_audio_manager_remove(int id)
|
||||
{
|
||||
struct gb_audio_manager_module *module;
|
||||
unsigned long flags;
|
||||
|
||||
write_lock_irqsave(&modules_lock, flags);
|
||||
|
||||
module = gb_audio_manager_get_locked(id);
|
||||
if (!module) {
|
||||
write_unlock_irqrestore(&modules_lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_del(&module->list);
|
||||
kobject_put(&module->kobj);
|
||||
write_unlock_irqrestore(&modules_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gb_audio_manager_remove);
|
||||
|
||||
void gb_audio_manager_remove_all(void)
|
||||
{
|
||||
struct gb_audio_manager_module *module, *next;
|
||||
int is_empty = 1;
|
||||
unsigned long flags;
|
||||
|
||||
write_lock_irqsave(&modules_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(module, next, &modules_list, list) {
|
||||
list_del(&module->list);
|
||||
kobject_put(&module->kobj);
|
||||
}
|
||||
|
||||
is_empty = list_empty(&modules_list);
|
||||
|
||||
write_unlock_irqrestore(&modules_lock, flags);
|
||||
|
||||
if (!is_empty)
|
||||
pr_warn("Not all nodes were deleted\n");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all);
|
||||
|
||||
struct gb_audio_manager_module *gb_audio_manager_get_module(int id)
|
||||
{
|
||||
struct gb_audio_manager_module *module;
|
||||
unsigned long flags;
|
||||
|
||||
read_lock_irqsave(&modules_lock, flags);
|
||||
module = gb_audio_manager_get_locked(id);
|
||||
kobject_get(&module->kobj);
|
||||
read_unlock_irqrestore(&modules_lock, flags);
|
||||
return module;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gb_audio_manager_get_module);
|
||||
|
||||
void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
|
||||
{
|
||||
kobject_put(&module->kobj);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
|
||||
|
||||
int gb_audio_manager_dump_module(int id)
|
||||
{
|
||||
struct gb_audio_manager_module *module;
|
||||
unsigned long flags;
|
||||
|
||||
read_lock_irqsave(&modules_lock, flags);
|
||||
module = gb_audio_manager_get_locked(id);
|
||||
read_unlock_irqrestore(&modules_lock, flags);
|
||||
|
||||
if (!module)
|
||||
return -EINVAL;
|
||||
|
||||
gb_audio_manager_module_dump(module);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
|
||||
|
||||
void gb_audio_manager_dump_all(void)
|
||||
{
|
||||
struct gb_audio_manager_module *module;
|
||||
int count = 0;
|
||||
unsigned long flags;
|
||||
|
||||
read_lock_irqsave(&modules_lock, flags);
|
||||
list_for_each_entry(module, &modules_list, list) {
|
||||
gb_audio_manager_module_dump(module);
|
||||
count++;
|
||||
}
|
||||
read_unlock_irqrestore(&modules_lock, flags);
|
||||
|
||||
pr_info("Number of connected modules: %d\n", count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
|
||||
|
||||
/*
|
||||
* module init/deinit
|
||||
*/
|
||||
static int __init manager_init(void)
|
||||
{
|
||||
manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL,
|
||||
kernel_kobj);
|
||||
if (!manager_kset)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef GB_AUDIO_MANAGER_SYSFS
|
||||
gb_audio_manager_sysfs_init(&manager_kset->kobj);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit manager_exit(void)
|
||||
{
|
||||
gb_audio_manager_remove_all();
|
||||
kset_unregister(manager_kset);
|
||||
}
|
||||
|
||||
module_init(manager_init);
|
||||
module_exit(manager_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Greybus operations
|
||||
*
|
||||
* Copyright 2015-2016 Google Inc.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#ifndef _GB_AUDIO_MANAGER_H_
|
||||
#define _GB_AUDIO_MANAGER_H_
|
||||
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#define GB_AUDIO_MANAGER_NAME "gb_audio_manager"
|
||||
#define GB_AUDIO_MANAGER_MODULE_NAME_LEN 64
|
||||
#define GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "63"
|
||||
|
||||
struct gb_audio_manager_module_descriptor {
|
||||
char name[GB_AUDIO_MANAGER_MODULE_NAME_LEN];
|
||||
int slot;
|
||||
int vid;
|
||||
int pid;
|
||||
int cport;
|
||||
unsigned int devices;
|
||||
};
|
||||
|
||||
struct gb_audio_manager_module {
|
||||
struct kobject kobj;
|
||||
struct list_head list;
|
||||
int id;
|
||||
struct gb_audio_manager_module_descriptor desc;
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates a new gb_audio_manager_module_descriptor, using the specified
|
||||
* descriptor.
|
||||
*
|
||||
* Returns a negative result on error, or the id of the newly created module.
|
||||
*
|
||||
*/
|
||||
int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc);
|
||||
|
||||
/*
|
||||
* Removes a connected gb_audio_manager_module_descriptor for the specified ID.
|
||||
*
|
||||
* Returns zero on success, or a negative value on error.
|
||||
*/
|
||||
int gb_audio_manager_remove(int id);
|
||||
|
||||
/*
|
||||
* Removes all connected gb_audio_modules
|
||||
*
|
||||
* Returns zero on success, or a negative value on error.
|
||||
*/
|
||||
void gb_audio_manager_remove_all(void);
|
||||
|
||||
/*
|
||||
* Retrieves a gb_audio_manager_module_descriptor for the specified id.
|
||||
* Returns the gb_audio_manager_module_descriptor structure,
|
||||
* or NULL if there is no module with the specified ID.
|
||||
*/
|
||||
struct gb_audio_manager_module *gb_audio_manager_get_module(int id);
|
||||
|
||||
/*
|
||||
* Decreases the refcount of the module, obtained by the get function.
|
||||
* Modules are removed via gb_audio_manager_remove
|
||||
*/
|
||||
void gb_audio_manager_put_module(struct gb_audio_manager_module *module);
|
||||
|
||||
/*
|
||||
* Dumps the module for the specified id
|
||||
* Return 0 on success
|
||||
*/
|
||||
int gb_audio_manager_dump_module(int id);
|
||||
|
||||
/*
|
||||
* Dumps all connected modules
|
||||
*/
|
||||
void gb_audio_manager_dump_all(void);
|
||||
|
||||
#endif /* _GB_AUDIO_MANAGER_H_ */
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Greybus operations
|
||||
*
|
||||
* Copyright 2015-2016 Google Inc.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#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)
|
||||
#define to_gb_audio_module(x) \
|
||||
container_of(x, 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_slot_show(
|
||||
struct gb_audio_manager_module *module,
|
||||
struct gb_audio_manager_module_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d", module->desc.slot);
|
||||
}
|
||||
|
||||
static struct gb_audio_manager_module_attribute gb_audio_module_slot_attribute =
|
||||
__ATTR(slot, 0664, gb_audio_module_slot_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_cport_show(
|
||||
struct gb_audio_manager_module *module,
|
||||
struct gb_audio_manager_module_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d", module->desc.cport);
|
||||
}
|
||||
|
||||
static struct gb_audio_manager_module_attribute
|
||||
gb_audio_module_cport_attribute =
|
||||
__ATTR(cport, 0664, gb_audio_module_cport_show, NULL);
|
||||
|
||||
static ssize_t gb_audio_module_devices_show(
|
||||
struct gb_audio_manager_module *module,
|
||||
struct gb_audio_manager_module_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "0x%X", module->desc.devices);
|
||||
}
|
||||
|
||||
static struct gb_audio_manager_module_attribute
|
||||
gb_audio_module_devices_attribute =
|
||||
__ATTR(devices, 0664, gb_audio_module_devices_show, NULL);
|
||||
|
||||
static struct attribute *gb_audio_module_default_attrs[] = {
|
||||
&gb_audio_module_name_attribute.attr,
|
||||
&gb_audio_module_slot_attribute.attr,
|
||||
&gb_audio_module_vid_attribute.attr,
|
||||
&gb_audio_module_pid_attribute.attr,
|
||||
&gb_audio_module_cport_attribute.attr,
|
||||
&gb_audio_module_devices_attribute.attr,
|
||||
NULL, /* need to NULL terminate the list of attributes */
|
||||
};
|
||||
|
||||
static struct kobj_type gb_audio_module_type = {
|
||||
.sysfs_ops = &gb_audio_module_sysfs_ops,
|
||||
.release = gb_audio_module_release,
|
||||
.default_attrs = gb_audio_module_default_attrs,
|
||||
};
|
||||
|
||||
static void send_add_uevent(struct gb_audio_manager_module *module)
|
||||
{
|
||||
char name_string[128];
|
||||
char slot_string[64];
|
||||
char vid_string[64];
|
||||
char pid_string[64];
|
||||
char cport_string[64];
|
||||
char devices_string[64];
|
||||
|
||||
char *envp[] = {
|
||||
name_string,
|
||||
slot_string,
|
||||
vid_string,
|
||||
pid_string,
|
||||
cport_string,
|
||||
devices_string,
|
||||
NULL
|
||||
};
|
||||
|
||||
snprintf(name_string, 128, "NAME=%s", module->desc.name);
|
||||
snprintf(slot_string, 64, "SLOT=%d", module->desc.slot);
|
||||
snprintf(vid_string, 64, "VID=%d", module->desc.vid);
|
||||
snprintf(pid_string, 64, "PID=%d", module->desc.pid);
|
||||
snprintf(cport_string, 64, "CPORT=%d", module->desc.cport);
|
||||
snprintf(devices_string, 64, "DEVICES=0x%X", module->desc.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(sizeof(*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 slot=%d vid=%d pid=%d cport=%d devices=0x%X\n",
|
||||
module->id,
|
||||
module->desc.name,
|
||||
module->desc.slot,
|
||||
module->desc.vid,
|
||||
module->desc.pid,
|
||||
module->desc.cport,
|
||||
module->desc.devices);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Greybus operations
|
||||
*
|
||||
* Copyright 2015-2016 Google Inc.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#ifndef _GB_AUDIO_MANAGER_PRIVATE_H_
|
||||
#define _GB_AUDIO_MANAGER_PRIVATE_H_
|
||||
|
||||
#include <linux/kobject.h>
|
||||
|
||||
#include "audio_manager.h"
|
||||
|
||||
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);
|
||||
|
||||
/* module destroyed via kobject_put */
|
||||
|
||||
void gb_audio_manager_module_dump(struct gb_audio_manager_module *module);
|
||||
|
||||
/* sysfs control */
|
||||
void gb_audio_manager_sysfs_init(struct kobject *kobj);
|
||||
|
||||
#endif /* _GB_AUDIO_MANAGER_PRIVATE_H_ */
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Greybus operations
|
||||
*
|
||||
* Copyright 2015-2016 Google Inc.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "audio_manager.h"
|
||||
#include "audio_manager_private.h"
|
||||
|
||||
static ssize_t manager_sysfs_add_store(
|
||||
struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct gb_audio_manager_module_descriptor desc = { {0} };
|
||||
|
||||
int num = sscanf(buf,
|
||||
"name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s "
|
||||
"slot=%d vid=%d pid=%d cport=%d devices=0x%X",
|
||||
desc.name, &desc.slot, &desc.vid, &desc.pid,
|
||||
&desc.cport, &desc.devices);
|
||||
|
||||
if (num != 6)
|
||||
return -EINVAL;
|
||||
|
||||
num = gb_audio_manager_add(&desc);
|
||||
if (num < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct kobj_attribute manager_add_attribute =
|
||||
__ATTR(add, 0664, NULL, manager_sysfs_add_store);
|
||||
|
||||
static ssize_t manager_sysfs_remove_store(
|
||||
struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int id;
|
||||
|
||||
int num = sscanf(buf, "%d", &id);
|
||||
|
||||
if (num != 1)
|
||||
return -EINVAL;
|
||||
|
||||
num = gb_audio_manager_remove(id);
|
||||
if (num)
|
||||
return num;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct kobj_attribute manager_remove_attribute =
|
||||
__ATTR(remove, 0664, NULL, manager_sysfs_remove_store);
|
||||
|
||||
static ssize_t manager_sysfs_dump_store(
|
||||
struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int id;
|
||||
|
||||
int num = sscanf(buf, "%d", &id);
|
||||
|
||||
if (num == 1) {
|
||||
num = gb_audio_manager_dump_module(id);
|
||||
if (num)
|
||||
return num;
|
||||
} else if (!strncmp("all", buf, 3))
|
||||
gb_audio_manager_dump_all();
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct kobj_attribute manager_dump_attribute =
|
||||
__ATTR(dump, 0664, NULL, manager_sysfs_dump_store);
|
||||
|
||||
static void manager_sysfs_init_attribute(
|
||||
struct kobject *kobj, struct kobj_attribute *kattr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sysfs_create_file(kobj, &kattr->attr);
|
||||
if (err) {
|
||||
pr_warn("creating the sysfs entry for %s failed: %d\n",
|
||||
kattr->attr.name, err);
|
||||
}
|
||||
}
|
||||
|
||||
void gb_audio_manager_sysfs_init(struct kobject *kobj)
|
||||
{
|
||||
manager_sysfs_init_attribute(kobj, &manager_add_attribute);
|
||||
manager_sysfs_init_attribute(kobj, &manager_remove_attribute);
|
||||
manager_sysfs_init_attribute(kobj, &manager_dump_attribute);
|
||||
}
|
Загрузка…
Ссылка в новой задаче