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