-
Notifications
You must be signed in to change notification settings - Fork 639
/
Copy pathspp_counter.c
273 lines (232 loc) · 10.7 KB
/
spp_counter.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
*
*/
#define BTSTACK_FILE__ "spp_counter.c"
// *****************************************************************************
/* EXAMPLE_START(spp_counter): SPP Server - Heartbeat Counter over RFCOMM
*
* @text The Serial port profile (SPP) is widely used as it provides a serial
* port over Bluetooth. The SPP counter example demonstrates how to setup an SPP
* service, and provide a periodic timer over RFCOMM.
*
* @text Note: To test, please run the spp_counter example, and then pair from
* a remote device, and open the Virtual Serial Port.
*/
// *****************************************************************************
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "btstack.h"
#define RFCOMM_SERVER_CHANNEL 1
#define HEARTBEAT_PERIOD_MS 1000
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static uint16_t rfcomm_channel_id;
static uint8_t spp_service_buffer[150];
static btstack_packet_callback_registration_t hci_event_callback_registration;
/* @section SPP Service Setup
*s
* @text To provide an SPP service, the L2CAP, RFCOMM, and SDP protocol layers
* are required. After setting up an RFCOMM service with channel nubmer
* RFCOMM_SERVER_CHANNEL, an SDP record is created and registered with the SDP server.
* Example code for SPP service setup is
* provided in Listing SPPSetup. The SDP record created by function
* spp_create_sdp_record consists of a basic SPP definition that uses the provided
* RFCOMM channel ID and service name. For more details, please have a look at it
* in \path{src/sdp_util.c}.
* The SDP record is created on the fly in RAM and is deterministic.
* To preserve valuable RAM, the result could be stored as constant data inside the ROM.
*/
/* LISTING_START(SPPSetup): SPP service setup */
static void spp_service_setup(void){
// register for HCI events
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
l2cap_init();
#ifdef ENABLE_BLE
// Initialize LE Security Manager. Needed for cross-transport key derivation
sm_init();
#endif
rfcomm_init();
rfcomm_register_service(packet_handler, RFCOMM_SERVER_CHANNEL, 0xffff); // reserved channel, mtu limited by l2cap
// init SDP, create record for SPP and register with SDP
sdp_init();
memset(spp_service_buffer, 0, sizeof(spp_service_buffer));
spp_create_sdp_record(spp_service_buffer, sdp_create_service_record_handle(), RFCOMM_SERVER_CHANNEL, "SPP Counter");
btstack_assert(de_get_len( spp_service_buffer) <= sizeof(spp_service_buffer));
sdp_register_service(spp_service_buffer);
}
/* LISTING_END */
/* @section Periodic Timer Setup
*
* @text The heartbeat handler increases the real counter every second,
* and sends a text string with the counter value, as shown in Listing PeriodicCounter.
*/
/* LISTING_START(PeriodicCounter): Periodic Counter */
static btstack_timer_source_t heartbeat;
static char lineBuffer[30];
static void heartbeat_handler(struct btstack_timer_source *ts){
static int counter = 0;
if (rfcomm_channel_id){
snprintf(lineBuffer, sizeof(lineBuffer), "BTstack counter %04u\n", ++counter);
printf("%s", lineBuffer);
rfcomm_request_can_send_now_event(rfcomm_channel_id);
}
btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS);
btstack_run_loop_add_timer(ts);
}
static void one_shot_timer_setup(void){
// set one-shot timer
heartbeat.process = &heartbeat_handler;
btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS);
btstack_run_loop_add_timer(&heartbeat);
}
/* LISTING_END */
/* @section Bluetooth Logic
* @text The Bluetooth logic is implemented within the
* packet handler, see Listing SppServerPacketHandler. In this example,
* the following events are passed sequentially:
* - BTSTACK_EVENT_STATE,
* - HCI_EVENT_PIN_CODE_REQUEST (Standard pairing) or
* - HCI_EVENT_USER_CONFIRMATION_REQUEST (Secure Simple Pairing),
* - RFCOMM_EVENT_INCOMING_CONNECTION,
* - RFCOMM_EVENT_CHANNEL_OPENED,
* - RFCOMM_EVETN_CAN_SEND_NOW, and
* - RFCOMM_EVENT_CHANNEL_CLOSED
*/
/* @text Upon receiving HCI_EVENT_PIN_CODE_REQUEST event, we need to handle
* authentication. Here, we use a fixed PIN code "0000".
*
* When HCI_EVENT_USER_CONFIRMATION_REQUEST is received, the user will be
* asked to accept the pairing request. If the IO capability is set to
* SSP_IO_CAPABILITY_DISPLAY_YES_NO, the request will be automatically accepted.
*
* The RFCOMM_EVENT_INCOMING_CONNECTION event indicates an incoming connection.
* Here, the connection is accepted. More logic is need, if you want to handle connections
* from multiple clients. The incoming RFCOMM connection event contains the RFCOMM
* channel number used during the SPP setup phase and the newly assigned RFCOMM
* channel ID that is used by all BTstack commands and events.
*
* If RFCOMM_EVENT_CHANNEL_OPENED event returns status greater then 0,
* then the channel establishment has failed (rare case, e.g., client crashes).
* On successful connection, the RFCOMM channel ID and MTU for this
* channel are made available to the heartbeat counter. After opening the RFCOMM channel,
* the communication between client and the application
* takes place. In this example, the timer handler increases the real counter every
* second.
*
* RFCOMM_EVENT_CAN_SEND_NOW indicates that it's possible to send an RFCOMM packet
* on the rfcomm_cid that is include
*/
/* LISTING_START(SppServerPacketHandler): SPP Server - Heartbeat Counter over RFCOMM */
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
/* LISTING_PAUSE */
bd_addr_t event_addr;
uint8_t rfcomm_channel_nr;
uint16_t mtu;
int i;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
/* LISTING_RESUME */
case HCI_EVENT_PIN_CODE_REQUEST:
// inform about pin code request
printf("Pin code request - using '0000'\n");
hci_event_pin_code_request_get_bd_addr(packet, event_addr);
gap_pin_code_response(event_addr, "0000");
break;
case HCI_EVENT_USER_CONFIRMATION_REQUEST:
// ssp: inform about user confirmation request
printf("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", little_endian_read_32(packet, 8));
printf("SSP User Confirmation Auto accept\n");
break;
case RFCOMM_EVENT_INCOMING_CONNECTION:
rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr);
rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet);
rfcomm_channel_id = rfcomm_event_incoming_connection_get_rfcomm_cid(packet);
printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr));
rfcomm_accept_connection(rfcomm_channel_id);
break;
case RFCOMM_EVENT_CHANNEL_OPENED:
if (rfcomm_event_channel_opened_get_status(packet)) {
printf("RFCOMM channel open failed, status 0x%02x\n", rfcomm_event_channel_opened_get_status(packet));
} else {
rfcomm_channel_id = rfcomm_event_channel_opened_get_rfcomm_cid(packet);
mtu = rfcomm_event_channel_opened_get_max_frame_size(packet);
printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_channel_id, mtu);
}
break;
case RFCOMM_EVENT_CAN_SEND_NOW:
rfcomm_send(rfcomm_channel_id, (uint8_t*) lineBuffer, (uint16_t) strlen(lineBuffer));
break;
/* LISTING_PAUSE */
case RFCOMM_EVENT_CHANNEL_CLOSED:
printf("RFCOMM channel closed\n");
rfcomm_channel_id = 0;
break;
default:
break;
}
break;
case RFCOMM_DATA_PACKET:
printf("RCV: '");
for (i=0;i<size;i++){
putchar(packet[i]);
}
printf("'\n");
break;
default:
break;
}
/* LISTING_RESUME */
}
/* LISTING_END */
int btstack_main(int argc, const char * argv[]);
int btstack_main(int argc, const char * argv[]){
(void)argc;
(void)argv;
one_shot_timer_setup();
spp_service_setup();
gap_discoverable_control(1);
gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
gap_set_local_name("SPP Counter 00:00:00:00:00:00");
// turn on!
hci_power_control(HCI_POWER_ON);
return 0;
}
/* EXAMPLE_END */