1 /* sdp_client.c - Bluetooth classic SDP client smoke test */
2 
3 /*
4  * Copyright 2024 NXP
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 #include <stdlib.h>
9 
10 #include <errno.h>
11 #include <zephyr/types.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/sys/printk.h>
14 #include <zephyr/sys/byteorder.h>
15 
16 #include <zephyr/bluetooth/bluetooth.h>
17 #include <zephyr/bluetooth/conn.h>
18 #include <zephyr/bluetooth/classic/rfcomm.h>
19 #include <zephyr/bluetooth/classic/sdp.h>
20 
21 #include <zephyr/shell/shell.h>
22 
23 static const struct shell *ctx_shell;
24 
25 extern struct bt_conn *default_conn;
26 
27 static struct bt_sdp_discover_params sdp_discover;
28 static union {
29 	struct bt_uuid_16 u16;
30 	struct bt_uuid_32 u32;
31 	struct bt_uuid_128 u128;
32 } sdp_discover_uuid;
33 
34 const uint16_t svclass_list[] = {
35 	BT_SDP_SDP_SERVER_SVCLASS,
36 	BT_SDP_BROWSE_GRP_DESC_SVCLASS,
37 	BT_SDP_PUBLIC_BROWSE_GROUP,
38 	BT_SDP_SERIAL_PORT_SVCLASS,
39 	BT_SDP_LAN_ACCESS_SVCLASS,
40 	BT_SDP_DIALUP_NET_SVCLASS,
41 	BT_SDP_IRMC_SYNC_SVCLASS,
42 	BT_SDP_OBEX_OBJPUSH_SVCLASS,
43 	BT_SDP_OBEX_FILETRANS_SVCLASS,
44 	BT_SDP_IRMC_SYNC_CMD_SVCLASS,
45 	BT_SDP_HEADSET_SVCLASS,
46 	BT_SDP_CORDLESS_TELEPHONY_SVCLASS,
47 	BT_SDP_AUDIO_SOURCE_SVCLASS,
48 	BT_SDP_AUDIO_SINK_SVCLASS,
49 	BT_SDP_AV_REMOTE_TARGET_SVCLASS,
50 	BT_SDP_ADVANCED_AUDIO_SVCLASS,
51 	BT_SDP_AV_REMOTE_SVCLASS,
52 	BT_SDP_AV_REMOTE_CONTROLLER_SVCLASS,
53 	BT_SDP_INTERCOM_SVCLASS,
54 	BT_SDP_FAX_SVCLASS,
55 	BT_SDP_HEADSET_AGW_SVCLASS,
56 	BT_SDP_WAP_SVCLASS,
57 	BT_SDP_WAP_CLIENT_SVCLASS,
58 	BT_SDP_PANU_SVCLASS,
59 	BT_SDP_NAP_SVCLASS,
60 	BT_SDP_GN_SVCLASS,
61 	BT_SDP_DIRECT_PRINTING_SVCLASS,
62 	BT_SDP_REFERENCE_PRINTING_SVCLASS,
63 	BT_SDP_IMAGING_SVCLASS,
64 	BT_SDP_IMAGING_RESPONDER_SVCLASS,
65 	BT_SDP_IMAGING_ARCHIVE_SVCLASS,
66 	BT_SDP_IMAGING_REFOBJS_SVCLASS,
67 	BT_SDP_HANDSFREE_SVCLASS,
68 	BT_SDP_HANDSFREE_AGW_SVCLASS,
69 	BT_SDP_DIRECT_PRT_REFOBJS_SVCLASS,
70 	BT_SDP_REFLECTED_UI_SVCLASS,
71 	BT_SDP_BASIC_PRINTING_SVCLASS,
72 	BT_SDP_PRINTING_STATUS_SVCLASS,
73 	BT_SDP_HID_SVCLASS,
74 	BT_SDP_HCR_SVCLASS,
75 	BT_SDP_HCR_PRINT_SVCLASS,
76 	BT_SDP_HCR_SCAN_SVCLASS,
77 	BT_SDP_CIP_SVCLASS,
78 	BT_SDP_VIDEO_CONF_GW_SVCLASS,
79 	BT_SDP_UDI_MT_SVCLASS,
80 	BT_SDP_UDI_TA_SVCLASS,
81 	BT_SDP_AV_SVCLASS,
82 	BT_SDP_SAP_SVCLASS,
83 	BT_SDP_PBAP_PCE_SVCLASS,
84 	BT_SDP_PBAP_PSE_SVCLASS,
85 	BT_SDP_PBAP_SVCLASS,
86 	BT_SDP_MAP_MSE_SVCLASS,
87 	BT_SDP_MAP_MCE_SVCLASS,
88 	BT_SDP_MAP_SVCLASS,
89 	BT_SDP_GNSS_SVCLASS,
90 	BT_SDP_GNSS_SERVER_SVCLASS,
91 	BT_SDP_MPS_SC_SVCLASS,
92 	BT_SDP_MPS_SVCLASS,
93 	BT_SDP_PNP_INFO_SVCLASS,
94 	BT_SDP_GENERIC_NETWORKING_SVCLASS,
95 	BT_SDP_GENERIC_FILETRANS_SVCLASS,
96 	BT_SDP_GENERIC_AUDIO_SVCLASS,
97 	BT_SDP_GENERIC_TELEPHONY_SVCLASS,
98 	BT_SDP_UPNP_SVCLASS,
99 	BT_SDP_UPNP_IP_SVCLASS,
100 	BT_SDP_UPNP_PAN_SVCLASS,
101 	BT_SDP_UPNP_LAP_SVCLASS,
102 	BT_SDP_UPNP_L2CAP_SVCLASS,
103 	BT_SDP_VIDEO_SOURCE_SVCLASS,
104 	BT_SDP_VIDEO_SINK_SVCLASS,
105 	BT_SDP_VIDEO_DISTRIBUTION_SVCLASS,
106 	BT_SDP_HDP_SVCLASS,
107 	BT_SDP_HDP_SOURCE_SVCLASS,
108 	BT_SDP_HDP_SINK_SVCLASS,
109 	BT_SDP_GENERIC_ACCESS_SVCLASS,
110 	BT_SDP_GENERIC_ATTRIB_SVCLASS,
111 	BT_SDP_APPLE_AGENT_SVCLASS,
112 };
113 
114 #define SDP_CLIENT_USER_BUF_LEN 4096
115 NET_BUF_POOL_FIXED_DEFINE(sdp_client_pool, 1, SDP_CLIENT_USER_BUF_LEN,
116 			  CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
117 
118 static bool sdp_record_found;
119 
sdp_discover_func(struct bt_conn * conn,struct bt_sdp_client_result * result,const struct bt_sdp_discover_params * params)120 static uint8_t sdp_discover_func(struct bt_conn *conn, struct bt_sdp_client_result *result,
121 				 const struct bt_sdp_discover_params *params)
122 {
123 	int err;
124 	uint16_t param;
125 
126 	if ((result == NULL) || (result->resp_buf == NULL) || (result->resp_buf->len == 0)) {
127 		if (sdp_record_found) {
128 			sdp_record_found = false;
129 			printk("SDP Discovery Done\n");
130 		} else {
131 			printk("No SDP Record\n");
132 		}
133 		return BT_SDP_DISCOVER_UUID_STOP;
134 	}
135 
136 	sdp_record_found = true;
137 
138 	printk("SDP Rsp Data:\n");
139 	err = bt_sdp_get_proto_param(result->resp_buf, BT_SDP_PROTO_L2CAP, &param);
140 	if (!err) {
141 		printk("    PROTOCOL: L2CAP: %d\n", param);
142 	}
143 	err = bt_sdp_get_proto_param(result->resp_buf, BT_SDP_PROTO_RFCOMM, &param);
144 	if (!err) {
145 		printk("    PROTOCOL: RFCOMM: %d\n", param);
146 	}
147 	for (size_t i = 0; i < ARRAY_SIZE(svclass_list); i++) {
148 		err = bt_sdp_get_profile_version(result->resp_buf, svclass_list[i], &param);
149 		if (!err) {
150 			printk("    VERSION: %04X: %d\n", svclass_list[i], param);
151 		}
152 	}
153 	err = bt_sdp_get_features(result->resp_buf, &param);
154 	if (!err) {
155 		printk("    FEATURE: %04X\n", param);
156 	}
157 	printk("    RAW:");
158 	for (uint16_t i = 0; i < result->resp_buf->len; i++) {
159 		printk("%02X", result->resp_buf->data[i]);
160 	}
161 	printk("\n");
162 
163 	if (!result->next_record_hint) {
164 		sdp_record_found = false;
165 		printk("SDP Discovery Done\n");
166 	}
167 
168 	return BT_SDP_DISCOVER_UUID_CONTINUE;
169 }
170 
cmd_ssa_discovery(const struct shell * sh,size_t argc,char * argv[])171 static int cmd_ssa_discovery(const struct shell *sh, size_t argc, char *argv[])
172 {
173 	int err;
174 	size_t len;
175 
176 	ctx_shell = sh;
177 
178 	len = strlen(argv[1]);
179 
180 	if (len == (BT_UUID_SIZE_16 * 2)) {
181 		uint16_t val;
182 
183 		sdp_discover_uuid.u16.uuid.type = BT_UUID_TYPE_16;
184 		hex2bin(argv[1], len, (uint8_t *)&val, sizeof(val));
185 		sdp_discover_uuid.u16.val = sys_be16_to_cpu(val);
186 		sdp_discover.uuid = &sdp_discover_uuid.u16.uuid;
187 	} else if (len == (BT_UUID_SIZE_32 * 2)) {
188 		uint32_t val;
189 
190 		sdp_discover_uuid.u32.uuid.type = BT_UUID_TYPE_32;
191 		hex2bin(argv[1], len, (uint8_t *)&val, sizeof(val));
192 		sdp_discover_uuid.u32.val = sys_be32_to_cpu(val);
193 		sdp_discover.uuid = &sdp_discover_uuid.u32.uuid;
194 	} else if (len == (BT_UUID_SIZE_128 * 2)) {
195 		sdp_discover_uuid.u128.uuid.type = BT_UUID_TYPE_128;
196 		hex2bin(argv[1], len, &sdp_discover_uuid.u128.val[0],
197 			sizeof(sdp_discover_uuid.u128.val));
198 		sdp_discover.uuid = &sdp_discover_uuid.u128.uuid;
199 	} else {
200 		shell_error(sh, "Invalid UUID");
201 		return -ENOEXEC;
202 	}
203 
204 	sdp_discover.func = sdp_discover_func;
205 	sdp_discover.pool = &sdp_client_pool;
206 	sdp_discover.type = BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR;
207 
208 	err = bt_sdp_discover(default_conn, &sdp_discover);
209 	if (err) {
210 		shell_error(ctx_shell, "Fail to start SDP Discovery (err %d)", err);
211 		return err;
212 	}
213 	return 0;
214 }
215 
cmd_ss_discovery(const struct shell * sh,size_t argc,char * argv[])216 static int cmd_ss_discovery(const struct shell *sh, size_t argc, char *argv[])
217 {
218 	int err;
219 	size_t len;
220 
221 	ctx_shell = sh;
222 
223 	len = strlen(argv[1]);
224 
225 	if (len == (BT_UUID_SIZE_16 * 2)) {
226 		uint16_t val;
227 
228 		sdp_discover_uuid.u16.uuid.type = BT_UUID_TYPE_16;
229 		hex2bin(argv[1], len, (uint8_t *)&val, sizeof(val));
230 		sdp_discover_uuid.u16.val = sys_be16_to_cpu(val);
231 		sdp_discover.uuid = &sdp_discover_uuid.u16.uuid;
232 	} else if (len == (BT_UUID_SIZE_32 * 2)) {
233 		uint32_t val;
234 
235 		sdp_discover_uuid.u32.uuid.type = BT_UUID_TYPE_32;
236 		hex2bin(argv[1], len, (uint8_t *)&val, sizeof(val));
237 		sdp_discover_uuid.u32.val = sys_be32_to_cpu(val);
238 		sdp_discover.uuid = &sdp_discover_uuid.u32.uuid;
239 	} else if (len == (BT_UUID_SIZE_128 * 2)) {
240 		sdp_discover_uuid.u128.uuid.type = BT_UUID_TYPE_128;
241 		hex2bin(argv[1], len, &sdp_discover_uuid.u128.val[0],
242 			sizeof(sdp_discover_uuid.u128.val));
243 		sdp_discover.uuid = &sdp_discover_uuid.u128.uuid;
244 	} else {
245 		shell_error(sh, "Invalid UUID");
246 		return -ENOEXEC;
247 	}
248 
249 	sdp_discover.func = sdp_discover_func;
250 	sdp_discover.pool = &sdp_client_pool;
251 	sdp_discover.type = BT_SDP_DISCOVER_SERVICE_SEARCH;
252 
253 	err = bt_sdp_discover(default_conn, &sdp_discover);
254 	if (err) {
255 		shell_error(ctx_shell, "Fail to start SDP Discovery (err %d)", err);
256 		return err;
257 	}
258 	return 0;
259 }
260 
cmd_sa_discovery(const struct shell * sh,size_t argc,char * argv[])261 static int cmd_sa_discovery(const struct shell *sh, size_t argc, char *argv[])
262 {
263 	int err;
264 	size_t len;
265 	uint32_t handle;
266 
267 	ctx_shell = sh;
268 
269 	len = strlen(argv[1]);
270 
271 	if (len == (sizeof(handle) * 2)) {
272 		hex2bin(argv[1], len, (uint8_t *)&handle, sizeof(handle));
273 		sdp_discover.handle = sys_be32_to_cpu(handle);
274 	} else {
275 		shell_error(sh, "Invalid UUID");
276 		return -ENOEXEC;
277 	}
278 
279 	sdp_discover.func = sdp_discover_func;
280 	sdp_discover.pool = &sdp_client_pool;
281 	sdp_discover.type = BT_SDP_DISCOVER_SERVICE_ATTR;
282 
283 	err = bt_sdp_discover(default_conn, &sdp_discover);
284 	if (err) {
285 		shell_error(ctx_shell, "Fail to start SDP Discovery (err %d)", err);
286 		return err;
287 	}
288 	return 0;
289 }
290 
291 SHELL_STATIC_SUBCMD_SET_CREATE(sdp_client_cmds,
292 	SHELL_CMD_ARG(ss_discovery, NULL, "<UUID>", cmd_ss_discovery, 2, 0),
293 	SHELL_CMD_ARG(sa_discovery, NULL, "<Service Record Handle>", cmd_sa_discovery, 2, 0),
294 	SHELL_CMD_ARG(ssa_discovery, NULL, "<UUID>", cmd_ssa_discovery, 2, 0),
295 	SHELL_SUBCMD_SET_END
296 );
297 
cmd_default_handler(const struct shell * sh,size_t argc,char ** argv)298 static int cmd_default_handler(const struct shell *sh, size_t argc, char **argv)
299 {
300 	if (argc == 1) {
301 		shell_help(sh);
302 		return SHELL_CMD_HELP_PRINTED;
303 	}
304 
305 	shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
306 
307 	return -EINVAL;
308 }
309 
310 SHELL_CMD_REGISTER(sdp_client, &sdp_client_cmds, "Bluetooth classic SDP client shell commands",
311 		   cmd_default_handler);
312