summaryrefslogtreecommitdiffstats
path: root/meta-agl-demo.md
blob: 35d6421a21b996a6e5ef144f757fe40e939d687e (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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
## Introduction

The `meta-agl-demo` layer is the reference user interface layer for the DEMO
platform of Automotive Grade Linux (AGL).
The layer provides a reference platform and applications.
The BitBake target name for the DEMO platform is `agl-demo-platform`, which is
the full DEMO platform image.

## Layer Dependencies

This section describes dependencies for the `meta-agl-demo` layer.
Dependencies are grouped into base, hardware, and feature dependencies.

### Base Dependencies

The `meta-agl-demo` layer has the following base dependencies:

* Yocto Project Release:

  - URI: git://git.yoctoproject.org/poky
  - Branch: "dunfell"
  - Tested Revision: See the [`default.xml`](https://github.com/leon-anavi/AGL-repo/blob/master/default.xml)
    manifest file for the `AGL-repo` repository for revision
    information.<br/><br/>

* AGL `meta-agl` Layer:

  - URI: https://gerrit.automotivelinux.org/gerrit/AGL/meta-agl
  - Branch: "master"<br/><br/>

* OpenEmbedded `meta-openembedded` Layer:

  - Branch: "thud"
  - Tested Revision: See the [`default.xml`](https://github.com/leon-anavi/AGL-repo/blob/master/default.xml)
    manifest file for the `AGL-repo` repository for revision
    information.

    Specifically, out of `meta-openembedded`, these sub-layers are used:

    - `meta-oe`
    - `meta-multimedia`
    - `meta-networking`
    - `meta-python`<br/><br/>

* Yocto Project `meta-qt5` Layer from the
  [OpenEmbedded Layer Index](https://layers.openembedded.org/layerindex/branch/master/layers/):

  - URI: https://github.com/meta-qt5/meta-qt5.git
  - Branch:   "dunfell"
  - Tested Revision: See the [`default.xml`](https://github.com/leon-anavi/AGL-repo/blob/master/default.xml)
    manifest file for the `AGL-repo` repository for revision
    information.<br/><br/>


### Feature Dependencies

The `meta-agl-demo` layer has the following AGL
[feature](../getting_started/reference/getting-started/image-workflow-initialize-build-environment.html#agl-features)
dependencies:

* Yocto Project `meta-security` Layer:

  - URI: https://git.yoctoproject.org/cgit/cgit.cgi/meta-security
  - Branch: "master"
  - Tested Revision: See the [`default.xml`](https://github.com/leon-anavi/AGL-repo/blob/master/default.xml)
    manifest file for the `AGL-repo` repository for revision
    information.<br/><br/>

* AGL's `meta-app-framework` Layer within the `meta-agl` Layer:

  - URI: https://gerrit.automotivelinux.org/gerrit/gitweb?p=AGL/meta-agl.git
  - Branch: "master"<br/><br/>

**The `agl-sota` Feature:**

* Here Technologies' `meta-updater` Layer:

  - URI: https://github.com/advancedtelematic/meta-updater/
  - Branch: "dunfell"<br/><br/>

* Here Technologies' `meta-updater-qemux86-64` Layer:

  - URI: https://github.com/advancedtelematic/meta-updater-qemux86-64/
  - Branch: "dunfell"<br/><br/>

* OpenEmbedded's `meta-openembedded` Layer:

  - URI: https://github.com/openembedded/meta-openembedded
  - Branch: "dunfell"
  - Tested Revision: See the [`default.xml`](https://github.com/leon-anavi/AGL-repo/blob/master/default.xml)
    manifest file for the `AGL-repo` repository for revision
    information.

    Specifically, out of `meta-openembedded`, these sub-layers are used:

    - `meta-filesystems`
    - `meta-oe`
    - `meta-python`<br/><br/>

**The `agl-netboot` Feature:**

* AGL's `meta-netboot` Layer within the `meta-agl` Layer:

  - URI: https://gerrit.automotivelinux.org/gerrit/gitweb?p=AGL/meta-agl.git
  - Branch: "master"
d='n498' href='#n498'>498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
// SPDX-License-Identifier: GPL-2.0
/*
 * AVIRT - ALSA Virtual Soundcard
 *
 * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd
 *
 * configfs.c - AVIRT configfs support
 */

#include <linux/slab.h>

#include "core.h"

#define D_LOGNAME "configfs"

#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)

/**
 * Defines the configfs direction 'show' callback for a stream or route
 */
#define CFG_SND_AVIRT_DIRECTION_RO(type)                                       \
	static ssize_t cfg_snd_avirt_##type##_direction_show(                  \
		struct config_item *item, char *page)                          \
	{                                                                      \
		ssize_t count;                                                 \
		struct snd_avirt_##type *s =                                   \
			snd_avirt_##type##_from_config_item(item);             \
		count = sprintf(page, "%d\n", s->direction);                   \
		return count;                                                  \
	}                                                                      \
	CONFIGFS_ATTR_RO(cfg_snd_avirt_##type##_, direction)

/**
 * Defines the configfs channels 'show'/'store' callbacks for a stream or route
 */
#define CFG_SND_AVIRT_CHANNELS(type)                                           \
	static ssize_t cfg_snd_avirt_##type##_channels_show(                   \
		struct config_item *item, char *page)                          \
	{                                                                      \
		ssize_t count;                                                 \
		struct snd_avirt_##type *s =                                   \
			snd_avirt_##type##_from_config_item(item);             \
		count = sprintf(page, "%d\n", s->channels);                    \
		return count;                                                  \
	}                                                                      \
	static ssize_t cfg_snd_avirt_##type##_channels_store(                  \
		struct config_item *item, const char *page, size_t count)      \
	{                                                                      \
		int err;                                                       \
		struct snd_avirt_##type *s =                                   \
			snd_avirt_##type##_from_config_item(item);             \
		unsigned long tmp;                                             \
		char *p = (char *)page;                                        \
		err = kstrtoul(p, 10, &tmp);                                   \
		if (err < 0)                                                   \
			return err;                                            \
		if ((tmp > INT_MAX) || (tmp == 0))                             \
			return -ERANGE;                                        \
		s->channels = tmp;                                             \
		snd_avirt_##type##_try_complete(s);                            \
		return count;                                                  \
	}                                                                      \
	CONFIGFS_ATTR(cfg_snd_avirt_##type##_, channels);

CFG_SND_AVIRT_DIRECTION_RO(stream);
CFG_SND_AVIRT_CHANNELS(stream);
CFG_SND_AVIRT_DIRECTION_RO(route);
CFG_SND_AVIRT_CHANNELS(route);

/*
 * Check PCM hw params between a source and a sink
 */
#define CHK_ROUTE_ERR(source_hw, sink_hw, field)                                \
	do {                                                                    \
		if (source_hw->field != sink_hw->field) {                       \
			D_ERRORK(                                               \
				"Route HW mismatch: ##field (src:%d, sink:%d)", \
				source_hw->field, sink_hw->field);              \
			return -1;                                              \
		}                                                               \
	} while (0)

/*
 * Check the a route's source and sink Audio Path's hardware params, to ensure
 * compatibility
 */
static int cfg_snd_avirt_route_verify_hw(struct snd_avirt_audiopath *source_ap,
					 struct snd_avirt_audiopath *sink_ap)
{
	const struct snd_pcm_hardware *source_hw = source_ap->hw;
	const struct snd_pcm_hardware *sink_hw = sink_ap->hw;

	CHK_ROUTE_ERR(source_hw, sink_hw, channels_max);
	CHK_ROUTE_ERR(source_hw, sink_hw, channels_min);
	CHK_ROUTE_ERR(source_hw, sink_hw, rate_max);
	CHK_ROUTE_ERR(source_hw, sink_hw, rate_min);
	CHK_ROUTE_ERR(source_hw, sink_hw, rates);

	return 0;
}

/*
 * Store the Audio Path endpoint (source or sink), and check compatiblity
 */
static int cfg_snd_avirt_route_ap_store(struct snd_avirt_route *route,
					struct snd_avirt_audiopath *ap,
					snd_avirt_route_endpoint endpoint)
{
	/* If other endpoint is set, we want to check that the two Audio Path
	 * endpoints are compatible before we set this endpoint */
	if (route->endpoint_ap[!endpoint]) {
		if (!cfg_snd_avirt_route_verify_hw(
			    route->endpoint_ap[!endpoint], ap)) {
			route->endpoint_ap[endpoint] = ap;
			D_INFOK("Route successfully created: '%s' [%s -> %s]",
				route->uid, ap->uid,
				route->endpoint_ap[!endpoint]->uid);
			return 0;
		} else {
			D_ERRORK("Route could not be created: %s", route->uid);
			return -1;
		}
	} else {
		route->endpoint_ap[endpoint] = ap;
	}

	return 0;
}

static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item,
					     char *page)
{
	struct snd_avirt_stream *stream =
		snd_avirt_stream_from_config_item(item);

	return sprintf(page, "%s\n", stream->map);
}

static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item,
					      const char *page, size_t count)
{
	char *map;
	struct snd_avirt_audiopath *audiopath;
	struct snd_avirt_stream *stream =
		snd_avirt_stream_from_config_item(item);

	map = strsep((char **)&page, "\n");

	/* If already configured, we cannot create the stream */
	if (snd_avirt_streams_configured()) {
		D_ERRORK("Streams already configured. Cannot set map: '%s'",
			 map);
		return -EPERM;
	}

	if (!strcmp(stream->map, map))
		return -1;

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

	memcpy(stream->map, (char *)map, strlen(map));

	snd_avirt_stream_try_complete(stream);

	return count;
}
CONFIGFS_ATTR(cfg_snd_avirt_stream_, map);

static struct configfs_attribute *cfg_snd_avirt_stream_attrs[] = {
	&cfg_snd_avirt_stream_attr_channels,
	&cfg_snd_avirt_stream_attr_map,
	&cfg_snd_avirt_stream_attr_direction,
	NULL,
};

static ssize_t cfg_snd_avirt_route_sink_ap_show(struct config_item *item,
						char *page)
{
	struct snd_avirt_route *route = snd_avirt_route_from_config_item(item);
	if (!route) {
		D_ERRORK("Cannot get route!");
		goto exit_err;
	}

	if (route->endpoint_ap[SND_AVIRT_ROUTE_SINK])
		return sprintf(page, "%s\n",
			       route->endpoint_ap[SND_AVIRT_ROUTE_SINK]->uid);

exit_err:
	return sprintf(page, "\n");
}

static ssize_t cfg_snd_avirt_route_sink_ap_store(struct config_item *item,
						 const char *page, size_t count)
{
	char *uid_ap;
	struct snd_avirt_route *route = snd_avirt_route_from_config_item(item);
	struct snd_avirt_audiopath *sink_ap;

	if (!route) {
		D_ERRORK("Cannot get route!");
		goto exit;
	}

	uid_ap = strsep((char **)&page, "\n");
	sink_ap = snd_avirt_audiopath_get(uid_ap);
	if (!sink_ap) {
		D_ERRORK("Audio Path '%s' does not exist!", uid_ap);
		D_ERRORK("Cannot set 'route'->'sink_ap'");
		goto exit;
	}

	cfg_snd_avirt_route_ap_store(route, sink_ap, SND_AVIRT_ROUTE_SINK);

exit:
	return count;
}
CONFIGFS_ATTR(cfg_snd_avirt_route_, sink_ap);

static ssize_t cfg_snd_avirt_route_source_ap_show(struct config_item *item,
						  char *page)
{
	struct snd_avirt_route *route = snd_avirt_route_from_config_item(item);
	if (!route) {
		D_ERRORK("Cannot get route!");
		goto exit_err;
	}

	if (route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE])
		return sprintf(page, "%s\n",
			       route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE]->uid);

exit_err:
	return sprintf(page, "\n");
}

static ssize_t cfg_snd_avirt_route_source_ap_store(struct config_item *item,
						   const char *page,
						   size_t count)
{
	char *uid_ap;
	struct snd_avirt_route *route = snd_avirt_route_from_config_item(item);
	struct snd_avirt_audiopath *source_ap;

	if (!route) {
		D_ERRORK("Cannot get route!");
		goto exit;
	}

	uid_ap = strsep((char **)&page, "\n");
	source_ap = snd_avirt_audiopath_get(uid_ap);
	if (!source_ap) {
		D_ERRORK("Audio Path '%s' does not exist!", uid_ap);
		D_ERRORK("Cannot set 'route'->'source_ap'");
		return count;
	}

	cfg_snd_avirt_route_ap_store(route, source_ap, SND_AVIRT_ROUTE_SOURCE);

exit:
	return count;
}
CONFIGFS_ATTR(cfg_snd_avirt_route_, source_ap);

static struct configfs_attribute *cfg_snd_avirt_route_attrs[] = {
	&cfg_snd_avirt_route_attr_channels,
	&cfg_snd_avirt_route_attr_direction,
	&cfg_snd_avirt_route_attr_source_ap,
	&cfg_snd_avirt_route_attr_sink_ap,
	NULL,
};

static void cfg_snd_avirt_stream_release(struct config_item *item)
{
	struct snd_avirt_stream *stream =
		snd_avirt_stream_from_config_item(item);
	if (!stream) {
		D_ERRORK("Cannot release stream!");
		return;
	}

	D_INFOK("Release stream: %s", stream->name);
	snd_avirt_stream_try_destroy(stream);
}

static void cfg_snd_avirt_route_release(struct config_item *item)
{
	struct snd_avirt_route *route = snd_avirt_route_from_config_item(item);
	if (!route) {
		D_ERRORK("Cannot release route!");
		return;
	}

	kfree(route);

	D_INFOK("Release route: %s", route->uid);
}

static struct configfs_item_operations cfg_snd_avirt_stream_ops = {
	.release = cfg_snd_avirt_stream_release,
};

static struct configfs_item_operations cfg_snd_avirt_route_ops = {
	.release = cfg_snd_avirt_route_release,
};

static struct config_item_type cfg_snd_avirt_stream_type = {
	.ct_item_ops = &cfg_snd_avirt_stream_ops,
	.ct_attrs = cfg_snd_avirt_stream_attrs,
	.ct_owner = THIS_MODULE,
};

static struct config_item_type cfg_snd_avirt_route_type = {
	.ct_item_ops = &cfg_snd_avirt_route_ops,
	.ct_attrs = cfg_snd_avirt_route_attrs,
	.ct_owner = THIS_MODULE,
};

static struct config_item *
cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name)
{
	char *split, *stream_name;
	bool internal = false;
	int direction;
	struct snd_avirt_stream *stream;

	// Get prefix (playback_ or capture_)
	split = strsep((char **)&name, "_");
	if (!split) {
		D_ERRORK("Stream name: '%s' invalid!", split);
		D_ERRORK("Must begin with playback_ * or capture_ *");
		return ERR_PTR(-EINVAL);
	}
	if (!strcmp(split, "playback")) {
		direction = SNDRV_PCM_STREAM_PLAYBACK;
	} else if (!strcmp(split, "capture")) {
		direction = SNDRV_PCM_STREAM_CAPTURE;
	} else {
		D_ERRORK("Stream name: '%s' invalid!", split);
		D_ERRORK("Must begin with playback_ * or capture_ ");
		return ERR_PTR(-EINVAL);
	}

	// Get stream name, and create PCM for stream
	stream_name = strsep((char **)&name, "\n");

	// If internal, get internal
	split = strstr(stream_name, "__internal");
	if (split) {
		stream_name = strsep((char **)&stream_name, "__");
		internal = true;
	}

	// Finally, create stream
	stream = snd_avirt_stream_create(stream_name, direction, internal);
	if (IS_ERR(stream))
		return ERR_PTR(PTR_ERR(stream));

	config_item_init_type_name(&stream->item, name,
				   &cfg_snd_avirt_stream_type);

	return &stream->item;
}

static struct config_item *
cfg_snd_avirt_route_make_item(struct config_group *group, const char *name)
{
	char *split;
	int direction;
	struct snd_avirt_route *route;

	// Get prefix (playback_ or capture_)
	split = strsep((char **)&name, "_");
	if (!split) {
		D_ERRORK("Route name: '%s' invalid!", split);
		D_ERRORK("Must begin with playback_ * or capture_ *");
		return ERR_PTR(-EINVAL);
	}
	if (!strcmp(split, "playback")) {
		direction = SNDRV_PCM_STREAM_PLAYBACK;
	} else if (!strcmp(split, "capture")) {
		direction = SNDRV_PCM_STREAM_CAPTURE;
	} else {
		D_ERRORK("Route name: '%s' invalid!", split);
		D_ERRORK("Must begin with playback_ * or capture_ ");
		return ERR_PTR(-EINVAL);
	}

	// Get route name, and create route
	split = strsep((char **)&name, "\n");
	route = snd_avirt_route_create(split, direction);
	if (IS_ERR(route))
		return ERR_PTR(PTR_ERR(route));

	config_item_init_type_name(&route->item, name,
				   &cfg_snd_avirt_route_type);

	return &route->item;
}

static ssize_t
cfg_snd_avirt_stream_group_configured_show(struct config_item *item, char *page)
{
	return snprintf(page, PAGE_SIZE, "%d\n",
			snd_avirt_streams_configured());
}

static ssize_t
cfg_snd_avirt_stream_group_configured_store(struct config_item *item,
					    const char *page, size_t count)
{
	unsigned long tmp;
	char *p = (char *)page;

	CHK_ERR(kstrtoul(p, 10, &tmp));

	if (tmp > 1) {
		D_ERRORK("Configure streams must be 0 or 1!");
		return -ERANGE;
	}

	(tmp) ? snd_avirt_streams_configure() : snd_avirt_streams_unconfigure();

	return count;
}
CONFIGFS_ATTR(cfg_snd_avirt_stream_group_, configured);

static struct configfs_attribute *cfg_snd_avirt_stream_group_attrs[] = {
	&cfg_snd_avirt_stream_group_attr_configured,
	NULL,
};

static struct configfs_group_operations cfg_snd_avirt_stream_group_ops = {
	.make_item = cfg_snd_avirt_stream_make_item
};

static struct configfs_group_operations cfg_snd_avirt_route_group_ops = {
	.make_item = cfg_snd_avirt_route_make_item
};

static struct config_item_type cfg_stream_group_type = {
	.ct_group_ops = &cfg_snd_avirt_stream_group_ops,
	.ct_attrs = cfg_snd_avirt_stream_group_attrs,
	.ct_owner = THIS_MODULE
};

static struct config_item_type cfg_route_group_type = {
	.ct_group_ops = &cfg_snd_avirt_route_group_ops,
	.ct_attrs = NULL,
	.ct_owner = THIS_MODULE
};

static struct config_item_type cfg_avirt_group_type = {
	.ct_owner = THIS_MODULE,
};

static struct configfs_subsystem cfg_subsys = {
	.su_group =
		{
			.cg_item =
				{
					.ci_namebuf = "snd-avirt",
					.ci_type = &cfg_avirt_group_type,
				},
		},
};

int __init snd_avirt_configfs_init(struct snd_avirt_core *core)
{
	int err;

	config_group_init(&cfg_subsys.su_group);
	mutex_init(&cfg_subsys.su_mutex);
	err = configfs_register_subsystem(&cfg_subsys);
	if (err) {
		D_ERRORK("Cannot register configfs subsys!");
		return err;
	}

	/* Create streams default group */
	core->stream_group = configfs_register_default_group(
		&cfg_subsys.su_group, "streams", &cfg_stream_group_type);
	if (IS_ERR(core->stream_group)) {
		err = PTR_ERR(core->stream_group);
		D_ERRORK("Cannot register configfs default group 'streams'!");
		goto exit_configfs;
	}

	/* Create routes default group */
	core->route_group = configfs_register_default_group(
		&cfg_subsys.su_group, "routes", &cfg_route_group_type);
	if (IS_ERR(core->route_group)) {
		err = PTR_ERR(core->route_group);
		D_ERRORK("Cannot register configfs default group 'routes'!");
		goto exit_configfs;
	}

	return 0;

exit_configfs:
	configfs_unregister_subsystem(&cfg_subsys);

	return err;
}

void __exit snd_avirt_configfs_exit(struct snd_avirt_core *core)
{
	configfs_unregister_default_group(core->stream_group);
	configfs_unregister_subsystem(&cfg_subsys);
}