1 /*
2 * Copyright (C) 2014 BlueKitchen GmbH
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holders nor the names of
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * 4. Any redistribution, use, or modification is done solely for
17 * personal benefit and not for any commercial purpose or for
18 * monetary gain.
19 *
20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * Please inquire about commercial licensing options at
34 * [email protected]
35 *
36 */
37
38 #define BTSTACK_FILE__ "spp_streamer.c"
39
40 /*
41 * spp_streamer.c
42 */
43
44 // *****************************************************************************
45 /* EXAMPLE_START(spp_streamer): Performance - Stream Data over SPP (Server)
46 *
47 * @text After RFCOMM connections gets open, request a
48 * RFCOMM_EVENT_CAN_SEND_NOW via rfcomm_request_can_send_now_event().
49 * @text When we get the RFCOMM_EVENT_CAN_SEND_NOW, send data and request another one.
50 *
51 * @text Note: To test, run the example, pair from a remote
52 * device, and open the Virtual Serial Port.
53 */
54 // *****************************************************************************
55
56 #include <inttypes.h>
57 #include <stdint.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61
62 #include "btstack.h"
63
64 int btstack_main(int argc, const char * argv[]);
65
66 #define RFCOMM_SERVER_CHANNEL 1
67
68 #define TEST_COD 0x1234
69 #define NUM_ROWS 25
70 #define NUM_COLS 40
71 #define DATA_VOLUME (10 * 1000 * 1000)
72
73 static btstack_packet_callback_registration_t hci_event_callback_registration;
74
75 static uint8_t test_data[NUM_ROWS * NUM_COLS];
76
77 // SPP
78 static uint8_t spp_service_buffer[150];
79
80 static uint16_t spp_test_data_len;
81 static uint16_t rfcomm_mtu;
82 static uint16_t rfcomm_cid = 0;
83 // static uint32_t data_to_send = DATA_VOLUME;
84
85 /**
86 * RFCOMM can make use for ERTM. Due to the need to re-transmit packets,
87 * a large buffer is needed to still get high throughput
88 */
89 #ifdef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE_FOR_RFCOMM
90 static uint8_t ertm_buffer[20000];
91 static l2cap_ertm_config_t ertm_config = {
92 0, // ertm mandatory
93 8, // max transmit
94 2000,
95 12000,
96 1000, // l2cap ertm mtu
97 8,
98 8,
99 0, // No FCS
100 };
101 static int ertm_buffer_in_use;
rfcomm_ertm_request_handler(rfcomm_ertm_request_t * ertm_request)102 static void rfcomm_ertm_request_handler(rfcomm_ertm_request_t * ertm_request){
103 printf("ERTM Buffer requested, buffer in use %u\n", ertm_buffer_in_use);
104 if (ertm_buffer_in_use) return;
105 ertm_buffer_in_use = 1;
106 ertm_request->ertm_config = &ertm_config;
107 ertm_request->ertm_buffer = ertm_buffer;
108 ertm_request->ertm_buffer_size = sizeof(ertm_buffer);
109 }
rfcomm_ertm_released_handler(uint16_t ertm_id)110 static void rfcomm_ertm_released_handler(uint16_t ertm_id){
111 printf("ERTM Buffer released, buffer in use %u, ertm_id %x\n", ertm_buffer_in_use, ertm_id);
112 ertm_buffer_in_use = 0;
113 }
114 #endif
115
116 /*
117 * @section Track throughput
118 * @text We calculate the throughput by setting a start time and measuring the amount of
119 * data sent. After a configurable REPORT_INTERVAL_MS, we print the throughput in kB/s
120 * and reset the counter and start time.
121 */
122
123 /* LISTING_START(tracking): Tracking throughput */
124 #define REPORT_INTERVAL_MS 3000
125 static uint32_t test_data_transferred;
126 static uint32_t test_data_start;
127
test_reset(void)128 static void test_reset(void){
129 test_data_start = btstack_run_loop_get_time_ms();
130 test_data_transferred = 0;
131 }
132
test_track_transferred(int bytes_sent)133 static void test_track_transferred(int bytes_sent){
134 test_data_transferred += bytes_sent;
135 // evaluate
136 uint32_t now = btstack_run_loop_get_time_ms();
137 uint32_t time_passed = now - test_data_start;
138 if (time_passed < REPORT_INTERVAL_MS) return;
139 // print speed
140 int bytes_per_second = test_data_transferred * 1000 / time_passed;
141 printf("%u bytes -> %u.%03u kB/s\n", (int) test_data_transferred, (int) bytes_per_second / 1000, bytes_per_second % 1000);
142
143 // restart
144 test_data_start = now;
145 test_data_transferred = 0;
146 }
147 /* LISTING_END(tracking): Tracking throughput */
148
149
spp_create_test_data(void)150 static void spp_create_test_data(void){
151 int x,y;
152 for (y=0;y<NUM_ROWS;y++){
153 for (x=0;x<NUM_COLS-2;x++){
154 test_data[y*NUM_COLS+x] = '0' + (x % 10);
155 }
156 test_data[y*NUM_COLS+NUM_COLS-2] = '\n';
157 test_data[y*NUM_COLS+NUM_COLS-1] = '\r';
158 }
159 }
160
spp_send_packet(void)161 static void spp_send_packet(void){
162 rfcomm_send(rfcomm_cid, (uint8_t*) test_data, spp_test_data_len);
163
164 test_track_transferred(spp_test_data_len);
165 #if 0
166 if (data_to_send <= spp_test_data_len){
167 printf("SPP Streamer: enough data send, closing channel\n");
168 rfcomm_disconnect(rfcomm_cid);
169 rfcomm_cid = 0;
170 return;
171 }
172 data_to_send -= spp_test_data_len;
173 #endif
174 rfcomm_request_can_send_now_event(rfcomm_cid);
175 }
176
177 /*
178 * @section Packet Handler
179 *
180 * @text The packet handler of the combined example is just the combination of the individual packet handlers.
181 */
182
packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)183 static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
184 UNUSED(channel);
185
186 bd_addr_t event_addr;
187 uint8_t rfcomm_channel_nr;
188
189 switch (packet_type) {
190 case HCI_EVENT_PACKET:
191 switch (hci_event_packet_get_type(packet)) {
192
193 case HCI_EVENT_PIN_CODE_REQUEST:
194 // inform about pin code request
195 printf("Pin code request - using '0000'\n");
196 hci_event_pin_code_request_get_bd_addr(packet, event_addr);
197 gap_pin_code_response(event_addr, "0000");
198 break;
199
200 case HCI_EVENT_USER_CONFIRMATION_REQUEST:
201 // inform about user confirmation request
202 printf("SSP User Confirmation Request with numeric value '%06" PRIu32 "'\n", little_endian_read_32(packet, 8));
203 printf("SSP User Confirmation Auto accept\n");
204 break;
205
206 case RFCOMM_EVENT_INCOMING_CONNECTION:
207 rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr);
208 rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet);
209 rfcomm_cid = rfcomm_event_incoming_connection_get_rfcomm_cid(packet);
210 printf("RFCOMM channel 0x%02x requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr));
211 rfcomm_accept_connection(rfcomm_cid);
212 break;
213
214 case RFCOMM_EVENT_CHANNEL_OPENED:
215 if (rfcomm_event_channel_opened_get_status(packet)) {
216 printf("RFCOMM channel open failed, status 0x%02x\n", rfcomm_event_channel_opened_get_status(packet));
217 } else {
218 rfcomm_cid = rfcomm_event_channel_opened_get_rfcomm_cid(packet);
219 rfcomm_mtu = rfcomm_event_channel_opened_get_max_frame_size(packet);
220 printf("RFCOMM channel open succeeded. New RFCOMM Channel ID 0x%02x, max frame size %u\n", rfcomm_cid, rfcomm_mtu);
221
222 spp_test_data_len = rfcomm_mtu;
223 if (spp_test_data_len > sizeof(test_data)){
224 spp_test_data_len = sizeof(test_data);
225 }
226
227 // disable page/inquiry scan to get max performance
228 gap_discoverable_control(0);
229 gap_connectable_control(0);
230
231 test_reset();
232 rfcomm_request_can_send_now_event(rfcomm_cid);
233 }
234 break;
235
236 case RFCOMM_EVENT_CAN_SEND_NOW:
237 spp_send_packet();
238 break;
239
240 case RFCOMM_EVENT_CHANNEL_CLOSED:
241 printf("RFCOMM channel closed\n");
242 rfcomm_cid = 0;
243
244 // re-enable page/inquiry scan again
245 gap_discoverable_control(1);
246 gap_connectable_control(1);
247 break;
248
249 default:
250 break;
251 }
252 break;
253
254 case RFCOMM_DATA_PACKET:
255 test_track_transferred(size);
256 #if 0
257 // optional: print received data as ASCII text
258 printf("RCV: '");
259 for (i=0;i<size;i++){
260 putchar(packet[i]);
261 }
262 printf("'\n");
263 #endif
264 break;
265
266 default:
267 break;
268 }
269 }
270
271 /*
272 * @section Main Application Setup
273 *
274 * @text As with the packet and the heartbeat handlers, the combined app setup contains the code from the individual example setups.
275 */
276
277
278 /* LISTING_START(MainConfiguration): Init L2CAP RFCOMM SDP SPP */
btstack_main(int argc,const char * argv[])279 int btstack_main(int argc, const char * argv[])
280 {
281 (void)argc;
282 (void)argv;
283
284 l2cap_init();
285
286 #ifdef ENABLE_BLE
287 // Initialize LE Security Manager. Needed for cross-transport key derivation
288 sm_init();
289 #endif
290
291 rfcomm_init();
292 rfcomm_register_service(packet_handler, RFCOMM_SERVER_CHANNEL, 0xffff);
293
294 #ifdef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE_FOR_RFCOMM
295 // setup ERTM management
296 rfcomm_enable_l2cap_ertm(&rfcomm_ertm_request_handler, &rfcomm_ertm_released_handler);
297 #endif
298
299 // init SDP, create record for SPP and register with SDP
300 sdp_init();
301 memset(spp_service_buffer, 0, sizeof(spp_service_buffer));
302 spp_create_sdp_record(spp_service_buffer, sdp_create_service_record_handle(), RFCOMM_SERVER_CHANNEL, "SPP Streamer");
303 btstack_assert(de_get_len( spp_service_buffer) <= sizeof(spp_service_buffer));
304 sdp_register_service(spp_service_buffer);
305
306 // register for HCI events
307 hci_event_callback_registration.callback = &packet_handler;
308 hci_add_event_handler(&hci_event_callback_registration);
309
310 // short-cut to find other SPP Streamer
311 gap_set_class_of_device(TEST_COD);
312
313 gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
314 gap_set_local_name("SPP Streamer 00:00:00:00:00:00");
315 gap_discoverable_control(1);
316
317 spp_create_test_data();
318
319 // turn on!
320 hci_power_control(HCI_POWER_ON);
321
322 return 0;
323 }
324 /* LISTING_END */
325 /* EXAMPLE_END */
326