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 <errno.h>
26 
27 #include <spa/debug/types.h>
28 #include <spa/utils/string.h>
29 
30 #include <pipewire/protocol.h>
31 #include <pipewire/private.h>
32 #include <pipewire/type.h>
33 
34 PW_LOG_TOPIC_EXTERN(log_protocol);
35 #define PW_LOG_TOPIC_DEFAULT log_protocol
36 
37 /** \cond */
38 struct impl {
39 	struct pw_protocol this;
40 };
41 
42 struct marshal {
43 	struct spa_list link;
44 	const struct pw_protocol_marshal *marshal;
45 };
46 /** \endcond */
47 
48 SPA_EXPORT
pw_protocol_new(struct pw_context * context,const char * name,size_t user_data_size)49 struct pw_protocol *pw_protocol_new(struct pw_context *context,
50 				    const char *name,
51 				    size_t user_data_size)
52 {
53 	struct pw_protocol *protocol;
54 
55 	protocol = calloc(1, sizeof(struct impl) + user_data_size);
56 	if (protocol == NULL)
57 		return NULL;
58 
59 	protocol->context = context;
60 	protocol->name = strdup(name);
61 
62 	spa_list_init(&protocol->marshal_list);
63 	spa_list_init(&protocol->server_list);
64 	spa_list_init(&protocol->client_list);
65 	spa_hook_list_init(&protocol->listener_list);
66 
67 	if (user_data_size > 0)
68 		protocol->user_data = SPA_PTROFF(protocol, sizeof(struct impl), void);
69 
70 	spa_list_append(&context->protocol_list, &protocol->link);
71 
72 	pw_log_debug("%p: Created protocol %s", protocol, name);
73 
74 	return protocol;
75 }
76 
77 SPA_EXPORT
pw_protocol_get_context(struct pw_protocol * protocol)78 struct pw_context *pw_protocol_get_context(struct pw_protocol *protocol)
79 {
80 	return protocol->context;
81 }
82 
83 SPA_EXPORT
pw_protocol_get_user_data(struct pw_protocol * protocol)84 void *pw_protocol_get_user_data(struct pw_protocol *protocol)
85 {
86 	return protocol->user_data;
87 }
88 
89 SPA_EXPORT
90 const struct pw_protocol_implementation *
pw_protocol_get_implementation(struct pw_protocol * protocol)91 pw_protocol_get_implementation(struct pw_protocol *protocol)
92 {
93 	return protocol->implementation;
94 }
95 
96 SPA_EXPORT
97 const void *
pw_protocol_get_extension(struct pw_protocol * protocol)98 pw_protocol_get_extension(struct pw_protocol *protocol)
99 {
100 	return protocol->extension;
101 }
102 
103 SPA_EXPORT
pw_protocol_destroy(struct pw_protocol * protocol)104 void pw_protocol_destroy(struct pw_protocol *protocol)
105 {
106 	struct impl *impl = SPA_CONTAINER_OF(protocol, struct impl, this);
107 	struct marshal *marshal, *t1;
108 	struct pw_protocol_server *server;
109 	struct pw_protocol_client *client;
110 
111 	pw_log_debug("%p: destroy", protocol);
112 	pw_protocol_emit_destroy(protocol);
113 
114 	spa_hook_list_clean(&protocol->listener_list);
115 
116 	spa_list_remove(&protocol->link);
117 
118 	spa_list_consume(server, &protocol->server_list, link)
119 		pw_protocol_server_destroy(server);
120 
121 	spa_list_consume(client, &protocol->client_list, link)
122 		pw_protocol_client_destroy(client);
123 
124 	spa_list_for_each_safe(marshal, t1, &protocol->marshal_list, link)
125 		free(marshal);
126 
127 	free(protocol->name);
128 
129 	free(impl);
130 }
131 
132 SPA_EXPORT
pw_protocol_add_listener(struct pw_protocol * protocol,struct spa_hook * listener,const struct pw_protocol_events * events,void * data)133 void pw_protocol_add_listener(struct pw_protocol *protocol,
134                               struct spa_hook *listener,
135                               const struct pw_protocol_events *events,
136                               void *data)
137 {
138 	spa_hook_list_append(&protocol->listener_list, listener, events, data);
139 }
140 
141 SPA_EXPORT
142 int
pw_protocol_add_marshal(struct pw_protocol * protocol,const struct pw_protocol_marshal * marshal)143 pw_protocol_add_marshal(struct pw_protocol *protocol,
144 			const struct pw_protocol_marshal *marshal)
145 {
146 	struct marshal *impl;
147 
148 	impl = calloc(1, sizeof(struct marshal));
149 	if (impl == NULL)
150 		return -errno;
151 
152 	impl->marshal = marshal;
153 
154 	spa_list_append(&protocol->marshal_list, &impl->link);
155 
156 	pw_log_debug("%p: Add marshal %s/%d to protocol %s", protocol,
157 			marshal->type, marshal->version, protocol->name);
158 
159 	return 0;
160 }
161 
162 SPA_EXPORT
163 const struct pw_protocol_marshal *
pw_protocol_get_marshal(struct pw_protocol * protocol,const char * type,uint32_t version,uint32_t flags)164 pw_protocol_get_marshal(struct pw_protocol *protocol, const char *type, uint32_t version, uint32_t flags)
165 {
166 	struct marshal *impl;
167 
168 	spa_list_for_each(impl, &protocol->marshal_list, link) {
169 		if (spa_streq(impl->marshal->type, type) &&
170 		    (impl->marshal->flags & flags) == flags)
171                         return impl->marshal;
172         }
173 	pw_log_debug("%p: No marshal %s/%d for protocol %s", protocol,
174 			type, version, protocol->name);
175 	return NULL;
176 }
177 
178 SPA_EXPORT
pw_context_find_protocol(struct pw_context * context,const char * name)179 struct pw_protocol *pw_context_find_protocol(struct pw_context *context, const char *name)
180 {
181 	struct pw_protocol *protocol;
182 
183 	spa_list_for_each(protocol, &context->protocol_list, link) {
184 		if (spa_streq(protocol->name, name))
185 			return protocol;
186 	}
187 	return NULL;
188 }
189