1 /**
2  * Copyright (c) 2024 Croxel, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/bluetooth/bluetooth.h>
9 #include <zephyr/bluetooth/conn.h>
10 #include <zephyr/bluetooth/hci.h>
11 
12 #define NAME_LEN 30
13 
14 static struct bt_conn *default_conn;
15 bt_addr_le_t ext_addr;
16 
17 enum bt_sample_scan_evt {
18 	BT_SAMPLE_EVT_EXT_ADV_FOUND,
19 	BT_SAMPLE_EVT_CONNECTED,
20 	BT_SAMPLE_EVT_DISCONNECTED,
21 	BT_SAMPLE_EVT_SCAN_DUE,
22 	BT_SAMPLE_EVT_MAX,
23 };
24 
25 enum bt_sample_scan_st {
26 	BT_SAMPLE_ST_SCANNING,
27 	BT_SAMPLE_ST_CONNECTING,
28 	BT_SAMPLE_ST_CONNECTED,
29 	BT_SAMPLE_ST_COOLDOWN,
30 };
31 
32 static volatile enum bt_sample_scan_st app_st = BT_SAMPLE_ST_SCANNING;
33 
34 static ATOMIC_DEFINE(evt_bitmask, BT_SAMPLE_EVT_MAX);
35 
36 static struct k_poll_signal poll_sig = K_POLL_SIGNAL_INITIALIZER(poll_sig);
37 static struct k_poll_event poll_evt = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
38 						K_POLL_MODE_NOTIFY_ONLY, &poll_sig);
39 
raise_evt(enum bt_sample_scan_evt evt)40 static void raise_evt(enum bt_sample_scan_evt evt)
41 {
42 	(void)atomic_set_bit(evt_bitmask, evt);
43 	k_poll_signal_raise(poll_evt.signal, 1);
44 }
45 
connected_cb(struct bt_conn * conn,uint8_t err)46 static void connected_cb(struct bt_conn *conn, uint8_t err)
47 {
48 	printk("Connected (err 0x%02X)\n", err);
49 
50 	if (err) {
51 		bt_conn_unref(default_conn);
52 		default_conn = NULL;
53 		return;
54 	}
55 
56 	raise_evt(BT_SAMPLE_EVT_CONNECTED);
57 }
58 
disconnected_cb(struct bt_conn * conn,uint8_t reason)59 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
60 {
61 	bt_conn_unref(default_conn);
62 	default_conn = NULL;
63 
64 	printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
65 }
66 
recycled_cb(void)67 static void recycled_cb(void)
68 {
69 	printk("Connection object available from previous conn. Disconnect is complete!\n");
70 	raise_evt(BT_SAMPLE_EVT_DISCONNECTED);
71 }
72 
73 BT_CONN_CB_DEFINE(conn_cb) = {
74 	.connected = connected_cb,
75 	.disconnected = disconnected_cb,
76 	.recycled = recycled_cb,
77 };
78 
79 
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)80 static void scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf)
81 {
82 	if (info->adv_type == BT_GAP_ADV_TYPE_EXT_ADV &&
83 	    info->adv_props & BT_GAP_ADV_PROP_EXT_ADV &&
84 	    info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) {
85 		/* Attempt connection request for device with extended advertisements */
86 		memcpy(&ext_addr, info->addr, sizeof(ext_addr));
87 		raise_evt(BT_SAMPLE_EVT_EXT_ADV_FOUND);
88 	}
89 }
90 
91 static struct bt_le_scan_cb scan_callbacks = {
92 	.recv = scan_recv,
93 };
94 
attempt_connection(void)95 static inline int attempt_connection(void)
96 {
97 	int err;
98 
99 	printk("Stopping scan\n");
100 	err = bt_le_scan_stop();
101 	if (err) {
102 		printk("Failed to stop scan: %d\n", err);
103 		return err;
104 	}
105 
106 	err = bt_conn_le_create(&ext_addr, BT_CONN_LE_CREATE_CONN,
107 		BT_LE_CONN_PARAM_DEFAULT, &default_conn);
108 	if (err) {
109 		printk("Failed to establish conn: %d\n", err);
110 		return err;
111 	}
112 
113 	return 0;
114 }
115 
start_scanning(void)116 static inline int start_scanning(void)
117 {
118 	int err;
119 
120 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
121 	if (err) {
122 		printk("failed (err %d)\n", err);
123 	}
124 
125 	return err;
126 }
127 
main(void)128 int main(void)
129 {
130 	int err;
131 
132 	printk("Starting Extended Advertising Demo [Scanner]\n");
133 
134 	/* Initialize the Bluetooth Subsystem */
135 	err = bt_enable(NULL);
136 	if (err) {
137 		printk("Bluetooth init failed (err %d)\n", err);
138 		return 0;
139 	}
140 
141 	bt_le_scan_cb_register(&scan_callbacks);
142 
143 	err = start_scanning();
144 	if (err) {
145 		return err;
146 	}
147 
148 	while (true) {
149 		(void)k_poll(&poll_evt, 1, K_FOREVER);
150 
151 		k_poll_signal_reset(poll_evt.signal);
152 		poll_evt.state = K_POLL_STATE_NOT_READY;
153 
154 		/* Identify event and act upon if applicable */
155 		if (atomic_test_and_clear_bit(evt_bitmask, BT_SAMPLE_EVT_EXT_ADV_FOUND) &&
156 		    app_st == BT_SAMPLE_ST_SCANNING) {
157 
158 			printk("Found extended advertisement packet!\n");
159 			app_st = BT_SAMPLE_ST_CONNECTING;
160 			err = attempt_connection();
161 			if (err) {
162 				return err;
163 			}
164 
165 		} else if (atomic_test_and_clear_bit(evt_bitmask, BT_SAMPLE_EVT_CONNECTED) &&
166 			   (app_st == BT_SAMPLE_ST_CONNECTING)) {
167 
168 			printk("Connected state!\n");
169 			app_st = BT_SAMPLE_ST_CONNECTED;
170 
171 		} else if (atomic_test_and_clear_bit(evt_bitmask, BT_SAMPLE_EVT_DISCONNECTED) &&
172 			   (app_st == BT_SAMPLE_ST_CONNECTED)) {
173 
174 			printk("Disconnected, cooldown for 5 seconds!\n");
175 			app_st = BT_SAMPLE_ST_COOLDOWN;
176 
177 			/* Wait a few seconds before starting to re-scan again... */
178 			k_sleep(K_SECONDS(5));
179 
180 			raise_evt(BT_SAMPLE_EVT_SCAN_DUE);
181 
182 		} else if (atomic_test_and_clear_bit(evt_bitmask, BT_SAMPLE_EVT_SCAN_DUE) &&
183 			   (app_st == BT_SAMPLE_ST_COOLDOWN)) {
184 
185 			printk("Starting to scan for extended adv\n");
186 			app_st = BT_SAMPLE_ST_SCANNING;
187 			err = start_scanning();
188 			if (err) {
189 				return err;
190 			}
191 
192 		}
193 	}
194 
195 	return 0;
196 }
197