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 <string.h>
26
27 #include "pipewire/private.h"
28 #include "pipewire/protocol.h"
29 #include "pipewire/resource.h"
30 #include "pipewire/type.h"
31
32 #include <spa/debug/types.h>
33
34 PW_LOG_TOPIC_EXTERN(log_device);
35 #define PW_LOG_TOPIC_DEFAULT log_device
36
37 /** \cond */
38 struct impl {
39 struct pw_resource this;
40 };
41 /** \endcond */
42
43 SPA_EXPORT
pw_resource_new(struct pw_impl_client * client,uint32_t id,uint32_t permissions,const char * type,uint32_t version,size_t user_data_size)44 struct pw_resource *pw_resource_new(struct pw_impl_client *client,
45 uint32_t id,
46 uint32_t permissions,
47 const char *type,
48 uint32_t version,
49 size_t user_data_size)
50 {
51 struct impl *impl;
52 struct pw_resource *this;
53 int res;
54
55 impl = calloc(1, sizeof(struct impl) + user_data_size);
56 if (impl == NULL)
57 return NULL;
58
59 this = &impl->this;
60 this->context = client->context;
61 this->client = client;
62 this->permissions = permissions;
63 this->type = type;
64 this->version = version;
65 this->bound_id = SPA_ID_INVALID;
66
67 spa_hook_list_init(&this->listener_list);
68 spa_hook_list_init(&this->object_listener_list);
69
70 if (id == SPA_ID_INVALID) {
71 res = -EINVAL;
72 goto error_clean;
73 }
74
75 if ((res = pw_map_insert_at(&client->objects, id, this)) < 0) {
76 pw_log_error("%p: can't add id %u for client %p: %s",
77 this, id, client, spa_strerror(res));
78 goto error_clean;
79 }
80 this->id = id;
81
82 if ((res = pw_resource_install_marshal(this, false)) < 0) {
83 pw_log_error("%p: no marshal for type %s/%d: %s", this,
84 type, version, spa_strerror(res));
85 goto error_clean;
86 }
87
88
89 if (user_data_size > 0)
90 this->user_data = SPA_PTROFF(impl, sizeof(struct impl), void);
91
92 pw_log_debug("%p: new %u type %s/%d client:%p marshal:%p",
93 this, id, type, version, client, this->marshal);
94
95 pw_impl_client_emit_resource_added(client, this);
96
97 return this;
98
99 error_clean:
100 free(impl);
101 errno = -res;
102 return NULL;
103 }
104
105 SPA_EXPORT
pw_resource_install_marshal(struct pw_resource * this,bool implementor)106 int pw_resource_install_marshal(struct pw_resource *this, bool implementor)
107 {
108 struct pw_impl_client *client = this->client;
109 const struct pw_protocol_marshal *marshal;
110
111 marshal = pw_protocol_get_marshal(client->protocol,
112 this->type, this->version,
113 implementor ? PW_PROTOCOL_MARSHAL_FLAG_IMPL : 0);
114 if (marshal == NULL)
115 return -EPROTO;
116
117 this->marshal = marshal;
118 this->type = marshal->type;
119
120 this->impl = SPA_INTERFACE_INIT(
121 this->type,
122 this->marshal->version,
123 this->marshal->server_marshal, this);
124 return 0;
125 }
126
127 SPA_EXPORT
pw_resource_get_client(struct pw_resource * resource)128 struct pw_impl_client *pw_resource_get_client(struct pw_resource *resource)
129 {
130 return resource->client;
131 }
132
133 SPA_EXPORT
pw_resource_get_id(struct pw_resource * resource)134 uint32_t pw_resource_get_id(struct pw_resource *resource)
135 {
136 return resource->id;
137 }
138
139 SPA_EXPORT
pw_resource_get_permissions(struct pw_resource * resource)140 uint32_t pw_resource_get_permissions(struct pw_resource *resource)
141 {
142 return resource->permissions;
143 }
144
145 SPA_EXPORT
pw_resource_get_type(struct pw_resource * resource,uint32_t * version)146 const char *pw_resource_get_type(struct pw_resource *resource, uint32_t *version)
147 {
148 if (version)
149 *version = resource->version;
150 return resource->type;
151 }
152
153 SPA_EXPORT
pw_resource_get_protocol(struct pw_resource * resource)154 struct pw_protocol *pw_resource_get_protocol(struct pw_resource *resource)
155 {
156 return resource->client->protocol;
157 }
158
159 SPA_EXPORT
pw_resource_get_user_data(struct pw_resource * resource)160 void *pw_resource_get_user_data(struct pw_resource *resource)
161 {
162 return resource->user_data;
163 }
164
165 SPA_EXPORT
pw_resource_add_listener(struct pw_resource * resource,struct spa_hook * listener,const struct pw_resource_events * events,void * data)166 void pw_resource_add_listener(struct pw_resource *resource,
167 struct spa_hook *listener,
168 const struct pw_resource_events *events,
169 void *data)
170 {
171 spa_hook_list_append(&resource->listener_list, listener, events, data);
172 }
173
174 SPA_EXPORT
pw_resource_add_object_listener(struct pw_resource * resource,struct spa_hook * listener,const void * funcs,void * data)175 void pw_resource_add_object_listener(struct pw_resource *resource,
176 struct spa_hook *listener,
177 const void *funcs,
178 void *data)
179 {
180 spa_hook_list_append(&resource->object_listener_list, listener, funcs, data);
181 }
182
183 SPA_EXPORT
pw_resource_get_object_listeners(struct pw_resource * resource)184 struct spa_hook_list *pw_resource_get_object_listeners(struct pw_resource *resource)
185 {
186 return &resource->object_listener_list;
187 }
188
189 SPA_EXPORT
pw_resource_get_marshal(struct pw_resource * resource)190 const struct pw_protocol_marshal *pw_resource_get_marshal(struct pw_resource *resource)
191 {
192 return resource->marshal;
193 }
194
195 SPA_EXPORT
pw_resource_ping(struct pw_resource * resource,int seq)196 int pw_resource_ping(struct pw_resource *resource, int seq)
197 {
198 int res = -EIO;
199 struct pw_impl_client *client = resource->client;
200
201 if (client->core_resource != NULL) {
202 pw_core_resource_ping(client->core_resource, resource->id, seq);
203 res = client->send_seq;
204 pw_log_debug("%p: %u seq:%d ping %d", resource, resource->id, seq, res);
205 }
206 return res;
207 }
208
209 SPA_EXPORT
pw_resource_set_bound_id(struct pw_resource * resource,uint32_t global_id)210 int pw_resource_set_bound_id(struct pw_resource *resource, uint32_t global_id)
211 {
212 struct pw_impl_client *client = resource->client;
213
214 resource->bound_id = global_id;
215 if (client->core_resource != NULL) {
216 pw_log_debug("%p: %u global_id:%u", resource, resource->id, global_id);
217 pw_core_resource_bound_id(client->core_resource, resource->id, global_id);
218 }
219 return 0;
220 }
221
222 SPA_EXPORT
pw_resource_get_bound_id(struct pw_resource * resource)223 uint32_t pw_resource_get_bound_id(struct pw_resource *resource)
224 {
225 return resource->bound_id;
226 }
227
228 static void SPA_PRINTF_FUNC(4, 0)
pw_resource_errorv_id(struct pw_resource * resource,uint32_t id,int res,const char * error,va_list ap)229 pw_resource_errorv_id(struct pw_resource *resource, uint32_t id, int res, const char *error, va_list ap)
230 {
231 struct pw_impl_client *client = resource->client;
232 if (client->core_resource != NULL)
233 pw_core_resource_errorv(client->core_resource,
234 id, client->recv_seq, res, error, ap);
235 }
236
237 SPA_EXPORT
pw_resource_errorf(struct pw_resource * resource,int res,const char * error,...)238 void pw_resource_errorf(struct pw_resource *resource, int res, const char *error, ...)
239 {
240 va_list ap;
241 va_start(ap, error);
242 pw_resource_errorv_id(resource, resource->id, res, error, ap);
243 va_end(ap);
244 }
245
246 SPA_EXPORT
pw_resource_errorf_id(struct pw_resource * resource,uint32_t id,int res,const char * error,...)247 void pw_resource_errorf_id(struct pw_resource *resource, uint32_t id, int res, const char *error, ...)
248 {
249 va_list ap;
250 va_start(ap, error);
251 pw_resource_errorv_id(resource, id, res, error, ap);
252 va_end(ap);
253 }
254
255 SPA_EXPORT
pw_resource_error(struct pw_resource * resource,int res,const char * error)256 void pw_resource_error(struct pw_resource *resource, int res, const char *error)
257 {
258 struct pw_impl_client *client = resource->client;
259 if (client->core_resource != NULL)
260 pw_core_resource_error(client->core_resource,
261 resource->id, client->recv_seq, res, error);
262 }
263
264 SPA_EXPORT
pw_resource_destroy(struct pw_resource * resource)265 void pw_resource_destroy(struct pw_resource *resource)
266 {
267 struct pw_impl_client *client = resource->client;
268
269 if (resource->global) {
270 spa_list_remove(&resource->link);
271 resource->global = NULL;
272 }
273
274 pw_log_debug("%p: destroy %u", resource, resource->id);
275 pw_resource_emit_destroy(resource);
276
277 pw_map_insert_at(&client->objects, resource->id, NULL);
278 pw_impl_client_emit_resource_removed(client, resource);
279
280 if (client->core_resource && !resource->removed)
281 pw_core_resource_remove_id(client->core_resource, resource->id);
282
283 pw_log_debug("%p: free %u", resource, resource->id);
284
285 spa_hook_list_clean(&resource->listener_list);
286 spa_hook_list_clean(&resource->object_listener_list);
287
288 free(resource);
289 }
290
291 SPA_EXPORT
pw_resource_remove(struct pw_resource * resource)292 void pw_resource_remove(struct pw_resource *resource)
293 {
294 resource->removed = true;
295 pw_resource_destroy(resource);
296 }
297