1 /* PipeWire
2  *
3  * Copyright © 2018 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <spa/pod/parser.h>
26 #include <spa/debug/types.h>
27 
28 #include <pipewire/control.h>
29 #include <pipewire/private.h>
30 
31 #define NAME "control"
32 
33 struct impl {
34 	struct pw_control this;
35 
36 	struct pw_memblock *mem;
37 };
38 
39 struct pw_control *
pw_control_new(struct pw_context * context,struct pw_impl_port * port,uint32_t id,uint32_t size,size_t user_data_size)40 pw_control_new(struct pw_context *context,
41 	       struct pw_impl_port *port,
42 	       uint32_t id, uint32_t size,
43 	       size_t user_data_size)
44 {
45 	struct impl *impl;
46 	struct pw_control *this;
47 	enum spa_direction direction;
48 
49 	switch (id) {
50 	case SPA_IO_Control:
51 		direction = SPA_DIRECTION_INPUT;
52 		break;
53 	case SPA_IO_Notify:
54 		direction = SPA_DIRECTION_OUTPUT;
55 		break;
56 	default:
57 		errno = ENOTSUP;
58 		goto error_exit;
59 	}
60 
61 	impl = calloc(1, sizeof(struct impl) + user_data_size);
62 	if (impl == NULL)
63 		goto error_exit;
64 
65 	this = &impl->this;
66 	this->id = id;
67 	this->size = size;
68 
69 	pw_log_debug(NAME" %p: new %s %d", this,
70 			spa_debug_type_find_name(spa_type_io, this->id), direction);
71 
72 	this->context = context;
73 	this->port = port;
74 	this->direction = direction;
75 
76 	spa_list_init(&this->links);
77 
78         if (user_data_size > 0)
79 		this->user_data = SPA_PTROFF(impl, sizeof(struct impl), void);
80 
81 	spa_hook_list_init(&this->listener_list);
82 
83 	spa_list_append(&context->control_list[direction], &this->link);
84 	if (port) {
85 		spa_list_append(&port->control_list[direction], &this->port_link);
86 		pw_impl_port_emit_control_added(port, this);
87 	}
88 	return this;
89 
90 error_exit:
91 	return NULL;
92 }
93 
pw_control_destroy(struct pw_control * control)94 void pw_control_destroy(struct pw_control *control)
95 {
96 	struct impl *impl = SPA_CONTAINER_OF(control, struct impl, this);
97 	struct pw_control_link *link;
98 
99 	pw_log_debug(NAME" %p: destroy", control);
100 
101 	pw_control_emit_destroy(control);
102 
103 	if (control->direction == SPA_DIRECTION_OUTPUT) {
104 		spa_list_consume(link, &control->links, out_link)
105 			pw_control_remove_link(link);
106 	}
107 	else {
108 		spa_list_consume(link, &control->links, in_link)
109 			pw_control_remove_link(link);
110 	}
111 
112 	spa_list_remove(&control->link);
113 
114 	if (control->port) {
115 		spa_list_remove(&control->port_link);
116 		pw_impl_port_emit_control_removed(control->port, control);
117 	}
118 
119 	pw_log_debug(NAME" %p: free", control);
120 	pw_control_emit_free(control);
121 
122 	spa_hook_list_clean(&control->listener_list);
123 
124 	if (control->direction == SPA_DIRECTION_OUTPUT) {
125 		if (impl->mem)
126 			pw_memblock_unref(impl->mem);
127 	}
128 	free(control);
129 }
130 
131 SPA_EXPORT
pw_control_get_port(struct pw_control * control)132 struct pw_impl_port *pw_control_get_port(struct pw_control *control)
133 {
134 	return control->port;
135 }
136 
137 SPA_EXPORT
pw_control_add_listener(struct pw_control * control,struct spa_hook * listener,const struct pw_control_events * events,void * data)138 void pw_control_add_listener(struct pw_control *control,
139 			     struct spa_hook *listener,
140 			     const struct pw_control_events *events,
141 			     void *data)
142 {
143 	spa_hook_list_append(&control->listener_list, listener, events, data);
144 }
145 
port_set_io(struct pw_impl_port * port,uint32_t mix,uint32_t id,void * data,uint32_t size)146 static int port_set_io(struct pw_impl_port *port, uint32_t mix, uint32_t id, void *data, uint32_t size)
147 {
148 	int res;
149 
150 	if (port->mix) {
151 		res = spa_node_port_set_io(port->mix, port->direction, mix, id, data, size);
152 		if (SPA_RESULT_IS_OK(res))
153 			return res;
154 	}
155 
156 	if ((res = spa_node_port_set_io(port->node->node,
157 			port->direction, port->port_id,
158 			id, data, size)) < 0) {
159 		pw_log_warn("port %p: set io failed %d %s", port,
160 			res, spa_strerror(res));
161 	}
162 	return res;
163 }
164 
165 SPA_EXPORT
pw_control_add_link(struct pw_control * control,uint32_t cmix,struct pw_control * other,uint32_t omix,struct pw_control_link * link)166 int pw_control_add_link(struct pw_control *control, uint32_t cmix,
167 		struct pw_control *other, uint32_t omix,
168 		struct pw_control_link *link)
169 {
170 	int res = 0;
171 	struct impl *impl;
172 	uint32_t size;
173 
174 	if (control->direction == SPA_DIRECTION_INPUT) {
175 		SPA_SWAP(control, other);
176 		SPA_SWAP(cmix, omix);
177 	}
178 	if (control->direction != SPA_DIRECTION_OUTPUT ||
179 	    other->direction != SPA_DIRECTION_INPUT)
180 		return -EINVAL;
181 
182 	impl = SPA_CONTAINER_OF(control, struct impl, this);
183 
184 	pw_log_debug(NAME" %p: link to %p %s", control, other,
185 			spa_debug_type_find_name(spa_type_io, control->id));
186 
187 	size = SPA_MAX(control->size, other->size);
188 
189 	if (impl->mem == NULL) {
190 		impl->mem = pw_mempool_alloc(control->context->pool,
191 						PW_MEMBLOCK_FLAG_READWRITE |
192 						PW_MEMBLOCK_FLAG_SEAL |
193 						PW_MEMBLOCK_FLAG_MAP,
194 						SPA_DATA_MemFd, size);
195 		if (impl->mem == NULL) {
196 			res = -errno;
197 			goto exit;
198 		}
199 	}
200 
201 	if (spa_list_is_empty(&control->links)) {
202 		if (control->port) {
203 			if ((res = port_set_io(control->port, cmix,
204 						control->id,
205 						impl->mem->map->ptr, size)) < 0) {
206 				pw_log_warn(NAME" %p: set io failed %d %s", control,
207 					res, spa_strerror(res));
208 				goto exit;
209 			}
210 		}
211 	}
212 
213 	if (other->port) {
214 		if ((res = port_set_io(other->port, omix,
215 				other->id, impl->mem->map->ptr, size)) < 0) {
216 			pw_log_warn(NAME" %p: set io failed %d %s", control,
217 					res, spa_strerror(res));
218 			goto exit;
219 		}
220 	}
221 
222 	link->output = control;
223 	link->input = other;
224 	link->out_port = cmix;
225 	link->in_port = omix;
226 	link->valid = true;
227 	spa_list_append(&control->links, &link->out_link);
228 	spa_list_append(&other->links, &link->in_link);
229 
230 	pw_control_emit_linked(control, other);
231 	pw_control_emit_linked(other, control);
232 exit:
233 	return res;
234 }
235 
236 SPA_EXPORT
pw_control_remove_link(struct pw_control_link * link)237 int pw_control_remove_link(struct pw_control_link *link)
238 {
239 	int res = 0;
240 	struct pw_control *output = link->output;
241 	struct pw_control *input = link->input;
242 
243 	pw_log_debug(NAME" %p: unlink from %p", output, input);
244 
245 	spa_list_remove(&link->in_link);
246 	spa_list_remove(&link->out_link);
247 	link->valid = false;
248 
249 	if (spa_list_is_empty(&output->links)) {
250 		if ((res = port_set_io(output->port, link->out_port,
251 					output->id, NULL, 0)) < 0) {
252 			pw_log_warn(NAME" %p: can't unset port control io", output);
253 		}
254 	}
255 
256 	if (input->port) {
257 		if ((res = port_set_io(input->port, link->in_port,
258 				     input->id, NULL, 0)) < 0) {
259 			pw_log_warn(NAME" %p: can't unset port control io", output);
260 		}
261 	}
262 
263 	pw_control_emit_unlinked(output, input);
264 	pw_control_emit_unlinked(input, output);
265 
266 	return res;
267 }
268