Compare commits

...

6 commits

Author SHA1 Message Date
Ranjani Sridharan
3c5d3bf607
Merge ffc63f76de into 21e0adfa3b 2024-07-31 08:39:59 -07:00
Takashi Iwai
21e0adfa3b aseqdump: Add dump of UMP Mixed Data Set messages
Add the support for yet more UMP messages.  UMP Mixed Data Set
messages are the generic data containers withe the message type 5
(shared with 8-bit SysEx).

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-28 11:24:06 +02:00
Takashi Iwai
095b064af6 aseqdump: Use snd_ump_get_byte() helper
For simplifying code, use the new helper function snd_ump_get_byte()
for extracting a byte data for SysEx and co.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-28 10:45:18 +02:00
Takashi Iwai
e26aa680aa aplaymidi2: Use snd_ump_get_byte() helper
For simplifying code, use snd_ump_get_byte() to retrieve the meta text
data.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-28 10:45:11 +02:00
Takashi Iwai
0188f93f02 aseqdump: Correct the limit of UMP 7-bit SysEx bytes
UMP 7-bit SysEx can hold up to 6 bytes, not 14 bytes.

Fixes: 02b0c3af56 ("aseqdump: Avoid OOB access with broken SysEx UMP packets")
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2024-07-26 14:46:58 +02:00
Ranjani Sridharan
ffc63f76de topology: pre-processor: Introduce a new feature for subtree copies
Introduce a new kyword "SubTreeCopy" for extneding existing conf nodes
with additional nodes. This feature is useful for extending previous
pipeline class definitions with the addition of one or more widgets
without having to duplicate everything in the new class definition.

For example: Consider a pipeline class definition as below. Note that
only the widgets & routes are shown here.

Class.Pipeline.mixout-gain-dai-copier-playback {
	Object.Widget {
		mixout."1" {}
		dai-copier."1" {}
		gain."1" {}
		pipeline."1" {}
	}

	Object.Base {
		!route [
			{
				source mixout.$index.1
				sink	gain.$index.1
			}
		]
	}
}

If we want to extend this pipeline with the addition of an eqiir/eqfir,
we can create a SubTreeCopy node with type extend as follows:

Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback {
	SubTreeCopy.baseclass {
		source "Class.Pipeline.mixout-gain-dai-copier-playback"
		type extend

                tree {
			Object.Widget {
				eqiir.1 {}
				eqfir.1 {}
			}

			Object.Base {
				!route [
					{
						source gain.$index.1
						sink   eqiir.$index.1
					}
					{
						source eqiir.$index.1
						sink   eqfir.$index.1
					}
				]
			}
		}
	}
}

Note that the target is left undefined, which means that the newly
created subtree will be merged to the parent node that contains the
"SubTreeCopy" node i.e. in this case
Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback".

But if we want to modify an existing pipeline class while modifying the
order of widgets and/or inserting new widgets, we should use the type
"override" instead. This allows for adding new widgets to the list of
widgets in the base class definition while also allowing overriding the
routes to allow inserting the new widgets and reordering the widgets in
the base class. For example, if we want to add a drc widget between the
gain and the eqiir modules in the above class, we can do the following:

Class.Pipeline.mixout-efx-dai-copier-playback {
	# This copy will override all widgets/routes in the base class
	SubTreeCopy.baseclass {
		source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback"
		target "Class.Pipeline"
		type override

		tree {
			Object.Widget {
				drc.1 {}
			}

			Object.Base {
				!route [
					{
						source mixout.$index.1
						sink	gain.$index.1
					}
					{
						source gain.$index.1
						sink	drc.$index.1
					}
					{
						source	drc.$index.1
						sink	eqiir.$index.1
					}
					{
						source	eqiir.$index.1
						sink	eqfir.$index.1
					}
				]
			}
		}
	}

	# Explicitly copy the widgets from the base class now
	SubTreeCopy.widgets {
		source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback.Object.Widget"
		target "Class.Pipeline.mixout-efx-dai-copier-playback.Object.Widget"
	}
}

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
2024-07-12 10:25:21 -07:00
3 changed files with 223 additions and 19 deletions

View file

@ -362,7 +362,7 @@ static void show_text(const uint32_t *ump)
len = 0;
for (i = 0; i < 12 && len < (int)sizeof(textbuf); i++) {
textbuf[len] = fh->meta.data[i / 4] >> ((3 - (i % 4)) * 8);
textbuf[len] = snd_ump_get_byte(ump, 4 + i);
if (!textbuf[len])
break;
switch (textbuf[len]) {

View file

@ -664,8 +664,7 @@ static void dump_ump_system_event(const unsigned int *ump)
static unsigned char ump_sysex7_data(const unsigned int *ump,
unsigned int offset)
{
offset += 2;
return (ump[offset / 4] >> ((3 - (offset & 3)) * 8)) & 0xff;
return snd_ump_get_byte(ump, offset + 2);
}
static void dump_ump_sysex_status(const char *prefix, unsigned int status)
@ -698,8 +697,8 @@ static void dump_ump_sysex_event(const unsigned int *ump)
dump_ump_sysex_status("SysEx", snd_ump_sysex_msg_status(ump));
length = snd_ump_sysex_msg_length(ump);
printf(" length %d ", length);
if (length > 14)
length = 14;
if (length > 6)
length = 6;
for (i = 0; i < length; i++)
printf("%s%02x", i ? ":" : "", ump_sysex7_data(ump, i));
printf("\n");
@ -708,8 +707,7 @@ static void dump_ump_sysex_event(const unsigned int *ump)
static unsigned char ump_sysex8_data(const unsigned int *ump,
unsigned int offset)
{
offset += 3;
return (ump[offset / 4] >> ((3 - (offset & 3)) * 8)) & 0xff;
return snd_ump_get_byte(ump, offset + 3);
}
static void dump_ump_sysex8_event(const unsigned int *ump)
@ -728,6 +726,45 @@ static void dump_ump_sysex8_event(const unsigned int *ump)
printf("\n");
}
static void dump_ump_mixed_data_event(const unsigned int *ump)
{
const snd_ump_msg_mixed_data_t *m =
(const snd_ump_msg_mixed_data_t *)ump;
int i;
printf("Group %2d, ", group_number(snd_ump_msg_group(ump)));
switch (snd_ump_sysex_msg_status(ump)) {
case SND_UMP_MIXED_DATA_SET_STATUS_HEADER:
printf("MDS Header id=0x%x, bytes=%d, chunk=%d/%d, manufacturer=0x%04x, device=0x%04x, sub_id=0x%04x 0x%04x\n",
m->header.mds_id, m->header.bytes,
m->header.chunk, m->header.chunks,
m->header.manufacturer, m->header.device,
m->header.sub_id_1, m->header.sub_id_2);
break;
case SND_UMP_MIXED_DATA_SET_STATUS_PAYLOAD:
printf("MDS Payload id=0x%x, ", m->payload.mds_id);
for (i = 0; i < 14; i++)
printf("%s%02x", i ? ":" : "",
snd_ump_get_byte(ump, i + 2));
printf("\n");
break;
default:
printf("Extended Data (status 0x%x)\n",
snd_ump_sysex_msg_status(ump));
break;
}
}
static void dump_ump_extended_data_event(const unsigned int *ump)
{
unsigned char status = snd_ump_sysex_msg_status(ump);
if (status < 4)
dump_ump_sysex8_event(ump);
else
dump_ump_mixed_data_event(ump);
}
static void print_ump_string(const unsigned int *ump, unsigned int fmt,
unsigned int offset, int maxlen)
{
@ -736,17 +773,12 @@ static void print_ump_string(const unsigned int *ump, unsigned int fmt,
int i = 0;
do {
buf[i] = (*ump >> (24 - offset)) & 0xff;
buf[i] = snd_ump_get_byte(ump, offset);
if (!buf[i])
break;
if (buf[i] < 0x20)
buf[i] = '.';
if (offset == 24) {
offset = 0;
ump++;
} else {
offset += 8;
}
offset++;
} while (++i < maxlen);
buf[i] = 0;
@ -780,12 +812,12 @@ static void dump_ump_stream_event(const unsigned int *ump)
break;
case SND_UMP_STREAM_MSG_STATUS_EP_NAME:
printf("EP Name ");
print_ump_string(ump, (ump[0] >> 26) & 3, 16, 14);
print_ump_string(ump, (ump[0] >> 26) & 3, 2, 14);
printf("\n");
break;
case SND_UMP_STREAM_MSG_STATUS_PRODUCT_ID:
printf("Product Id ");
print_ump_string(ump, (ump[0] >> 26) & 3, 16, 14);
print_ump_string(ump, (ump[0] >> 26) & 3, 2, 14);
printf("\n");
break;
case SND_UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
@ -810,7 +842,7 @@ static void dump_ump_stream_event(const unsigned int *ump)
case SND_UMP_STREAM_MSG_STATUS_FB_NAME:
printf("Product Id ");
printf("FB Name #%02d ", (ump[0] >> 8) & 0xff);
print_ump_string(ump, (ump[0] >> 26) & 3, 24, 13);
print_ump_string(ump, (ump[0] >> 26) & 3, 3, 13);
printf("\n");
break;
case SND_UMP_STREAM_MSG_STATUS_START_CLIP:
@ -959,7 +991,7 @@ static void dump_ump_flex_data_event(const unsigned int *ump)
if (fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_METADATA ||
fh->meta.status_bank == SND_UMP_FLEX_DATA_MSG_BANK_PERF_TEXT) {
printf("Meta (%s) ", ump_meta_prefix(fh));
print_ump_string(ump + 1, fh->meta.format, 0, 12);
print_ump_string(ump, fh->meta.format, 4, 12);
printf("\n");
return;
}
@ -995,7 +1027,7 @@ static void dump_ump_event(const snd_seq_ump_event_t *ev)
dump_ump_sysex_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_EXTENDED_DATA:
dump_ump_sysex8_event(ev->ump);
dump_ump_extended_data_event(ev->ump);
break;
case SND_UMP_MSG_TYPE_FLEX_DATA:
dump_ump_flex_data_event(ev->ump);

View file

@ -921,6 +921,172 @@ static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *
return 0;
}
static int pre_process_subtree_copy(struct tplg_pre_processor *tplg_pp, snd_config_t *curr,
snd_config_t *top)
{
snd_config_iterator_t i, next;
snd_config_t *subtrees;
int ret;
ret = snd_config_search(curr, "SubTreeCopy", &subtrees);
if (ret < 0)
return 0;
snd_config_for_each(i, next, subtrees) {
snd_config_t *n, *source_cfg, *target_cfg, *type_cfg;
snd_config_t *source_tree, *target_tree, *tmp, *subtree_cfg;
const char *id, *source_id;
const char *type = NULL;
const char *target_id = NULL;
bool override = false;
n = snd_config_iterator_entry(i);
ret = snd_config_get_id(n, &id);
if (ret < 0) {
SNDERR("Failed to get ID for subtree copy\n");
return ret;
}
/* get the type of copy ex: override/extend if set, by default override is false */
ret = snd_config_search(n, "type", &type_cfg);
if (ret >= 0) {
ret = snd_config_get_string(type_cfg, &type);
if (ret >= 0) {
if (strcmp(type, "override") && strcmp(type, "extend")) {
SNDERR("Invalid value for sub tree copy type %s\n", type);
return ret;
}
if (!strcmp(type, "override"))
override = true;
}
}
ret = snd_config_search(n, "source", &source_cfg);
if (ret < 0) {
SNDERR("failed to get source config for subtree %s\n", id);
return ret;
}
/* if the target node is not set, the subtree will be copied to current node */
ret = snd_config_search(n, "target", &target_cfg);
if (ret >= 0) {
snd_config_t *temp_target;
char *s;
ret = snd_config_get_string(target_cfg, &target_id);
if (ret < 0) {
SNDERR("Invalid target id for subtree %s\n", id);
return ret;
}
/*
* create a temporary node with target_id and merge with top to avoid
* failure in case the target node ID doesn't exist already
*/
s = tplg_snprintf("%s {}", target_id);
if (!s)
return -ENOMEM;
ret = snd_config_load_string(&temp_target, s, 0);
free(s);
if (ret < 0) {
SNDERR("Cannot create temp node with target id %s\n", target_id);
return ret;
}
ret = snd_config_merge(top, temp_target, false);
if (ret < 0) {
SNDERR("Cannot merge temp node with target id %s\n", target_id);
return ret;
}
ret = snd_config_search(top, target_id, &target_tree);
if (ret < 0) {
SNDERR("failed to get target tree %s\n", target_id);
return ret;
}
} else {
target_tree = curr;
}
/* get the source tree node */
ret = snd_config_get_string(source_cfg, &source_id);
if (ret < 0) {
SNDERR("Invalid source node id for subtree %s\n", id);
return ret;
}
ret = snd_config_search(top, source_id, &source_tree);
if (ret < 0) {
SNDERR("failed to get source tree %s\n", source_id);
return ret;
}
/* get the subtree to be merged */
ret = snd_config_search(n, "tree", &subtree_cfg);
if (ret < 0)
subtree_cfg = NULL;
/* make a temp copy of the source tree */
ret = snd_config_copy(&tmp, source_tree);
if (ret < 0) {
SNDERR("failed to copy source tree for subtreecopy %s\n", id);
return ret;
}
/* merge the current block with the source tree */
ret = snd_config_merge(tmp, subtree_cfg, override);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("Failed to merge source tree w/ subtree %s\n", id);
return ret;
}
/* merge the merged block to the target tree */
ret = snd_config_merge(target_tree, tmp, override);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("Failed to merge subtree %s w/ target\n", id);
return ret;
}
}
/* all subtree copies have been processed, remove the node */
snd_config_delete(subtrees);
return 0;
}
static int pre_process_subtree_copies(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
snd_config_t *curr)
{
snd_config_iterator_t i, next;
int ret;
if (snd_config_get_type(curr) != SND_CONFIG_TYPE_COMPOUND)
return 0;
/* process subtreecopies at this node */
ret = pre_process_subtree_copy(tplg_pp, curr, top);
if (ret < 0)
return ret;
/* process subtreecopies at all child nodes */
snd_config_for_each(i, next, curr) {
snd_config_t *n;
n = snd_config_iterator_entry(i);
/* process subtreecopies at this node */
ret = pre_process_subtree_copies(tplg_pp, top, n);
if (ret < 0)
return ret;
}
return 0;
}
#endif /* version < 1.2.6 */
int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,
@ -980,6 +1146,12 @@ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_
fprintf(stderr, "Failed to process object arrays in input config\n");
goto err;
}
err = pre_process_subtree_copies(tplg_pp, tplg_pp->input_cfg, tplg_pp->input_cfg);
if (err < 0) {
SNDERR("Failed to process subtree copies in input config\n");
goto err;
}
#endif
err = pre_process_config(tplg_pp, tplg_pp->input_cfg);