summaryrefslogtreecommitdiffstats
path: root/meta-agl-devel.md
blob: 84bcfd1e51cc7e5a18afa50ab0e06e483a6c4c60 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
## Introduction

The `meta-agl-devel` layer contains components that are being tested or
still in development.
The layer also contains software packages that Original Equipment
Manufacturers (OEMs) need but are not included in the AGL software.

## Sub-Layers

The `meta-agl-devel` layer contains the following files and sub-layers:

```
.
├── docs
├── LICENSE
├── LICENSE.GPL-2.0-only
├── LICENSE.MIT
├── meta-agl-devel.md
├── meta-agl-jailhouse
├── meta-agl-lxc
├── meta-agl-ros2
├── meta-oem-production-readiness
├── meta-speech-framework
├── README.md
└── templates
```

The following list provides a summary of these sub-layers:

* `meta-speech-framework`: Provides libraries and software packages needed by
  for speech recognition.

* `meta-agl-jailhouse`: Provides Jailhouse partitioning hypervisor and
  supporting packages.

* `meta-agl-lxc`: Provides LXC container support and example images.

* `meta-agl-ros2`: Provides ROS2 and additional YDLIDAR support.

* `meta-oem-production-readiness`: Provides libraries and software packages of
  IVI product readiness

* `meta-agl-flutter`: Provides Flutter support.

* `templates`: Feature templates that support the `meta-agl-devel` layer.
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
// SPDX-License-Identifier: GPL-2.0
/*
 * AVIRT - ALSA Virtual Soundcard
 *
 * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd
 *
 * core.c - AVIRT core internals
 */

#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <sound/initval.h>
#include <linux/platform_device.h>

#include "core.h"

MODULE_AUTHOR("James O'Shannessy <james.oshannessy@fiberdyne.com.au>");
MODULE_AUTHOR("Mark Farrugia <mark.farrugia@fiberdyne.com.au>");
MODULE_DESCRIPTION("ALSA virtual, dynamic soundcard");
MODULE_LICENSE("GPL v2");

#define D_LOGNAME "core"

#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args)
#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args)
#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args)

#define PCM_OPS_SET(pcm_ops_ap, pcm_ops, cb)                                   \
	((pcm_ops_ap->cb) ? ((*pcm_ops)->cb = pcm_ops_ap->cb) :                \
			    ((*pcm_ops)->cb = NULL));

#define SND_AVIRT_DRIVER "snd_avirt"

static struct snd_avirt_core core = {
	.version = { 0, 0, 1 },
	.stream_count = 0,
	.streams_sealed = false,
};

static LIST_HEAD(audiopath_list);

struct snd_avirt_audiopath_obj {
	struct kobject kobj;
	struct list_head list;
	struct snd_avirt_audiopath *path;
};

static struct kset *snd_avirt_audiopath_kset;
static struct kobject *kobj;

#define to_audiopath_obj(d)                                                    \
	container_of(d, struct snd_avirt_audiopath_obj, kobj)
#define to_audiopath_attr(a)                                                   \
	container_of(a, struct snd_avirt_audiopath_attribute, attr)

/**
 * struct snd_avirt_audiopath_attribute - access the attributes of Audio Path
 * @attr: attributes of an Audio Path
 * @show: pointer to the show function
 * @store: pointer to the store function
 */
struct snd_avirt_audiopath_attribute {
	struct attribute attr;
	ssize_t (*show)(struct snd_avirt_audiopath_obj *d,
			struct snd_avirt_audiopath_attribute *attr, char *buf);
	ssize_t (*store)(struct snd_avirt_audiopath_obj *d,
			 struct snd_avirt_audiopath_attribute *attr,
			 const char *buf, size_t count);
};

/**
 * audiopath_attr_show - show function of an Audio Path
 * @kobj: pointer to kobject
 * @attr: pointer to attribute struct
 * @buf: buffer
 */
static ssize_t audiopath_attr_show(struct kobject *kobj, struct attribute *attr,
				   char *buf)
{
	struct snd_avirt_audiopath_attribute *audiopath_attr;
	struct snd_avirt_audiopath_obj *audiopath_obj;

	audiopath_attr = to_audiopath_attr(attr);
	audiopath_obj = to_audiopath_obj(kobj);

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

	return audiopath_attr->show(audiopath_obj, audiopath_attr, buf);
}

/**
 * audiopath_attr_store - attribute store function of Audio Path object
 * @kobj: pointer to kobject
 * @attr: pointer to attribute struct
 * @buf: buffer
 * @len: length of buffer
 */
static ssize_t audiopath_attr_store(struct kobject *kobj,
				    struct attribute *attr, const char *buf,
				    size_t len)
{
	struct snd_avirt_audiopath_attribute *audiopath_attr;
	struct snd_avirt_audiopath_obj *audiopath_obj;

	audiopath_attr = to_audiopath_attr(attr);
	audiopath_obj = to_audiopath_obj(kobj);

	if (!audiopath_attr->store)
		return -EIO;
	return audiopath_attr->store(audiopath_obj, audiopath_attr, buf, len);
}

static const struct sysfs_ops snd_avirt_audiopath_sysfs_ops = {
	.show = audiopath_attr_show,
	.store = audiopath_attr_store,
};

/**
 * snd_avirt_audiopath_release - Audio Path release function
 * @kobj: pointer to Audio Paths's kobject
 */
static void snd_avirt_audiopath_release(struct kobject *kobj)
{
	struct snd_avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj);

	kfree(audiopath_obj);
}

static ssize_t
	audiopath_name_show(struct snd_avirt_audiopath_obj *audiopath_obj,
			    struct snd_avirt_audiopath_attribute *attr,
			    char *buf)
{
	return sprintf(buf, "%s\n", audiopath_obj->path->name);
}

static ssize_t
	audiopath_version_show(struct snd_avirt_audiopath_obj *audiopath_obj,
			       struct snd_avirt_audiopath_attribute *attr,
			       char *buf)
{
	struct snd_avirt_audiopath *audiopath = audiopath_obj->path;

	return sprintf(buf, "%d.%d.%d\n", audiopath->version[0],
		       audiopath->version[1], audiopath->version[2]);
}

static struct snd_avirt_audiopath_attribute snd_avirt_audiopath_attrs[] = {
	__ATTR_RO(audiopath_name),
	__ATTR_RO(audiopath_version),
};

static struct attribute *snd_avirt_audiopath_def_attrs[] = {
	&snd_avirt_audiopath_attrs[0].attr,
	&snd_avirt_audiopath_attrs[1].attr,
	NULL,
};

static struct kobj_type snd_avirt_audiopath_ktype = {
	.sysfs_ops = &snd_avirt_audiopath_sysfs_ops,
	.release = snd_avirt_audiopath_release,
	.default_attrs = snd_avirt_audiopath_def_attrs,
};

/**
 * create_snd_avirt_audiopath_obj - creates an Audio Path object
 * @uid: Unique ID of the Audio Path
 *
 * This creates an Audio Path object and assigns the kset and registers
 * it with sysfs.
 * @return: Pointer to the Audio Path object or NULL if it failed.
 */
static struct snd_avirt_audiopath_obj *
	create_snd_avirt_audiopath_obj(const char *uid)
{
	struct snd_avirt_audiopath_obj *snd_avirt_audiopath;
	int retval;

	snd_avirt_audiopath = kzalloc(sizeof(*snd_avirt_audiopath), GFP_KERNEL);
	if (!snd_avirt_audiopath)
		return NULL;
	snd_avirt_audiopath->kobj.kset = snd_avirt_audiopath_kset;
	retval = kobject_init_and_add(&snd_avirt_audiopath->kobj,
				      &snd_avirt_audiopath_ktype, kobj, "%s",
				      uid);
	if (retval) {
		kobject_put(&snd_avirt_audiopath->kobj);
		return NULL;
	}
	kobject_uevent(&snd_avirt_audiopath->kobj, KOBJ_ADD);
	return snd_avirt_audiopath;
}

/**
 * destroy_snd_avirt_audiopath_obj - destroys an Audio Path object
 * @name: the Audio Path object
 */
static void destroy_snd_avirt_audiopath_obj(struct snd_avirt_audiopath_obj *p)
{
	kobject_put(&p->kobj);
}

/**
 * pcm_private_data_free - callback function to free private data allocated to pcm
 * @pcm: the PCM with private data
 */
static void pcm_private_data_free(struct snd_pcm *pcm)
{
	struct snd_avirt_private_data *avirt_private_data;

	avirt_private_data = (struct snd_avirt_private_data *)pcm->private_data;
	if (avirt_private_data) {
		if (avirt_private_data->ap_private_data &&
		    avirt_private_data->ap_private_free)
			avirt_private_data->ap_private_free(pcm);
	}

	kfree(pcm->private_data);
}

static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream)
{
	struct snd_avirt_private_data *avirt_private_data;
	struct snd_avirt_audiopath *audiopath;
	struct snd_pcm_ops *pcm_ops, *pcm_ops_ap;
	struct snd_pcm *pcm;
	bool playback = false, capture = false;
	int err;

	audiopath = snd_avirt_audiopath_get(stream->map);
	if (!audiopath) {
		D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map);
		return ERR_PTR(-EFAULT);
	}

	if (!stream->direction) {
		pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops;
		playback = true;
	} else {
		pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops;
		capture = true;
	}
	if (!pcm_ops_ap) {
		D_ERRORK("No PCM ops for direction '%s' for Audio Path: %s",
			 (stream->direction) ? "capture" : "playback",
			 stream->map);
		return ERR_PTR(-EFAULT);
	}
	pcm_ops = &pcm_ops_avirt;

	/* Set PCM ops for the Audio Path*/
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, prepare);
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, trigger);
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, pointer);
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, get_time_info);
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, fill_silence);
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, copy_user);
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, copy_kernel);
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, mmap);
	PCM_OPS_SET(pcm_ops_ap, &pcm_ops, ack);

	/** Special case: loopback */
	if (!strcmp(stream->map, "ap_loopback")) {
		playback = true;
		capture = true;
	}

	err = snd_pcm_new(core.card, stream->name, stream->device, playback,
			  capture, &pcm);

	if (err < 0)
		return ERR_PTR(err);

	/** Register driver callbacks */
	if (playback)
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, pcm_ops);
	if (capture)
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, pcm_ops);

	pcm->info_flags = 0;
	strcpy(pcm->name, stream->name);

	avirt_private_data = kzalloc(sizeof(*avirt_private_data), GFP_KERNEL);
	pcm->private_data = avirt_private_data;
	// Set the private free function for the private user data
	pcm->private_free = pcm_private_data_free;

	snd_device_register(core.card, pcm);

	return pcm;
}

static int snd_avirt_streams_get(const char *map,
				 struct snd_avirt_stream_array *stream_array)
{
	struct list_head *entry;
	struct config_item *item;
	struct snd_avirt_stream *stream;

	list_for_each (entry, &core.stream_group->cg_children) {
		item = container_of(entry, struct config_item, ci_entry);
		stream = snd_avirt_stream_from_config_item(item);
		if (!strcmp(map, stream->map)) {
			stream_array->streams[stream_array->count++] = stream;
		}
	}

	return stream_array->count;
}

/**
 * snd_avirt_audiopath_get - retrieves the Audio Path by its UID
 * @uid: Unique ID for the Audio Path
 * @return: Corresponding Audio Path
 */
struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid)
{
	struct snd_avirt_audiopath_obj *ap_obj;

	list_for_each_entry(ap_obj, &audiopath_list, list) {
		// pr_info("get_ap %s\n", ap_obj->path->uid);
		if (!strcmp(ap_obj->path->uid, uid))
			return ap_obj->path;
	}

	return NULL;
}

/**
 * snd_avirt_audiopath_register - register Audio Path with AVIRT
 * @audiopath: Audio Path to be registered
 * @return: 0 on success or error code otherwise
 */
int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath)
{
	struct snd_avirt_audiopath_obj *audiopath_obj;
	struct snd_avirt_stream_array stream_array;

	if (!audiopath) {
		D_ERRORK("Audio Path is NULL!");
		return -EINVAL;
	}

	audiopath_obj = create_snd_avirt_audiopath_obj(audiopath->uid);
	if (!audiopath_obj) {
		D_INFOK("Failed to alloc driver object");
		return -ENOMEM;
	}
	audiopath_obj->path = audiopath;

	audiopath->context = audiopath_obj;
	D_INFOK("Registered new Audio Path: %s", audiopath->name);

	list_add_tail(&audiopath_obj->list, &audiopath_list);

	// If we have already sealed the streams, configure this AP
	if (core.streams_sealed) {
		stream_array.count = 0;
		if (snd_avirt_streams_get(audiopath->uid, &stream_array) > 0)
			audiopath->configure(core.card, &stream_array);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_register);

/**
 * snd_avirt_audiopath_deregister - deregister Audio Path with AVIRT
 * @audiopath: Audio Path to be deregistered
 * @return: 0 on success or error code otherwise
 */
int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath)
{
	struct snd_avirt_audiopath_obj *audiopath_obj;

	// Check if audio path is registered
	if (!audiopath) {
		D_ERRORK("Bad Audio Path Driver");
		return -EINVAL;
	}

	audiopath_obj = audiopath->context;
	if (!audiopath_obj) {
		D_INFOK("driver not registered");
		return -EINVAL;
	}

	list_del(&audiopath_obj->list);
	destroy_snd_avirt_audiopath_obj(audiopath_obj);
	D_INFOK("Deregistered Audio Path %s", audiopath->uid);

	return 0;
}
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_deregister);

/**
 * snd_avirt_stream_create - Create audio stream, including it's ALSA PCM device
 * @name: The name designated to the audio stream
 * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or
 *             SNDRV_PCM_STREAM_CAPTURE)
 * @return: The newly created audio stream if successful, or an error pointer
 */
struct snd_avirt_stream *snd_avirt_stream_create(const char *name,
						 int direction)
{
	struct snd_avirt_stream *stream;

	if ((core.stream_count + 1) > MAX_STREAMS) {
		D_ERRORK("Cannot add stream %s, PCMs are maxxed out!", name);
		return ERR_PTR(-EPERM);
	}

	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
	if (!stream)
		return ERR_PTR(-ENOMEM);

	strcpy(stream->name, name);
	strcpy(stream->map, "none");
	stream->channels = 0;
	stream->direction = direction;
	stream->device = core.stream_count++;

	D_INFOK("name: %s device:%d", name, stream->device);

	return stream;
}

int snd_avirt_streams_seal(void)
{
	int err = 0, i = 0;
	struct snd_avirt_audiopath_obj *ap_obj;
	struct snd_avirt_stream *stream;
	struct snd_avirt_stream_array stream_array;
	struct config_item *item;
	struct list_head *entry;

	if (core.streams_sealed) {
		D_ERRORK("streams are already sealed!");
		return -1;
	}

	list_for_each(entry, &core.stream_group->cg_children) {
		item = container_of(entry, struct config_item, ci_entry);
		stream = snd_avirt_stream_from_config_item(item);
		if (!stream)
			return -EFAULT;
		stream->pcm = pcm_create(stream);
		if (IS_ERR_OR_NULL(stream->pcm))
			return (PTR_ERR(stream->pcm));
	}

	list_for_each_entry(ap_obj, &audiopath_list, list) {
		D_INFOK("configure() AP uid: %s", ap_obj->path->uid);
		for (i = 0; i < MAX_STREAMS; i++)
			stream_array.streams[i] = NULL;
		stream_array.count = 0;
		if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) > 0)
			ap_obj->path->configure(core.card, &stream_array);
	}

	core.streams_sealed = true;

	return err;
}

bool snd_avirt_streams_sealed(void)
{
	return core.streams_sealed;
}

struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device)
{
	struct snd_avirt_stream *stream;
	struct config_item *item;
	struct list_head *entry;

	if (device >= core.stream_count) {
		D_ERRORK("Stream device number is larger than stream count");
		return ERR_PTR(-EINVAL);
	}

	list_for_each(entry, &core.stream_group->cg_children) {
		item = container_of(entry, struct config_item, ci_entry);
		stream = snd_avirt_stream_from_config_item(item);
		if (!stream)
			return ERR_PTR(-EFAULT);
		if (stream->device == device)
			return stream;
	}

	return NULL;
}

static int snd_avirt_core_probe(struct platform_device *devptr)
{
	int err;

	// Create the card instance
	err = snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt",
			   THIS_MODULE, 0, &core.card);
	if (err < 0) {
		D_ERRORK("Failed to create sound card");
		return err;
	}

	strncpy(core.card->driver, "avirt-alsa-dev", 16);
	strncpy(core.card->shortname, "avirt", 32);
	strncpy(core.card->longname, "A virtual sound card driver for ALSA",
		80);

	return 0;
}

static int snd_avirt_core_remove(struct platform_device *devptr)
{
	snd_card_free(core.card);

	return 0;
}

static struct platform_driver snd_avirt_driver = {
	.probe = snd_avirt_core_probe,
	.remove = snd_avirt_core_remove,
	.driver =
		{
			.name = SND_AVIRT_DRIVER,
		},
};

/**
 * snd_avirt_core_init - Initialize the kernel module
 */
static int __init snd_avirt_core_init(void)
{
	int err;

	D_INFOK("Alsa Virtual Sound Driver avirt-%d.%d.%d", core.version[0],
		core.version[1], core.version[2]);

	err = platform_driver_register(&snd_avirt_driver);
	if (err < 0)
		return err;

	core.plat_dev =
		platform_device_register_simple(SND_AVIRT_DRIVER, 0, NULL, 0);
	if (IS_ERR(core.plat_dev)) {
		err = PTR_ERR(core.plat_dev);
		goto exit_platform_device;
	}

	core.class = class_create(THIS_MODULE, SND_AVIRT_DRIVER);
	if (IS_ERR(core.class)) {
		D_ERRORK("No udev support");
		return PTR_ERR(core.class);
	}

	core.dev = device_create(core.class, NULL, 0, NULL, "avirtcore");
	if (IS_ERR(core.dev)) {
		err = PTR_ERR(core.dev);
		goto exit_class;
	}

	snd_avirt_audiopath_kset =
		kset_create_and_add("audiopaths", NULL, &core.dev->kobj);
	if (!snd_avirt_audiopath_kset) {
		err = -ENOMEM;
		goto exit_class_container;
	}

	err = snd_card_register(core.card);
	if (err < 0) {
		D_ERRORK("Sound card registration failed!");
		snd_card_free(core.card);
	}

	err = snd_avirt_configfs_init(&core);
	if (err < 0)
		goto exit_kset;

	return 0;

exit_kset:
	kset_unregister(snd_avirt_audiopath_kset);
exit_class_container:
	device_destroy(core.class, 0);
exit_class:
	class_destroy(core.class);
exit_platform_device:
	platform_device_unregister(core.plat_dev);
	platform_driver_unregister(&snd_avirt_driver);

	return err;
}

/**
 * snd_avirt_core_exit - Destroy the kernel module
 */
static void __exit snd_avirt_core_exit(void)
{
	snd_avirt_configfs_exit(&core);
	kset_unregister(snd_avirt_audiopath_kset);
	device_destroy(core.class, 0);
	class_destroy(core.class);
	platform_device_unregister(core.plat_dev);
	platform_driver_unregister(&snd_avirt_driver);
}

module_init(snd_avirt_core_init);
module_exit(snd_avirt_core_exit);