Skip to content

Commit 8d1b3e9

Browse files
lylezhu2012MaureenHelm
authored andcommitted
Bluetooth: shell: Add transport commands for GOEP
Add commands for GOEP to test transport features. Add commands for transport over RFCOMM, including `register-rfcomm`, `connect-rfcomm`, and `disconnect-rfcomm`. Add commands for transport over L2CAP, including `register-l2cap`, `connect-l2cap`, and `disconnect-l2cap`. Signed-off-by: Lyle Zhu <[email protected]>
1 parent bfd42f5 commit 8d1b3e9

File tree

3 files changed

+378
-0
lines changed

3 files changed

+378
-0
lines changed

subsys/bluetooth/host/classic/shell/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ if(CONFIG_BT_HFP_HF OR CONFIG_BT_HFP_AG)
1010
hfp.c
1111
)
1212
endif()
13+
14+
zephyr_library_sources_ifdef(CONFIG_BT_GOEP goep.c)
Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
/** @file
2+
* @brief Bluetooth GOEP shell module
3+
*
4+
* Provide some Bluetooth shell commands that can be useful to applications.
5+
*/
6+
/*
7+
* Copyright 2024-2025 NXP
8+
*
9+
* SPDX-License-Identifier: Apache-2.0
10+
*/
11+
12+
#include <errno.h>
13+
#include <zephyr/types.h>
14+
15+
#include <zephyr/bluetooth/bluetooth.h>
16+
#include <zephyr/bluetooth/conn.h>
17+
#include <zephyr/bluetooth/l2cap.h>
18+
#include <zephyr/bluetooth/classic/rfcomm.h>
19+
#include <zephyr/bluetooth/classic/sdp.h>
20+
#include <zephyr/bluetooth/classic/goep.h>
21+
22+
#include <zephyr/shell/shell.h>
23+
24+
#include "host/shell/bt.h"
25+
#include "common/bt_shell_private.h"
26+
27+
struct bt_goep_app {
28+
struct bt_goep goep;
29+
struct bt_conn *conn;
30+
};
31+
32+
static struct bt_goep_app goep_app;
33+
34+
static struct bt_goep_transport_rfcomm_server rfcomm_server;
35+
static struct bt_goep_transport_l2cap_server l2cap_server;
36+
37+
static struct bt_goep_app *goep_alloc(struct bt_conn *conn)
38+
{
39+
if (goep_app.conn) {
40+
return NULL;
41+
}
42+
43+
goep_app.conn = conn;
44+
return &goep_app;
45+
}
46+
47+
static void goep_free(struct bt_goep_app *goep)
48+
{
49+
goep->conn = NULL;
50+
}
51+
52+
static void goep_transport_connected(struct bt_conn *conn, struct bt_goep *goep)
53+
{
54+
bt_shell_print("GOEP %p transport connected on %p", goep, conn);
55+
}
56+
57+
static void goep_transport_disconnected(struct bt_goep *goep)
58+
{
59+
struct bt_goep_app *g_app = CONTAINER_OF(goep, struct bt_goep_app, goep);
60+
61+
goep_free(g_app);
62+
bt_shell_print("GOEP %p transport disconnected", goep);
63+
}
64+
65+
struct bt_goep_transport_ops goep_transport_ops = {
66+
.connected = goep_transport_connected,
67+
.disconnected = goep_transport_disconnected,
68+
};
69+
70+
static void goep_server_connect(struct bt_obex *obex, uint8_t version, uint16_t mopl,
71+
struct net_buf *buf)
72+
{
73+
}
74+
75+
static void goep_server_disconnect(struct bt_obex *obex, struct net_buf *buf)
76+
{
77+
}
78+
79+
static void goep_server_put(struct bt_obex *obex, bool final, struct net_buf *buf)
80+
{
81+
}
82+
83+
static void goep_server_get(struct bt_obex *obex, bool final, struct net_buf *buf)
84+
{
85+
}
86+
87+
static void goep_server_abort(struct bt_obex *obex, struct net_buf *buf)
88+
{
89+
}
90+
91+
static void goep_server_setpath(struct bt_obex *obex, uint8_t flags, struct net_buf *buf)
92+
{
93+
}
94+
95+
static void goep_server_action(struct bt_obex *obex, bool final, struct net_buf *buf)
96+
{
97+
}
98+
99+
struct bt_obex_server_ops goep_server_ops = {
100+
.connect = goep_server_connect,
101+
.disconnect = goep_server_disconnect,
102+
.put = goep_server_put,
103+
.get = goep_server_get,
104+
.abort = goep_server_abort,
105+
.setpath = goep_server_setpath,
106+
.action = goep_server_action,
107+
};
108+
109+
static void goep_client_connect(struct bt_obex *obex, uint8_t rsp_code, uint8_t version,
110+
uint16_t mopl, struct net_buf *buf)
111+
{
112+
}
113+
114+
static void goep_client_disconnect(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf)
115+
{
116+
}
117+
118+
static void goep_client_put(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf)
119+
{
120+
}
121+
122+
static void goep_client_get(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf)
123+
{
124+
}
125+
126+
static void goep_client_abort(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf)
127+
{
128+
}
129+
130+
static void goep_client_setpath(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf)
131+
{
132+
}
133+
134+
static void goep_client_action(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf)
135+
{
136+
}
137+
138+
struct bt_obex_client_ops goep_client_ops = {
139+
.connect = goep_client_connect,
140+
.disconnect = goep_client_disconnect,
141+
.put = goep_client_put,
142+
.get = goep_client_get,
143+
.abort = goep_client_abort,
144+
.setpath = goep_client_setpath,
145+
.action = goep_client_action,
146+
};
147+
148+
static int rfcomm_accept(struct bt_conn *conn, struct bt_goep_transport_rfcomm_server *server,
149+
struct bt_goep **goep)
150+
{
151+
struct bt_goep_app *g_app;
152+
153+
g_app = goep_alloc(conn);
154+
if (!g_app) {
155+
bt_shell_print("Cannot allocate goep instance");
156+
return -ENOMEM;
157+
}
158+
159+
g_app->goep.transport_ops = &goep_transport_ops;
160+
g_app->goep.obex.server_ops = &goep_server_ops;
161+
*goep = &g_app->goep;
162+
return 0;
163+
}
164+
165+
static int cmd_register_rfcomm(const struct shell *sh, size_t argc, char *argv[])
166+
{
167+
int err;
168+
uint8_t channel;
169+
170+
if (rfcomm_server.rfcomm.channel) {
171+
shell_error(sh, "RFCOMM has been registered");
172+
return -EBUSY;
173+
}
174+
175+
channel = (uint8_t)strtoul(argv[1], NULL, 16);
176+
177+
rfcomm_server.rfcomm.channel = channel;
178+
rfcomm_server.accept = rfcomm_accept;
179+
err = bt_goep_transport_rfcomm_server_register(&rfcomm_server);
180+
if (err) {
181+
shell_error(sh, "Fail to register RFCOMM server (error %d)", err);
182+
rfcomm_server.rfcomm.channel = 0;
183+
return -ENOEXEC;
184+
}
185+
shell_print(sh, "RFCOMM server (channel %02x) is registered", rfcomm_server.rfcomm.channel);
186+
return 0;
187+
}
188+
189+
static int cmd_connect_rfcomm(const struct shell *sh, size_t argc, char *argv[])
190+
{
191+
int err;
192+
struct bt_goep_app *g_app;
193+
uint8_t channel;
194+
195+
if (!default_conn) {
196+
shell_error(sh, "Not connected");
197+
return -ENOEXEC;
198+
}
199+
200+
channel = (uint8_t)strtoul(argv[1], NULL, 16);
201+
if (!channel) {
202+
shell_error(sh, "Invalid channel");
203+
return -ENOEXEC;
204+
}
205+
206+
g_app = goep_alloc(default_conn);
207+
if (!g_app) {
208+
shell_error(sh, "Cannot allocate goep instance");
209+
return -ENOMEM;
210+
}
211+
212+
g_app->goep.transport_ops = &goep_transport_ops;
213+
g_app->goep.obex.client_ops = &goep_client_ops;
214+
215+
err = bt_goep_transport_rfcomm_connect(default_conn, &g_app->goep, channel);
216+
if (err) {
217+
goep_free(g_app);
218+
shell_error(sh, "Fail to connect to channel %d (err %d)", channel, err);
219+
} else {
220+
shell_print(sh, "GOEP RFCOMM connection pending");
221+
}
222+
223+
return err;
224+
}
225+
226+
static int cmd_disconnect_rfcomm(const struct shell *sh, size_t argc, char *argv[])
227+
{
228+
int err;
229+
230+
if (!default_conn) {
231+
shell_error(sh, "Not connected");
232+
return -ENOEXEC;
233+
}
234+
235+
if (!goep_app.conn) {
236+
shell_error(sh, "No goep transport connection");
237+
return -ENOEXEC;
238+
}
239+
240+
err = bt_goep_transport_rfcomm_disconnect(&goep_app.goep);
241+
if (err) {
242+
shell_error(sh, "Fail to disconnect to channel (err %d)", err);
243+
} else {
244+
shell_print(sh, "GOEP RFCOMM disconnection pending");
245+
}
246+
return err;
247+
}
248+
249+
static int l2cap_accept(struct bt_conn *conn, struct bt_goep_transport_l2cap_server *server,
250+
struct bt_goep **goep)
251+
{
252+
struct bt_goep_app *g_app;
253+
254+
g_app = goep_alloc(conn);
255+
if (!g_app) {
256+
bt_shell_print("Cannot allocate goep instance");
257+
return -ENOMEM;
258+
}
259+
260+
g_app->goep.transport_ops = &goep_transport_ops;
261+
g_app->goep.obex.server_ops = &goep_server_ops;
262+
*goep = &g_app->goep;
263+
return 0;
264+
}
265+
266+
static int cmd_register_l2cap(const struct shell *sh, size_t argc, char *argv[])
267+
{
268+
int err;
269+
uint16_t psm;
270+
271+
if (l2cap_server.l2cap.psm) {
272+
shell_error(sh, "L2CAP server has been registered");
273+
return -EBUSY;
274+
}
275+
276+
psm = (uint16_t)strtoul(argv[1], NULL, 16);
277+
278+
l2cap_server.l2cap.psm = psm;
279+
l2cap_server.accept = l2cap_accept;
280+
err = bt_goep_transport_l2cap_server_register(&l2cap_server);
281+
if (err) {
282+
shell_error(sh, "Fail to register L2CAP server (error %d)", err);
283+
l2cap_server.l2cap.psm = 0;
284+
return -ENOEXEC;
285+
}
286+
shell_print(sh, "L2CAP server (psm %04x) is registered", l2cap_server.l2cap.psm);
287+
return 0;
288+
}
289+
290+
static int cmd_connect_l2cap(const struct shell *sh, size_t argc, char *argv[])
291+
{
292+
int err;
293+
struct bt_goep_app *g_app;
294+
uint16_t psm;
295+
296+
if (!default_conn) {
297+
shell_error(sh, "Not connected");
298+
return -ENOEXEC;
299+
}
300+
301+
psm = (uint16_t)strtoul(argv[1], NULL, 16);
302+
if (!psm) {
303+
shell_error(sh, "Invalid psm");
304+
return -ENOEXEC;
305+
}
306+
307+
g_app = goep_alloc(default_conn);
308+
if (!g_app) {
309+
shell_error(sh, "Cannot allocate goep instance");
310+
return -ENOMEM;
311+
}
312+
313+
g_app->goep.transport_ops = &goep_transport_ops;
314+
g_app->goep.obex.client_ops = &goep_client_ops;
315+
316+
err = bt_goep_transport_l2cap_connect(default_conn, &g_app->goep, psm);
317+
if (err) {
318+
goep_free(g_app);
319+
shell_error(sh, "Fail to connect to PSM %d (err %d)", psm, err);
320+
} else {
321+
shell_print(sh, "GOEP L2CAP connection pending");
322+
}
323+
324+
return err;
325+
}
326+
327+
static int cmd_disconnect_l2cap(const struct shell *sh, size_t argc, char *argv[])
328+
{
329+
int err;
330+
331+
if (!default_conn) {
332+
shell_error(sh, "Not connected");
333+
return -ENOEXEC;
334+
}
335+
336+
if (!goep_app.conn) {
337+
shell_error(sh, "No goep transport connection");
338+
return -ENOEXEC;
339+
}
340+
341+
err = bt_goep_transport_l2cap_disconnect(&goep_app.goep);
342+
if (err) {
343+
shell_error(sh, "Fail to disconnect L2CAP conn (err %d)", err);
344+
} else {
345+
shell_print(sh, "GOEP L2CAP disconnection pending");
346+
}
347+
return err;
348+
}
349+
350+
#define HELP_NONE ""
351+
352+
SHELL_STATIC_SUBCMD_SET_CREATE(goep_cmds,
353+
SHELL_CMD_ARG(register-rfcomm, NULL, "<channel>", cmd_register_rfcomm, 2, 0),
354+
SHELL_CMD_ARG(connect-rfcomm, NULL, "<channel>", cmd_connect_rfcomm, 2, 0),
355+
SHELL_CMD_ARG(disconnect-rfcomm, NULL, HELP_NONE, cmd_disconnect_rfcomm, 1, 0),
356+
SHELL_CMD_ARG(register-l2cap, NULL, "<psm>", cmd_register_l2cap, 2, 0),
357+
SHELL_CMD_ARG(connect-l2cap, NULL, "<psm>", cmd_connect_l2cap, 2, 0),
358+
SHELL_CMD_ARG(disconnect-l2cap, NULL, HELP_NONE, cmd_disconnect_l2cap, 1, 0),
359+
SHELL_SUBCMD_SET_END
360+
);
361+
362+
static int cmd_goep(const struct shell *sh, size_t argc, char **argv)
363+
{
364+
if (argc == 1) {
365+
shell_help(sh);
366+
return SHELL_CMD_HELP_PRINTED;
367+
}
368+
369+
shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
370+
371+
return -ENOEXEC;
372+
}
373+
374+
SHELL_CMD_ARG_REGISTER(goep, &goep_cmds, "Bluetooth GOEP shell commands", cmd_goep, 1, 1);

tests/bluetooth/shell/prj_br.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ CONFIG_BT_L2CAP_MAX_WINDOW_SIZE=5
2727

2828
CONFIG_BT_HFP_HF=y
2929
CONFIG_BT_HFP_AG=y
30+
31+
CONFIG_BT_GOEP=y

0 commit comments

Comments
 (0)