1 #include <stdint.h>
2 
3 #include <regex.h>
4 
5 #include <spa/param/props.h>
6 #include <spa/pod/builder.h>
7 #include <spa/pod/pod.h>
8 #include <spa/utils/defs.h>
9 #include <spa/utils/json.h>
10 #include <spa/utils/string.h>
11 
12 #include <pipewire/pipewire.h>
13 
14 #include "collect.h"
15 #include "log.h"
16 #include "manager.h"
17 #include "message-handler.h"
18 
bluez_card_object_message_handler(struct pw_manager * m,struct pw_manager_object * o,const char * message,const char * params,char ** response)19 static int bluez_card_object_message_handler(struct pw_manager *m, struct pw_manager_object *o, const char *message, const char *params, char **response)
20 {
21 	struct transport_codec_info codecs[64];
22 	uint32_t n_codecs, active;
23 
24 	pw_log_debug(": bluez-card %p object message:'%s' params:'%s'", o, message, params);
25 
26 	n_codecs = collect_transport_codec_info(o, codecs, SPA_N_ELEMENTS(codecs), &active);
27 
28 	if (n_codecs == 0)
29 		return -EINVAL;
30 
31 	if (spa_streq(message, "switch-codec")) {
32 		char codec[256];
33 		struct spa_json it;
34 		char buf[1024];
35 		struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
36 		struct spa_pod_frame f[1];
37 		struct spa_pod *param;
38 		uint32_t codec_id = SPA_ID_INVALID;
39 
40 		/* Parse args */
41 		if (params == NULL)
42 			return -EINVAL;
43 
44 		spa_json_init(&it, params, strlen(params));
45 		if (spa_json_get_string(&it, codec, sizeof(codec)) <= 0)
46 			return -EINVAL;
47 
48 		codec_id = atoi(codec);
49 
50 		/* Switch codec */
51 		spa_pod_builder_push_object(&b, &f[0],
52 				SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
53 		spa_pod_builder_add(&b,
54 				SPA_PROP_bluetoothAudioCodec, SPA_POD_Id(codec_id), 0);
55 		param = spa_pod_builder_pop(&b, &f[0]);
56 
57 		pw_device_set_param((struct pw_device *)o->proxy,
58 				SPA_PARAM_Props, 0, param);
59 		return 0;
60 	} else if (spa_streq(message, "list-codecs")) {
61 		uint32_t i;
62 		FILE *r;
63 		size_t size;
64 		bool first = true;
65 
66 		r = open_memstream(response, &size);
67 		if (r == NULL)
68 			return -ENOMEM;
69 
70 		fputc('[', r);
71 		for (i = 0; i < n_codecs; ++i) {
72 			const char *desc = codecs[i].description;
73 			fprintf(r, "%s{\"name\":\"%d\",\"description\":\"%s\"}",
74 					first ? "" : ",",
75 					(int)codecs[i].id, desc ? desc : "Unknown");
76 			first = false;
77 		}
78 		fputc(']', r);
79 
80 		return fclose(r) ? -errno : 0;
81 	} else if (spa_streq(message, "get-codec")) {
82 		if (active == SPA_ID_INVALID)
83 			*response = strdup("null");
84 		else
85 			*response = spa_aprintf("\"%d\"", (int)codecs[active].id);
86 		return *response ? 0 : -ENOMEM;
87 	}
88 
89 	return -ENOSYS;
90 }
91 
core_object_message_handler(struct pw_manager * m,struct pw_manager_object * o,const char * message,const char * params,char ** response)92 static int core_object_message_handler(struct pw_manager *m, struct pw_manager_object *o, const char *message, const char *params, char **response)
93 {
94 	pw_log_debug(": core %p object message:'%s' params:'%s'", o, message, params);
95 
96 	if (spa_streq(message, "list-handlers")) {
97 		FILE *r;
98 		size_t size;
99 		bool first = true;
100 
101 		r = open_memstream(response, &size);
102 		if (r == NULL)
103 			return -ENOMEM;
104 
105 		fputc('[', r);
106 		spa_list_for_each(o, &m->object_list, link) {
107 			if (o->message_object_path) {
108 				fprintf(r, "%s{\"name\":\"%s\",\"description\":\"%s\"}",
109 						first ? "" : ",",
110 						o->message_object_path, o->type);
111 				first = false;
112 			}
113 		}
114 		fputc(']', r);
115 		return fclose(r) ? -errno : 0;
116 	}
117 
118 	return -ENOSYS;
119 }
120 
register_object_message_handlers(struct pw_manager_object * o)121 void register_object_message_handlers(struct pw_manager_object *o)
122 {
123 	const char *str;
124 
125 	if (o->id == 0) {
126 		free(o->message_object_path);
127 		o->message_object_path = strdup("/core");
128 		o->message_handler = core_object_message_handler;
129 		return;
130 	}
131 
132 	if (pw_manager_object_is_card(o) && o->props != NULL &&
133 	    (str = pw_properties_get(o->props, PW_KEY_DEVICE_API)) != NULL &&
134 	    spa_streq(str, "bluez5")) {
135 		str = pw_properties_get(o->props, PW_KEY_DEVICE_NAME);
136 		if (str) {
137 			free(o->message_object_path);
138 			o->message_object_path = spa_aprintf("/card/%s/bluez", str);
139 			o->message_handler = bluez_card_object_message_handler;
140 		}
141 		return;
142 	}
143 }
144