1 /**
2  * Copyright (c) 2024 Croxel, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/bluetooth.h>
8 #include <zephyr/bluetooth/conn.h>
9 #include <zephyr/bluetooth/gap.h>
10 #include <zephyr/bluetooth/hci.h>
11 
12 static struct bt_conn *default_conn;
13 
14 enum bt_sample_adv_evt {
15 	BT_SAMPLE_EVT_CONNECTED,
16 	BT_SAMPLE_EVT_DISCONNECTED,
17 	BT_SAMPLE_EVT_MAX,
18 };
19 
20 enum bt_sample_adv_st {
21 	BT_SAMPLE_ST_ADV,
22 	BT_SAMPLE_ST_CONNECTED,
23 };
24 
25 static ATOMIC_DEFINE(evt_bitmask, BT_SAMPLE_EVT_MAX);
26 
27 static volatile enum bt_sample_adv_st app_st = BT_SAMPLE_ST_ADV;
28 
29 static struct k_poll_signal poll_sig = K_POLL_SIGNAL_INITIALIZER(poll_sig);
30 static struct k_poll_event poll_evt = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
31 						K_POLL_MODE_NOTIFY_ONLY, &poll_sig);
32 
raise_evt(enum bt_sample_adv_evt evt)33 static void raise_evt(enum bt_sample_adv_evt evt)
34 {
35 	(void)atomic_set_bit(evt_bitmask, evt);
36 	k_poll_signal_raise(poll_evt.signal, 1);
37 }
38 
connected_cb(struct bt_conn * conn,uint8_t err)39 static void connected_cb(struct bt_conn *conn, uint8_t err)
40 {
41 	printk("Connected (err 0x%02X)\n", err);
42 
43 	if (err) {
44 		return;
45 	}
46 
47 	__ASSERT(!default_conn, "Attempting to override existing connection object!");
48 	default_conn = bt_conn_ref(conn);
49 
50 	raise_evt(BT_SAMPLE_EVT_CONNECTED);
51 }
52 
disconnected_cb(struct bt_conn * conn,uint8_t reason)53 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
54 {
55 	printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
56 
57 	__ASSERT(conn == default_conn, "Unexpected disconnected callback");
58 
59 	bt_conn_unref(default_conn);
60 	default_conn = NULL;
61 }
62 
recycled_cb(void)63 static void recycled_cb(void)
64 {
65 	printk("Connection object available from previous conn. Disconnect is complete!\n");
66 	raise_evt(BT_SAMPLE_EVT_DISCONNECTED);
67 }
68 
69 BT_CONN_CB_DEFINE(conn_cb) = {
70 	.connected = connected_cb,
71 	.disconnected = disconnected_cb,
72 	.recycled = recycled_cb,
73 };
74 
start_advertising(struct bt_le_ext_adv * adv)75 static int start_advertising(struct bt_le_ext_adv *adv)
76 {
77 	int err;
78 
79 	printk("Starting Extended Advertising\n");
80 	err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
81 	if (err) {
82 		printk("Failed to start extended advertising (err %d)\n", err);
83 	}
84 
85 	return err;
86 }
87 
88 static const struct bt_data ad[] = {
89 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
90 };
91 
main(void)92 int main(void)
93 {
94 	int err;
95 	struct bt_le_ext_adv *adv;
96 
97 	printk("Starting Extended Advertising Demo\n");
98 
99 	/* Initialize the Bluetooth Subsystem */
100 	err = bt_enable(NULL);
101 	if (err) {
102 		printk("Bluetooth init failed (err %d)\n", err);
103 		return err;
104 	}
105 
106 	/* Create a connectable advertising set */
107 	err = bt_le_ext_adv_create(BT_LE_EXT_ADV_CONN, NULL, &adv);
108 	if (err) {
109 		printk("Failed to create advertising set (err %d)\n", err);
110 		return err;
111 	}
112 
113 	/* Set advertising data to have complete local name set */
114 	err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
115 	if (err) {
116 		printk("Failed to set advertising data (err %d)\n", err);
117 		return 0;
118 	}
119 
120 	err = start_advertising(adv);
121 	if (err) {
122 		return err;
123 	}
124 
125 	while (true) {
126 		k_poll(&poll_evt, 1, K_FOREVER);
127 
128 		k_poll_signal_reset(poll_evt.signal);
129 		poll_evt.state = K_POLL_STATE_NOT_READY;
130 
131 		/* Identify event and act upon if applicable */
132 		if (atomic_test_and_clear_bit(evt_bitmask, BT_SAMPLE_EVT_CONNECTED) &&
133 		    app_st == BT_SAMPLE_ST_ADV) {
134 
135 			printk("Connected state!\n");
136 			app_st = BT_SAMPLE_ST_CONNECTED;
137 
138 			printk("Initiating disconnect within 5 seconds...\n");
139 			if (k_poll(&poll_evt, 1, K_SECONDS(5)) == 0) {
140 				printk("Remote disconnected early...\n");
141 				/* Don't clear event here as we want the loop to run immediately */
142 			} else {
143 				/* Connection still alive after 5 seconds, terminate it */
144 				bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
145 			}
146 		} else if (atomic_test_and_clear_bit(evt_bitmask, BT_SAMPLE_EVT_DISCONNECTED) &&
147 			   app_st == BT_SAMPLE_ST_CONNECTED) {
148 
149 			printk("Disconnected state! Restarting advertising\n");
150 			app_st = BT_SAMPLE_ST_ADV;
151 			err = start_advertising(adv);
152 			if (err) {
153 				return err;
154 			}
155 		}
156 	}
157 
158 	return err;
159 }
160