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, ¶m);
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, ¶m);
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], ¶m);
149 if (!err) {
150 printk(" VERSION: %04X: %d\n", svclass_list[i], param);
151 }
152 }
153 err = bt_sdp_get_features(result->resp_buf, ¶m);
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