spp_streamer_client.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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 MATTHIAS
  24. * RINGWALD 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. * contact@bluekitchen-gmbh.com
  35. *
  36. */
  37. #define BTSTACK_FILE__ "spp_streamer_client.c"
  38. /*
  39. * spp_streamer_client.c
  40. */
  41. // *****************************************************************************
  42. /* EXAMPLE_START(spp_streamer_client): Performance - Stream Data over SPP (Client)
  43. *
  44. * @text Note: The SPP Streamer Client scans for and connects to SPP Streamer,
  45. * and measures the throughput.
  46. */
  47. // *****************************************************************************
  48. #include <stdint.h>
  49. #include <stdio.h>
  50. #include <stdlib.h>
  51. #include <string.h>
  52. #include <inttypes.h>
  53. #include "btstack.h"
  54. // prototypes
  55. static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
  56. static uint8_t rfcomm_server_channel;
  57. #define NUM_ROWS 25
  58. #define NUM_COLS 40
  59. #define TEST_COD 0x1234
  60. #define TEST_MODE_SEND 1
  61. #define TEST_MODE_RECEIVE 2
  62. #define TEST_MODE_DUPLEX 3
  63. // configure test mode: send only, receive only, full duplex
  64. #define TEST_MODE TEST_MODE_SEND
  65. typedef enum {
  66. // SPP
  67. W4_PEER_COD,
  68. W4_SCAN_COMPLETE,
  69. W4_SDP_RESULT,
  70. W2_SEND_SDP_QUERY,
  71. W4_RFCOMM_CHANNEL,
  72. SENDING,
  73. DONE
  74. } state_t;
  75. static uint8_t test_data[NUM_ROWS * NUM_COLS];
  76. static uint16_t spp_test_data_len;
  77. static btstack_packet_callback_registration_t hci_event_callback_registration;
  78. static btstack_context_callback_registration_t handle_sdp_client_query_request;
  79. static bd_addr_t peer_addr;
  80. static state_t state;
  81. // SPP
  82. static uint16_t rfcomm_mtu;
  83. static uint16_t rfcomm_cid = 0;
  84. // static uint32_t data_to_send = DATA_VOLUME;
  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;
  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. }
  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. * Find remote peer by COD
  117. */
  118. #define INQUIRY_INTERVAL 5
  119. static void start_scan(void){
  120. printf("Starting inquiry scan..\n");
  121. state = W4_PEER_COD;
  122. gap_inquiry_start(INQUIRY_INTERVAL);
  123. }
  124. static void stop_scan(void){
  125. printf("Stopping inquiry scan..\n");
  126. state = W4_SCAN_COMPLETE;
  127. gap_inquiry_stop();
  128. }
  129. /*
  130. * @section Track throughput
  131. * @text We calculate the throughput by setting a start time and measuring the amount of
  132. * data sent. After a configurable REPORT_INTERVAL_MS, we print the throughput in kB/s
  133. * and reset the counter and start time.
  134. */
  135. /* LISTING_START(tracking): Tracking throughput */
  136. #define REPORT_INTERVAL_MS 3000
  137. static uint32_t test_data_transferred;
  138. static uint32_t test_data_start;
  139. static void test_reset(void){
  140. test_data_start = btstack_run_loop_get_time_ms();
  141. test_data_transferred = 0;
  142. }
  143. static void test_track_transferred(int bytes_sent){
  144. test_data_transferred += bytes_sent;
  145. // evaluate
  146. uint32_t now = btstack_run_loop_get_time_ms();
  147. uint32_t time_passed = now - test_data_start;
  148. if (time_passed < REPORT_INTERVAL_MS) return;
  149. // print speed
  150. int bytes_per_second = test_data_transferred * 1000 / time_passed;
  151. printf("%u bytes -> %u.%03u kB/s\n", (int) test_data_transferred, (int) bytes_per_second / 1000, bytes_per_second % 1000);
  152. // restart
  153. test_data_start = now;
  154. test_data_transferred = 0;
  155. }
  156. /* LISTING_END(tracking): Tracking throughput */
  157. #if (TEST_MODE & TEST_MODE_SEND)
  158. static void spp_create_test_data(void){
  159. int x,y;
  160. for (y=0;y<NUM_ROWS;y++){
  161. for (x=0;x<NUM_COLS-2;x++){
  162. test_data[y*NUM_COLS+x] = '0' + (x % 10);
  163. }
  164. test_data[y*NUM_COLS+NUM_COLS-2] = '\n';
  165. test_data[y*NUM_COLS+NUM_COLS-1] = '\r';
  166. }
  167. }
  168. static void spp_send_packet(void){
  169. rfcomm_send(rfcomm_cid, (uint8_t*) test_data, spp_test_data_len);
  170. test_track_transferred(spp_test_data_len);
  171. rfcomm_request_can_send_now_event(rfcomm_cid);
  172. }
  173. #endif
  174. /*
  175. * @section SDP Query Packet Handler
  176. *
  177. * @text Store RFCOMM Channel for SPP service and initiates RFCOMM connection
  178. */
  179. static void handle_query_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
  180. UNUSED(packet_type);
  181. UNUSED(channel);
  182. UNUSED(size);
  183. switch (hci_event_packet_get_type(packet)){
  184. case SDP_EVENT_QUERY_RFCOMM_SERVICE:
  185. rfcomm_server_channel = sdp_event_query_rfcomm_service_get_rfcomm_channel(packet);
  186. break;
  187. case SDP_EVENT_QUERY_COMPLETE:
  188. if (sdp_event_query_complete_get_status(packet)){
  189. printf("SDP query failed 0x%02x\n", sdp_event_query_complete_get_status(packet));
  190. break;
  191. }
  192. if (rfcomm_server_channel == 0){
  193. printf("No SPP service found\n");
  194. break;
  195. }
  196. printf("SDP query done, channel %u.\n", rfcomm_server_channel);
  197. rfcomm_create_channel(packet_handler, peer_addr, rfcomm_server_channel, NULL);
  198. break;
  199. default:
  200. break;
  201. }
  202. }
  203. static void handle_start_sdp_client_query(void * context){
  204. UNUSED(context);
  205. if (state != W2_SEND_SDP_QUERY) return;
  206. state = W4_RFCOMM_CHANNEL;
  207. sdp_client_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, peer_addr, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT);
  208. }
  209. /*
  210. * @section Gerenal Packet Handler
  211. *
  212. * @text Handles startup (BTSTACK_EVENT_STATE), inquiry, pairing, starts SDP query for SPP service, and RFCOMM connection
  213. */
  214. static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
  215. UNUSED(channel);
  216. bd_addr_t event_addr;
  217. uint8_t rfcomm_channel_nr;
  218. uint32_t class_of_device;
  219. switch (packet_type) {
  220. case HCI_EVENT_PACKET:
  221. switch (hci_event_packet_get_type(packet)) {
  222. case BTSTACK_EVENT_STATE:
  223. if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return;
  224. start_scan();
  225. break;
  226. case GAP_EVENT_INQUIRY_RESULT:
  227. if (state != W4_PEER_COD) break;
  228. class_of_device = gap_event_inquiry_result_get_class_of_device(packet);
  229. gap_event_inquiry_result_get_bd_addr(packet, event_addr);
  230. if (class_of_device == TEST_COD){
  231. memcpy(peer_addr, event_addr, 6);
  232. printf("Peer found: %s\n", bd_addr_to_str(peer_addr));
  233. stop_scan();
  234. } else {
  235. printf("Device found: %s with COD: 0x%06x\n", bd_addr_to_str(event_addr), (int) class_of_device);
  236. }
  237. break;
  238. case GAP_EVENT_INQUIRY_COMPLETE:
  239. switch (state){
  240. case W4_PEER_COD:
  241. printf("Inquiry complete\n");
  242. printf("Peer not found, starting scan again\n");
  243. start_scan();
  244. break;
  245. case W4_SCAN_COMPLETE:
  246. printf("Start to connect and query for SPP service\n");
  247. state = W2_SEND_SDP_QUERY;
  248. handle_sdp_client_query_request.callback = &handle_start_sdp_client_query;
  249. (void) sdp_client_register_query_callback(&handle_sdp_client_query_request);
  250. break;
  251. default:
  252. break;
  253. }
  254. if (state == W4_PEER_COD){
  255. }
  256. break;
  257. case HCI_EVENT_PIN_CODE_REQUEST:
  258. // inform about pin code request
  259. printf("Pin code request - using '0000'\n");
  260. hci_event_pin_code_request_get_bd_addr(packet, event_addr);
  261. gap_pin_code_response(event_addr, "0000");
  262. break;
  263. case HCI_EVENT_USER_CONFIRMATION_REQUEST:
  264. // inform about user confirmation request
  265. printf("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", little_endian_read_32(packet, 8));
  266. printf("SSP User Confirmation Auto accept\n");
  267. break;
  268. case RFCOMM_EVENT_INCOMING_CONNECTION:
  269. rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr);
  270. rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet);
  271. rfcomm_cid = rfcomm_event_incoming_connection_get_rfcomm_cid(packet);
  272. printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr));
  273. rfcomm_accept_connection(rfcomm_cid);
  274. break;
  275. case RFCOMM_EVENT_CHANNEL_OPENED:
  276. if (rfcomm_event_channel_opened_get_status(packet)) {
  277. printf("RFCOMM channel open failed, status %u\n", rfcomm_event_channel_opened_get_status(packet));
  278. } else {
  279. rfcomm_cid = rfcomm_event_channel_opened_get_rfcomm_cid(packet);
  280. rfcomm_mtu = rfcomm_event_channel_opened_get_max_frame_size(packet);
  281. printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_cid, rfcomm_mtu);
  282. test_reset();
  283. // disable page/inquiry scan to get max performance
  284. gap_discoverable_control(0);
  285. gap_connectable_control(0);
  286. #if (TEST_MODE & TEST_MODE_SEND)
  287. // configure test data
  288. spp_test_data_len = rfcomm_mtu;
  289. if (spp_test_data_len > sizeof(test_data)){
  290. spp_test_data_len = sizeof(test_data);
  291. }
  292. spp_create_test_data();
  293. state = SENDING;
  294. // start sending
  295. rfcomm_request_can_send_now_event(rfcomm_cid);
  296. #endif
  297. }
  298. break;
  299. #if (TEST_MODE & TEST_MODE_SEND)
  300. case RFCOMM_EVENT_CAN_SEND_NOW:
  301. spp_send_packet();
  302. break;
  303. #endif
  304. case RFCOMM_EVENT_CHANNEL_CLOSED:
  305. printf("RFCOMM channel closed\n");
  306. rfcomm_cid = 0;
  307. // re-enable page/inquiry scan again
  308. gap_discoverable_control(1);
  309. gap_connectable_control(1);
  310. break;
  311. default:
  312. break;
  313. }
  314. break;
  315. case RFCOMM_DATA_PACKET:
  316. test_track_transferred(size);
  317. #if 0
  318. // optional: print received data as ASCII text
  319. printf("RCV: '");
  320. for (i=0;i<size;i++){
  321. putchar(packet[i]);
  322. }
  323. printf("'\n");
  324. #endif
  325. break;
  326. default:
  327. break;
  328. }
  329. }
  330. /*
  331. * @section Main Application Setup
  332. *
  333. * @text As with the packet and the heartbeat handlers, the combined app setup contains the code from the individual example setups.
  334. */
  335. /* LISTING_START(MainConfiguration): Init L2CAP RFCOMM SDO SM ATT Server and start heartbeat timer */
  336. int btstack_main(int argc, const char * argv[]);
  337. int btstack_main(int argc, const char * argv[]){
  338. UNUSED(argc);
  339. (void)argv;
  340. l2cap_init();
  341. rfcomm_init();
  342. #ifdef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE_FOR_RFCOMM
  343. // setup ERTM management
  344. rfcomm_enable_l2cap_ertm(&rfcomm_ertm_request_handler, &rfcomm_ertm_released_handler);
  345. #endif
  346. // register for HCI events
  347. hci_event_callback_registration.callback = &packet_handler;
  348. hci_add_event_handler(&hci_event_callback_registration);
  349. // init SDP
  350. gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
  351. // turn on!
  352. hci_power_control(HCI_POWER_ON);
  353. return 0;
  354. }
  355. /* LISTING_END */
  356. /* EXAMPLE_END */