aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/lib/bluealsa/hal-bluealsa-transports.c
blob: 2c580235afecbfebf03295189642596708c34655 (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Copyright (C) 2018 "IoT.bzh"
 * Author : Thierry Bultel <thierry.bultel@iot.bzh>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <malloc.h>

#include "hal-bluealsa-transports.h"


static bool halBlueAlsaTransportMatch(
	const struct ba_msg_transport * transport1,
	const struct ba_msg_transport * transport2);

int  halBlueAlsaTransportsInit(bluealsa_transport_t * list) {
	CDS_INIT_LIST_HEAD(&list->list);
	return 0;
}

bluealsa_transport_t *  halBlueAlsaTransportsAdd(
	const bluealsa_watch * watch,
	bluealsa_transport_t * list,
	const struct ba_msg_transport * transport) {

	bluealsa_transport_t  * newTransport = (bluealsa_transport_t*) calloc(1, sizeof(bluealsa_transport_t));
	if (newTransport == NULL)
		goto fail;

	memcpy(&newTransport->transport, transport, sizeof(struct ba_msg_transport));

	newTransport->watch = watch;

	CDS_INIT_LIST_HEAD(&newTransport->list);
	cds_list_add(&newTransport->list, &list->list);

	return newTransport;
fail:
	return NULL;

}

bool halBlueAlsaTransportFind(
	const bluealsa_watch * watch,
	bluealsa_transport_t * list,
	const struct ba_msg_transport * transport) {

	bluealsa_transport_t *tmp;
	bool found = false;
	cds_list_for_each_entry(tmp, &list->list, list) {
		struct ba_msg_transport * _transport = &tmp->transport;

		if (watch != tmp->watch)
			continue;

		if (!halBlueAlsaTransportMatch(_transport, transport))
			continue;

		found = true;
		break;
	}
	return found;
}


int halBlueAlsaTransportUpdate(
	const bluealsa_watch * watch,
	bluealsa_transport_t * list,
	const struct ba_msg_transport *transports,
	size_t nb,
	transport_destructor destructor) {

	bluealsa_transport_t *transport, *sav;

	cds_list_for_each_entry_safe(transport, sav, &list->list, list) {
		struct ba_msg_transport * ba_transport1 = &transport->transport;

		if (watch != transport->watch)
			continue;

		bool found = false;

		for (int ix = 0; ix<nb; ix++) {
			const struct ba_msg_transport * ba_transport2 = &transports[ix];

			if (halBlueAlsaTransportMatch(ba_transport1, ba_transport2)) {
				found = true;
				break;
			}
		}

		if (found)
			continue;

		char transportS[HAL_BLUEALSA_TRANSPORT_LEN_MAX];

		AFB_ApiInfo(watch->plugin->api,
				    "Unregister transport %s",
					halBlueAlsaTransportAsString(transportS, HAL_BLUEALSA_TRANSPORT_LEN_MAX, &transport->transport));

		cds_list_del(&transport->list);
		if (destructor) {
			destructor(transport);
		}

		free(transport->transactionUidS);
		free(transport);

	}

	return 0;
}


static bool halBlueAlsaTransportMatch(const struct ba_msg_transport * transport1, const struct ba_msg_transport * transport2) {
	bool ret = false;

	if (transport1->type != transport2->type)
		goto done;

	if (transport1->stream != transport2->stream)
		goto done;

	if (bacmp(&transport1->addr, &transport2->addr) != 0) {
		goto done;
	}
	ret = true;
done:
	return ret;
}


char * halBlueAlsaTransportAsString(char * buff, size_t len, const struct ba_msg_transport * transport) {
	char addr[18];

	ba2str(&transport->addr, addr);
	snprintf(buff, len, "%s,%s,%s", addr,
			transport->type == BA_PCM_TYPE_A2DP?"a2dp":"sco",
			transport->stream == BA_PCM_STREAM_PLAYBACK?"playback":"capture");

	return buff;
}
lass="gs">***afb\_req\_unsubscribe*** function tells the framework to remove the requesting client from the event's list of subscribers. Example: ```C afb_req_unsubscribe(afb_req, event); ``` Subscription count does not matter to the framework: subscribing the same client several times has the same effect that subscribing only one time. Thus, when unsubscribing is invoked, it becomes immediately effective. #### More on naming events Within the AGL framework, a signaling agent is a binding that has an API prefix. This prefix is meant to be unique and to identify the binding API. The names of the events that this signaling agent creates are automatically prefixed by the framework, using the API prefix of the binding. Thus, if a signaling agent of API prefix ***api*** creates an event of name ***event*** and pushes data to that event, the subscribers will receive an event of name ***api/event***. ### Generating and pushing signals and data This of the responsibility of the designer of the signaling agent to establish the processing chain for generating events. In many cases, this can be achieved using I/O or timer or signal events inserted in the main loop. For this case, the AGL framework uses **libsystemd** and provide a way to integrates to the main loop of this library using afb\_daemon\_get\_event\_loop. Example: ```C sdev = afb_daemon_get_event_loop(af_daemon); rc = sd_event_add_io(sdev, &source, fd, EPOLLIN, myfunction, NULL); ``` In some other cases, the events are coming from D-Bus. In that case, the framework also uses **libsystemd** internally to access D-Bus. It provides two methods to get the available D-Bus objects, already existing and bound to the main**libsystemd**event loop. Use either ***afb\_daemon\_get\_system\_bus*** or ***afb\_daemon\_get\_user\_bus*** to get the required instance. Then use functions of **libsystemd** to handle D-Bus. In some rare cases, the generation of the data requires to start a new thread. When a data is generated and ready to be pushed, the signaling agent should call the function ***afb\_event\_push***. Example: ```C rc = afb_event_push(event, JSON); if (rc == 0) { stop_generating(event); afb_event_drop(event); } ``` The function ***afb\_event\_push*** pushes json data to all the subscribers. It then returns the count of subscribers. When the count is zero, there is no subscriber listening for the event. The example above shows that in that case, the signaling agent stops to generate data for the event and delete the event using afb\_event\_drop. This is one possible option. Other valuable options are: do nothing and continue to generate and push the event or just stop to generate and push the data but keep the event existing. ### Receiving the signals Understanding what a client expects when it receives signals, events or data shall be the most important topic of the designer of a signaling agent. The good point here is that because JSON[^1] is the exchange format, structured data can be sent in a flexible way. The good design is to allow as much as possible the client to describe what is needed with the goal to optimize the processing to the requirements only. ### The exceptional case of wide broadcast Some data or events have so much importance that they can be widely broadcasted to alert any listening client. Examples of such an alert are: - system is entering/leaving “power safe” mode - system is shutting down - the car starts/stops moving - ... An event can be broadcasted using one of the two following methods: ***afb\_daemon\_broadcast\_event*** or ***afb\_event\_broadcast***. Example 1: ```C afb_daemon_broadcast_event(afb_daemon, name, json); ``` Example 2: ```C event = afb_daemon_make_event(afb_daemon, name); . . . . afb_event_broadcast(event, json); ``` As for other events, the name of events broadcasted using ***afb\_daemon\_broadcast\_event*** are automatically prefixed by the framework with API prefix of the binding (signaling agent). Reference of functions ---------------------- ### Function afb\_event afb\_daemon\_make\_event The function ***afb\_daemon\_make\_event*** that is defined as below: ```C /* * Creates an event of 'name' and returns it. * 'daemon' MUST be the daemon given in interface when activating the binding. */ struct afb_event afb_daemon_make_event(struct afb_daemon daemon, const char *name); ``` The daemon is the handler to the application framework binder daemon received during initialisation steps of the binding. Calling the function ***afb\_daemon\_make\_event*** within the initialisation function ***afbBindingV1Register*** will _fail_ because the binding name is not known at this time. The correct way to create the event at initialisation is to call the function ***afb\_daemon\_make\_event*** within the initialisation function ***afbBindingV1ServiceInit***. ### Function afb\_event\_push The function ***afb\_event\_push*** is defined as below: ```C /* * Pushes the 'event' with the data 'object' to its observers. * 'object' can be NULL. * * For convenience, the function calls 'json_object_put' for object'. * Thus, in the case where 'object' should remain available after * the function returns, the function 'json_object_get' shall be used. * * Returns the count of clients that received the event. */ int afb_event_push(struct afb_event event, struct json_object *object); ``` As the function ***afb\_event\_push*** returns 0 when there is no more subscriber, a binding can remove such unexpected event using the function ***afb\_event\_drop***. ### Function afb\_event\_drop The function ***afb\_event\_drop*** is defined as below: ```C /* * Drops the data associated to the event * After calling this function, the event * MUST NOT BE USED ANYMORE. */ void afb_event_drop(struct afb_event event); ``` ### Function afb\_req\_subscribe The function ***afb\_req\_subscribe*** is defined as below: ```C /* * Establishes for the client link identified by 'req' a subscription * to the 'event'. * Returns 0 in case of successful subscription or -1 in case of error. */ int afb_req_subscribe(struct afb_req req, struct afb_event event); ``` The subscription adds the client of the request to the list of subscribers to the event. ### Function afb\_req\_unsubscribe The function ***afb\_req\_unsubscribe*** is defined as below: ```C /* * Revokes the subscription established to the 'event' for the client * link identified by 'req'. * Returns 0 in case of successful unsubscription or -1 in case of error. */ int afb_req_unsubscribe(struct afb_req req, struct afb_event event); ``` The unsubscription removes the client of the request of the list of subscribers to the event. When the list of subscribers to the event becomes empty, the function ***afb\_event\_push*** will return zero. ### Function afb\_event\_broadcast The function ***afb\_event\_broadcast*** is defined as below: ```C /* * Broadcasts widely the 'event' with the data 'object'. * 'object' can be NULL. * * For convenience, the function calls 'json_object_put' for 'object'. * Thus, in the case where 'object' should remain available after * the function returns, the function 'json_object_get' shall be used. * * Returns the count of clients that received the event. */ int afb_event_broadcast(struct afb_event event, struct json_object *object); ``` This uses an existing event (created with ***afb\_daemon\_make\_event***) for broadcasting an event having its name. ### Function afb\_daemon\_broadcast\_event The function ***afb\_daemon\_broadcast\_event*** is defined as below: ```C /* * Broadcasts widely the event of 'name' with the data 'object'. * 'object' can be NULL. * 'daemon' MUST be the daemon given in interface when activating the binding. * * For convenience, the function calls 'json_object_put' for 'object'. * Thus, in the case where 'object' should remain available after * the function returns, the function 'json_object_get' shall be used. * * Returns the count of clients that received the event. */ int afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); ``` The name is given here explicitly. The name is automatically prefixed with the name of the binding. For example, a binding of prefix "xxx" would broadcat the event "xxx/name". ### Function afbBindingV1ServiceEvent Binding can implement function **afbBindingV1ServiceEvent** which will be called when an event is broadcasted or if service subscribed to an event. That allow a service to react to an event and do what it is to do if this is relevant for it (ie: car back camera detects imminent collision and broadcast it, then appropriate service enable parking brake.). Here is the **afbBindingV1ServiceEvent** definition: ```C /* * When a binding have an implementation of the function 'afbBindingV1ServiceEvent', * defined below, the framework calls that function for any broadcasted event or for * events that the service subscribed to in its name. * * It receive the 'event' name and its related data in 'object' (be aware that 'object' * might be NULL). */ extern void afbBindingV1ServiceEvent(const char *event, struct json_object *object); The binding *tic-tac-toe* broadcasts events when the board changes. This is done in the function **changed**: ``` Architectural digressions ------------------------- Based on their dependencies to hardware, signaling agents can be split into 2 categories: low-level signaling agents and high-level signaling agents. Low-level signaling agents are bound to the hardware and focused on interfacing and driving. High-level signaling agent are independent of the hardware and focused on providing service. This separation (that may in the corner look artificial) aim to help in the systems design. The main idea here is that high-level signaling agents are providing “business logic”, also known as “application logic”, that is proper to the car industry and that can be reused and that can evolve as a foundation for the future of the industry. The implementation of this decomposition may follow 2 paths: strict separation or soft composition. ### Strict separation The strict separation implements the modularity composition of signaling agent through the framework. The high-level signaling agent subscribes to the low level signaling agent using the standard client API. Advantages: - Modularity - Separation of responsibilities - Possible aggregation of multiple sources - Soft binding of agent good for maintenance Drawbacks: - Cost of propagation of data (might serialize) - Difficulties to abstract low-level signaling agent or to find a trade-off between abstracting and specializing The key is modularity versus cost of propagation. It can be partly solved when logical group of signaling agent are launched together in the same binder process. In that particular case, the cost of propagation of data between agents is reduced[^2] because there is no serialization. This reduction of the propagation cost (and of the resources used) precludes implementation of strong security between the agents because they share the same memory. ### Soft composition The soft composition implements the business logic of high-level signaling agents as libraries that can then be used directly by the low level signaling agents. Advantages: - No propagation: same memory, sharing of native structures Drawbacks: - Cannot be used for aggregation of several sources - Difficulties to abstract low-level signaling agent or to find a trade-off between abstracting and specializing - Source code binding not good for maintenance [^1]: There are two aspect in using JSON: the first is the flexible data structure that mixes common types (booleans, numbers, strings, arrays, dictionaries, nulls), the second, is the streaming specification. Streaming is often seen as the bottleneck of using JSON (see http://bjson.org). When the agent share the same process, there is no streaming at all. [^2]: Within the same process, there is not serialization, the propagation has the cost of wrapping a json data and calling callbacks with the benefit of having a powerful callback manager: the event mechanism of the framework.