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 <assert.h>
26
27 #include <pipewire/log.h>
28 #include <pipewire/proxy.h>
29 #include <pipewire/core.h>
30 #include <pipewire/private.h>
31 #include <pipewire/type.h>
32
33 #include <spa/debug/types.h>
34
35 PW_LOG_TOPIC_EXTERN(log_proxy);
36 #define PW_LOG_TOPIC_DEFAULT log_proxy
37
38 /** \cond */
39 struct proxy {
40 struct pw_proxy this;
41 };
42 /** \endcond */
43
pw_proxy_init(struct pw_proxy * proxy,const char * type,uint32_t version)44 int pw_proxy_init(struct pw_proxy *proxy, const char *type, uint32_t version)
45 {
46 int res;
47
48 proxy->refcount = 1;
49 proxy->type = type;
50 proxy->version = version;
51 proxy->bound_id = SPA_ID_INVALID;
52
53 proxy->id = pw_map_insert_new(&proxy->core->objects, proxy);
54 if (proxy->id == SPA_ID_INVALID) {
55 res = -errno;
56 pw_log_error("%p: can't allocate new id: %m", proxy);
57 goto error;
58 }
59
60 spa_hook_list_init(&proxy->listener_list);
61 spa_hook_list_init(&proxy->object_listener_list);
62
63 if ((res = pw_proxy_install_marshal(proxy, false)) < 0) {
64 pw_log_error("%p: no marshal for type %s/%d: %s", proxy,
65 type, version, spa_strerror(res));
66 goto error_clean;
67 }
68 proxy->in_map = true;
69 return 0;
70
71 error_clean:
72 pw_map_remove(&proxy->core->objects, proxy->id);
73 error:
74 return res;
75 }
76
77 /** Create a proxy object with a given id and type
78 *
79 * \param factory another proxy object that serves as a factory
80 * \param type Type of the proxy object
81 * \param version Interface version
82 * \param user_data_size size of user_data
83 * \return A newly allocated proxy object or NULL on failure
84 *
85 * This function creates a new proxy object with the supplied id and type. The
86 * proxy object will have an id assigned from the client id space.
87 *
88 * \sa pw_core
89 */
90 SPA_EXPORT
pw_proxy_new(struct pw_proxy * factory,const char * type,uint32_t version,size_t user_data_size)91 struct pw_proxy *pw_proxy_new(struct pw_proxy *factory,
92 const char *type, uint32_t version,
93 size_t user_data_size)
94 {
95 struct proxy *impl;
96 struct pw_proxy *this;
97 int res;
98
99 impl = calloc(1, sizeof(struct proxy) + user_data_size);
100 if (impl == NULL)
101 return NULL;
102
103 this = &impl->this;
104 this->core = factory->core;
105
106 if ((res = pw_proxy_init(this, type, version)) < 0)
107 goto error_init;
108
109 if (user_data_size > 0)
110 this->user_data = SPA_PTROFF(impl, sizeof(struct proxy), void);
111
112 pw_log_debug("%p: new %u type %s/%d core-proxy:%p, marshal:%p",
113 this, this->id, type, version, this->core, this->marshal);
114 return this;
115
116 error_init:
117 free(impl);
118 errno = -res;
119 return NULL;
120 }
121
122 SPA_EXPORT
pw_proxy_install_marshal(struct pw_proxy * this,bool implementor)123 int pw_proxy_install_marshal(struct pw_proxy *this, bool implementor)
124 {
125 struct pw_core *core = this->core;
126 const struct pw_protocol_marshal *marshal;
127
128 if (core == NULL)
129 return -EIO;
130
131 marshal = pw_protocol_get_marshal(core->conn->protocol,
132 this->type, this->version,
133 implementor ? PW_PROTOCOL_MARSHAL_FLAG_IMPL : 0);
134 if (marshal == NULL)
135 return -EPROTO;
136
137 this->marshal = marshal;
138 this->type = marshal->type;
139
140 this->impl = SPA_INTERFACE_INIT(
141 this->type,
142 this->marshal->version,
143 this->marshal->client_marshal, this);
144 return 0;
145 }
146
147 SPA_EXPORT
pw_proxy_get_user_data(struct pw_proxy * proxy)148 void *pw_proxy_get_user_data(struct pw_proxy *proxy)
149 {
150 return proxy->user_data;
151 }
152
153 SPA_EXPORT
pw_proxy_get_id(struct pw_proxy * proxy)154 uint32_t pw_proxy_get_id(struct pw_proxy *proxy)
155 {
156 return proxy->id;
157 }
158
159 SPA_EXPORT
pw_proxy_set_bound_id(struct pw_proxy * proxy,uint32_t global_id)160 int pw_proxy_set_bound_id(struct pw_proxy *proxy, uint32_t global_id)
161 {
162 proxy->bound_id = global_id;
163 pw_log_debug("%p: id:%d bound:%d", proxy, proxy->id, global_id);
164 pw_proxy_emit_bound(proxy, global_id);
165 return 0;
166 }
167
168 SPA_EXPORT
pw_proxy_get_bound_id(struct pw_proxy * proxy)169 uint32_t pw_proxy_get_bound_id(struct pw_proxy *proxy)
170 {
171 return proxy->bound_id;
172 }
173
174 SPA_EXPORT
pw_proxy_get_type(struct pw_proxy * proxy,uint32_t * version)175 const char *pw_proxy_get_type(struct pw_proxy *proxy, uint32_t *version)
176 {
177 if (version)
178 *version = proxy->version;
179 return proxy->type;
180 }
181
182 SPA_EXPORT
pw_proxy_get_core(struct pw_proxy * proxy)183 struct pw_core *pw_proxy_get_core(struct pw_proxy *proxy)
184 {
185 return proxy->core;
186 }
187
188 SPA_EXPORT
pw_proxy_get_protocol(struct pw_proxy * proxy)189 struct pw_protocol *pw_proxy_get_protocol(struct pw_proxy *proxy)
190 {
191 if (proxy->core == NULL || proxy->core->conn == NULL)
192 return NULL;
193 return proxy->core->conn->protocol;
194 }
195
196 SPA_EXPORT
pw_proxy_add_listener(struct pw_proxy * proxy,struct spa_hook * listener,const struct pw_proxy_events * events,void * data)197 void pw_proxy_add_listener(struct pw_proxy *proxy,
198 struct spa_hook *listener,
199 const struct pw_proxy_events *events,
200 void *data)
201 {
202 spa_hook_list_append(&proxy->listener_list, listener, events, data);
203 }
204
205 SPA_EXPORT
pw_proxy_add_object_listener(struct pw_proxy * proxy,struct spa_hook * listener,const void * funcs,void * data)206 void pw_proxy_add_object_listener(struct pw_proxy *proxy,
207 struct spa_hook *listener,
208 const void *funcs,
209 void *data)
210 {
211 spa_hook_list_append(&proxy->object_listener_list, listener, funcs, data);
212 }
213
remove_from_map(struct pw_proxy * proxy)214 static inline void remove_from_map(struct pw_proxy *proxy)
215 {
216 if (proxy->in_map) {
217 if (proxy->core)
218 pw_map_remove(&proxy->core->objects, proxy->id);
219 proxy->in_map = false;
220 }
221 }
222
223 /** Destroy a proxy object
224 *
225 * \param proxy Proxy object to destroy
226 *
227 * \note This is normally called by \ref pw_core when the server
228 * decides to destroy the server side object
229 */
230 SPA_EXPORT
pw_proxy_destroy(struct pw_proxy * proxy)231 void pw_proxy_destroy(struct pw_proxy *proxy)
232 {
233 pw_log_debug("%p: destroy id:%u removed:%u zombie:%u ref:%d", proxy,
234 proxy->id, proxy->removed, proxy->zombie, proxy->refcount);
235
236 assert(!proxy->destroyed);
237 proxy->destroyed = true;
238
239 if (!proxy->removed) {
240 /* if the server did not remove this proxy, schedule a
241 * destroy if we can */
242 if (proxy->core && !proxy->core->removed) {
243 pw_core_destroy(proxy->core, proxy);
244 proxy->refcount++;
245 } else {
246 proxy->removed = true;
247 }
248 }
249 if (proxy->removed)
250 remove_from_map(proxy);
251
252 if (!proxy->zombie) {
253 /* mark zombie and emit destroyed. No more
254 * events will be emitted on zombie objects */
255 proxy->zombie = true;
256 pw_proxy_emit_destroy(proxy);
257 }
258
259 spa_hook_list_clean(&proxy->listener_list);
260 spa_hook_list_clean(&proxy->object_listener_list);
261
262 pw_proxy_unref(proxy);
263 }
264
265 /** called when cleaning up or when the server removed the resource. Can
266 * be called multiple times */
pw_proxy_remove(struct pw_proxy * proxy)267 void pw_proxy_remove(struct pw_proxy *proxy)
268 {
269 assert(proxy->refcount > 0);
270
271 pw_log_debug("%p: remove id:%u removed:%u destroyed:%u zombie:%u ref:%d", proxy,
272 proxy->id, proxy->removed, proxy->destroyed, proxy->zombie,
273 proxy->refcount);
274
275 if (!proxy->destroyed)
276 proxy->refcount++;
277
278 if (!proxy->removed) {
279 /* mark removed and emit the removed signal only once and
280 * only when not already destroyed */
281 proxy->removed = true;
282 if (!proxy->destroyed)
283 pw_proxy_emit_removed(proxy);
284 }
285 if (proxy->destroyed)
286 remove_from_map(proxy);
287
288 pw_proxy_unref(proxy);
289 }
290
291 SPA_EXPORT
pw_proxy_unref(struct pw_proxy * proxy)292 void pw_proxy_unref(struct pw_proxy *proxy)
293 {
294 assert(proxy->refcount > 0);
295 if (--proxy->refcount > 0)
296 return;
297
298 pw_log_debug("%p: free %u", proxy, proxy->id);
299 /** client must explicitly destroy all proxies */
300 assert(proxy->destroyed);
301 free(proxy);
302 }
303
304 SPA_EXPORT
pw_proxy_ref(struct pw_proxy * proxy)305 void pw_proxy_ref(struct pw_proxy *proxy)
306 {
307 assert(proxy->refcount > 0);
308 proxy->refcount++;
309 }
310
311 SPA_EXPORT
pw_proxy_sync(struct pw_proxy * proxy,int seq)312 int pw_proxy_sync(struct pw_proxy *proxy, int seq)
313 {
314 int res = -EIO;
315 struct pw_core *core = proxy->core;
316
317 if (core && !core->removed) {
318 res = pw_core_sync(core, proxy->id, seq);
319 pw_log_debug("%p: %u seq:%d sync %u", proxy, proxy->id, seq, res);
320 }
321 return res;
322 }
323
324 SPA_EXPORT
pw_proxy_errorf(struct pw_proxy * proxy,int res,const char * error,...)325 int pw_proxy_errorf(struct pw_proxy *proxy, int res, const char *error, ...)
326 {
327 va_list ap;
328 int r = -EIO;
329 struct pw_core *core = proxy->core;
330
331 va_start(ap, error);
332 if (core && !core->removed)
333 r = pw_core_errorv(core, proxy->id,
334 core->recv_seq, res, error, ap);
335 va_end(ap);
336 return r;
337 }
338
339 SPA_EXPORT
pw_proxy_error(struct pw_proxy * proxy,int res,const char * error)340 int pw_proxy_error(struct pw_proxy *proxy, int res, const char *error)
341 {
342 int r = -EIO;
343 struct pw_core *core = proxy->core;
344
345 if (core && !core->removed)
346 r = pw_core_error(core, proxy->id,
347 core->recv_seq, res, error);
348 return r;
349 }
350
351 SPA_EXPORT
pw_proxy_get_object_listeners(struct pw_proxy * proxy)352 struct spa_hook_list *pw_proxy_get_object_listeners(struct pw_proxy *proxy)
353 {
354 return &proxy->object_listener_list;
355 }
356
357 SPA_EXPORT
pw_proxy_get_marshal(struct pw_proxy * proxy)358 const struct pw_protocol_marshal *pw_proxy_get_marshal(struct pw_proxy *proxy)
359 {
360 return proxy->marshal;
361 }
362