spp_streamer.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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.c"
  38. /*
  39. * spp_streamer.c
  40. */
  41. // *****************************************************************************
  42. /* EXAMPLE_START(spp_streamer): Send test data via SPP as fast as possible.
  43. *
  44. * @text After RFCOMM connections gets open, request a
  45. * RFCOMM_EVENT_CAN_SEND_NOW via rfcomm_request_can_send_now_event().
  46. * @text When we get the RFCOMM_EVENT_CAN_SEND_NOW, send data and request another one.
  47. *
  48. * @text Note: To test, run the example, pair from a remote
  49. * device, and open the Virtual Serial Port.
  50. */
  51. // *****************************************************************************
  52. #include <stdint.h>
  53. #include <stdio.h>
  54. #include <stdlib.h>
  55. #include <string.h>
  56. #include <inttypes.h>
  57. #include "btstack.h"
  58. int btstack_main(int argc, const char * argv[]);
  59. #define RFCOMM_SERVER_CHANNEL 1
  60. #define TEST_COD 0x1234
  61. #define NUM_ROWS 25
  62. #define NUM_COLS 40
  63. #define DATA_VOLUME (10 * 1000 * 1000)
  64. static btstack_packet_callback_registration_t hci_event_callback_registration;
  65. static uint8_t test_data[NUM_ROWS * NUM_COLS];
  66. // SPP
  67. static uint8_t spp_service_buffer[150];
  68. static uint16_t spp_test_data_len;
  69. static uint16_t rfcomm_mtu;
  70. static uint16_t rfcomm_cid = 0;
  71. // static uint32_t data_to_send = DATA_VOLUME;
  72. /**
  73. * RFCOMM can make use for ERTM. Due to the need to re-transmit packets,
  74. * a large buffer is needed to still get high throughput
  75. */
  76. #ifdef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE_FOR_RFCOMM
  77. static uint8_t ertm_buffer[20000];
  78. static l2cap_ertm_config_t ertm_config = {
  79. 0, // ertm mandatory
  80. 8, // max transmit
  81. 2000,
  82. 12000,
  83. 1000, // l2cap ertm mtu
  84. 8,
  85. 8,
  86. 0, // No FCS
  87. };
  88. static int ertm_buffer_in_use;
  89. static void rfcomm_ertm_request_handler(rfcomm_ertm_request_t * ertm_request){
  90. printf("ERTM Buffer requested, buffer in use %u\n", ertm_buffer_in_use);
  91. if (ertm_buffer_in_use) return;
  92. ertm_buffer_in_use = 1;
  93. ertm_request->ertm_config = &ertm_config;
  94. ertm_request->ertm_buffer = ertm_buffer;
  95. ertm_request->ertm_buffer_size = sizeof(ertm_buffer);
  96. }
  97. static void rfcomm_ertm_released_handler(uint16_t ertm_id){
  98. printf("ERTM Buffer released, buffer in use %u, ertm_id %x\n", ertm_buffer_in_use, ertm_id);
  99. ertm_buffer_in_use = 0;
  100. }
  101. #endif
  102. /*
  103. * @section Track throughput
  104. * @text We calculate the throughput by setting a start time and measuring the amount of
  105. * data sent. After a configurable REPORT_INTERVAL_MS, we print the throughput in kB/s
  106. * and reset the counter and start time.
  107. */
  108. /* LISTING_START(tracking): Tracking throughput */
  109. #define REPORT_INTERVAL_MS 3000
  110. static uint32_t test_data_transferred;
  111. static uint32_t test_data_start;
  112. static void test_reset(void){
  113. test_data_start = btstack_run_loop_get_time_ms();
  114. test_data_transferred = 0;
  115. }
  116. static void test_track_transferred(int bytes_sent){
  117. test_data_transferred += bytes_sent;
  118. // evaluate
  119. uint32_t now = btstack_run_loop_get_time_ms();
  120. uint32_t time_passed = now - test_data_start;
  121. if (time_passed < REPORT_INTERVAL_MS) return;
  122. // print speed
  123. int bytes_per_second = test_data_transferred * 1000 / time_passed;
  124. printf("%u bytes -> %u.%03u kB/s\n", (int) test_data_transferred, (int) bytes_per_second / 1000, bytes_per_second % 1000);
  125. // restart
  126. test_data_start = now;
  127. test_data_transferred = 0;
  128. }
  129. /* LISTING_END(tracking): Tracking throughput */
  130. static void spp_create_test_data(void){
  131. int x,y;
  132. for (y=0;y<NUM_ROWS;y++){
  133. for (x=0;x<NUM_COLS-2;x++){
  134. test_data[y*NUM_COLS+x] = '0' + (x % 10);
  135. }
  136. test_data[y*NUM_COLS+NUM_COLS-2] = '\n';
  137. test_data[y*NUM_COLS+NUM_COLS-1] = '\r';
  138. }
  139. }
  140. static void spp_send_packet(void){
  141. rfcomm_send(rfcomm_cid, (uint8_t*) test_data, spp_test_data_len);
  142. test_track_transferred(spp_test_data_len);
  143. #if 0
  144. if (data_to_send <= spp_test_data_len){
  145. printf("SPP Streamer: enough data send, closing channel\n");
  146. rfcomm_disconnect(rfcomm_cid);
  147. rfcomm_cid = 0;
  148. return;
  149. }
  150. data_to_send -= spp_test_data_len;
  151. #endif
  152. rfcomm_request_can_send_now_event(rfcomm_cid);
  153. }
  154. /*
  155. * @section Packet Handler
  156. *
  157. * @text The packet handler of the combined example is just the combination of the individual packet handlers.
  158. */
  159. static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
  160. UNUSED(channel);
  161. bd_addr_t event_addr;
  162. uint8_t rfcomm_channel_nr;
  163. switch (packet_type) {
  164. case HCI_EVENT_PACKET:
  165. switch (hci_event_packet_get_type(packet)) {
  166. case HCI_EVENT_PIN_CODE_REQUEST:
  167. // inform about pin code request
  168. printf("Pin code request - using '0000'\n");
  169. hci_event_pin_code_request_get_bd_addr(packet, event_addr);
  170. gap_pin_code_response(event_addr, "0000");
  171. break;
  172. case HCI_EVENT_USER_CONFIRMATION_REQUEST:
  173. // inform about user confirmation request
  174. printf("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", little_endian_read_32(packet, 8));
  175. printf("SSP User Confirmation Auto accept\n");
  176. break;
  177. case RFCOMM_EVENT_INCOMING_CONNECTION:
  178. // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
  179. rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr);
  180. rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet);
  181. rfcomm_cid = rfcomm_event_incoming_connection_get_rfcomm_cid(packet);
  182. printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr));
  183. rfcomm_accept_connection(rfcomm_cid);
  184. break;
  185. case RFCOMM_EVENT_CHANNEL_OPENED:
  186. // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16)
  187. if (rfcomm_event_channel_opened_get_status(packet)) {
  188. printf("RFCOMM channel open failed, status %u\n", rfcomm_event_channel_opened_get_status(packet));
  189. } else {
  190. rfcomm_cid = rfcomm_event_channel_opened_get_rfcomm_cid(packet);
  191. rfcomm_mtu = rfcomm_event_channel_opened_get_max_frame_size(packet);
  192. printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_cid, rfcomm_mtu);
  193. spp_test_data_len = rfcomm_mtu;
  194. if (spp_test_data_len > sizeof(test_data)){
  195. spp_test_data_len = sizeof(test_data);
  196. }
  197. // disable page/inquiry scan to get max performance
  198. gap_discoverable_control(0);
  199. gap_connectable_control(0);
  200. test_reset();
  201. rfcomm_request_can_send_now_event(rfcomm_cid);
  202. }
  203. break;
  204. case RFCOMM_EVENT_CAN_SEND_NOW:
  205. spp_send_packet();
  206. break;
  207. case RFCOMM_EVENT_CHANNEL_CLOSED:
  208. printf("RFCOMM channel closed\n");
  209. rfcomm_cid = 0;
  210. // re-enable page/inquiry scan again
  211. gap_discoverable_control(1);
  212. gap_connectable_control(1);
  213. break;
  214. default:
  215. break;
  216. }
  217. break;
  218. case RFCOMM_DATA_PACKET:
  219. test_track_transferred(size);
  220. #if 0
  221. printf("RCV: '");
  222. for (i=0;i<size;i++){
  223. putchar(packet[i]);
  224. }
  225. printf("'\n");
  226. #endif
  227. break;
  228. default:
  229. break;
  230. }
  231. }
  232. /*
  233. * @section Main Application Setup
  234. *
  235. * @text As with the packet and the heartbeat handlers, the combined app setup contains the code from the individual example setups.
  236. */
  237. /* LISTING_START(MainConfiguration): Init L2CAP RFCOMM SDP SPP */
  238. int btstack_main(int argc, const char * argv[])
  239. {
  240. (void)argc;
  241. (void)argv;
  242. l2cap_init();
  243. rfcomm_init();
  244. rfcomm_register_service(packet_handler, RFCOMM_SERVER_CHANNEL, 0xffff);
  245. #ifdef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE_FOR_RFCOMM
  246. // setup ERTM management
  247. rfcomm_enable_l2cap_ertm(&rfcomm_ertm_request_handler, &rfcomm_ertm_released_handler);
  248. #endif
  249. // init SDP, create record for SPP and register with SDP
  250. sdp_init();
  251. memset(spp_service_buffer, 0, sizeof(spp_service_buffer));
  252. spp_create_sdp_record(spp_service_buffer, 0x10001, RFCOMM_SERVER_CHANNEL, "SPP Streamer");
  253. sdp_register_service(spp_service_buffer);
  254. // printf("SDP service record size: %u\n", de_get_len(spp_service_buffer));
  255. // register for HCI events
  256. hci_event_callback_registration.callback = &packet_handler;
  257. hci_add_event_handler(&hci_event_callback_registration);
  258. // short-cut to find other SPP Streamer
  259. gap_set_class_of_device(TEST_COD);
  260. gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
  261. gap_set_local_name("SPP Streamer 00:00:00:00:00:00");
  262. gap_discoverable_control(1);
  263. spp_create_test_data();
  264. // turn on!
  265. hci_power_control(HCI_POWER_ON);
  266. return 0;
  267. }
  268. /* LISTING_END */
  269. /* EXAMPLE_END */