spp_streamer_client.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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): Client for SPP Streamer
  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. W4_SDP_COMPLETE,
  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 bd_addr_t peer_addr;
  79. static state_t state;
  80. // SPP
  81. static uint16_t rfcomm_mtu;
  82. static uint16_t rfcomm_cid = 0;
  83. // static uint32_t data_to_send = DATA_VOLUME;
  84. /**
  85. * RFCOMM can make use for ERTM. Due to the need to re-transmit packets,
  86. * a large buffer is needed to still get high throughput
  87. */
  88. #ifdef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE_FOR_RFCOMM
  89. static uint8_t ertm_buffer[20000];
  90. static l2cap_ertm_config_t ertm_config = {
  91. 0, // ertm mandatory
  92. 8, // max transmit
  93. 2000,
  94. 12000,
  95. 1000, // l2cap ertm mtu
  96. 8,
  97. 8,
  98. 0, // No FCS
  99. };
  100. static int ertm_buffer_in_use;
  101. static void rfcomm_ertm_request_handler(rfcomm_ertm_request_t * ertm_request){
  102. printf("ERTM Buffer requested, buffer in use %u\n", ertm_buffer_in_use);
  103. if (ertm_buffer_in_use) return;
  104. ertm_buffer_in_use = 1;
  105. ertm_request->ertm_config = &ertm_config;
  106. ertm_request->ertm_buffer = ertm_buffer;
  107. ertm_request->ertm_buffer_size = sizeof(ertm_buffer);
  108. }
  109. static void rfcomm_ertm_released_handler(uint16_t ertm_id){
  110. printf("ERTM Buffer released, buffer in use %u, ertm_id %x\n", ertm_buffer_in_use, ertm_id);
  111. ertm_buffer_in_use = 0;
  112. }
  113. #endif
  114. /**
  115. * Find remote peer by COD
  116. */
  117. #define INQUIRY_INTERVAL 5
  118. static void start_scan(void){
  119. printf("Starting inquiry scan..\n");
  120. state = W4_PEER_COD;
  121. gap_inquiry_start(INQUIRY_INTERVAL);
  122. }
  123. static void stop_scan(void){
  124. printf("Stopping inquiry scan..\n");
  125. state = W4_SCAN_COMPLETE;
  126. gap_inquiry_stop();
  127. }
  128. /*
  129. * @section Track throughput
  130. * @text We calculate the throughput by setting a start time and measuring the amount of
  131. * data sent. After a configurable REPORT_INTERVAL_MS, we print the throughput in kB/s
  132. * and reset the counter and start time.
  133. */
  134. /* LISTING_START(tracking): Tracking throughput */
  135. #define REPORT_INTERVAL_MS 3000
  136. static uint32_t test_data_transferred;
  137. static uint32_t test_data_start;
  138. static void test_reset(void){
  139. test_data_start = btstack_run_loop_get_time_ms();
  140. test_data_transferred = 0;
  141. }
  142. static void test_track_transferred(int bytes_sent){
  143. test_data_transferred += bytes_sent;
  144. // evaluate
  145. uint32_t now = btstack_run_loop_get_time_ms();
  146. uint32_t time_passed = now - test_data_start;
  147. if (time_passed < REPORT_INTERVAL_MS) return;
  148. // print speed
  149. int bytes_per_second = test_data_transferred * 1000 / time_passed;
  150. printf("%u bytes -> %u.%03u kB/s\n", (int) test_data_transferred, (int) bytes_per_second / 1000, bytes_per_second % 1000);
  151. // restart
  152. test_data_start = now;
  153. test_data_transferred = 0;
  154. }
  155. /* LISTING_END(tracking): Tracking throughput */
  156. #if (TEST_MODE & TEST_MODE_SEND)
  157. static void spp_create_test_data(void){
  158. int x,y;
  159. for (y=0;y<NUM_ROWS;y++){
  160. for (x=0;x<NUM_COLS-2;x++){
  161. test_data[y*NUM_COLS+x] = '0' + (x % 10);
  162. }
  163. test_data[y*NUM_COLS+NUM_COLS-2] = '\n';
  164. test_data[y*NUM_COLS+NUM_COLS-1] = '\r';
  165. }
  166. }
  167. static void spp_send_packet(void){
  168. rfcomm_send(rfcomm_cid, (uint8_t*) test_data, spp_test_data_len);
  169. test_track_transferred(spp_test_data_len);
  170. rfcomm_request_can_send_now_event(rfcomm_cid);
  171. }
  172. #endif
  173. /*
  174. * @section SDP Query Packet Handler
  175. *
  176. * @text Store RFCOMM Channel for SPP service and initiates RFCOMM connection
  177. */
  178. static void handle_query_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
  179. UNUSED(packet_type);
  180. UNUSED(channel);
  181. UNUSED(size);
  182. switch (packet[0]){
  183. case SDP_EVENT_QUERY_RFCOMM_SERVICE:
  184. rfcomm_server_channel = sdp_event_query_rfcomm_service_get_rfcomm_channel(packet);
  185. break;
  186. case SDP_EVENT_QUERY_COMPLETE:
  187. if (sdp_event_query_complete_get_status(packet)){
  188. printf("SDP query failed 0x%02x\n", sdp_event_query_complete_get_status(packet));
  189. break;
  190. }
  191. if (rfcomm_server_channel == 0){
  192. printf("No SPP service found\n");
  193. break;
  194. }
  195. printf("SDP query done, channel %u.\n", rfcomm_server_channel);
  196. rfcomm_create_channel(packet_handler, peer_addr, rfcomm_server_channel, NULL);
  197. break;
  198. }
  199. }
  200. /*
  201. * @section Gerenal Packet Handler
  202. *
  203. * @text Handles startup (BTSTACK_EVENT_STATE), inquiry, pairing, starts SDP query for SPP service, and RFCOMM connection
  204. */
  205. static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
  206. UNUSED(channel);
  207. bd_addr_t event_addr;
  208. uint8_t rfcomm_channel_nr;
  209. uint32_t class_of_device;
  210. switch (packet_type) {
  211. case HCI_EVENT_PACKET:
  212. switch (hci_event_packet_get_type(packet)) {
  213. case BTSTACK_EVENT_STATE:
  214. if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return;
  215. start_scan();
  216. break;
  217. case GAP_EVENT_INQUIRY_RESULT:
  218. if (state != W4_PEER_COD) break;
  219. class_of_device = gap_event_inquiry_result_get_class_of_device(packet);
  220. gap_event_inquiry_result_get_bd_addr(packet, event_addr);
  221. if (class_of_device == TEST_COD){
  222. memcpy(peer_addr, event_addr, 6);
  223. printf("Peer found: %s\n", bd_addr_to_str(peer_addr));
  224. stop_scan();
  225. } else {
  226. printf("Device found: %s with COD: 0x%06x\n", bd_addr_to_str(event_addr), (int) class_of_device);
  227. }
  228. break;
  229. case GAP_EVENT_INQUIRY_COMPLETE:
  230. switch (state){
  231. case W4_PEER_COD:
  232. printf("Inquiry complete\n");
  233. printf("Peer not found, starting scan again\n");
  234. start_scan();
  235. break;
  236. case W4_SCAN_COMPLETE:
  237. printf("Start to connect and query for SPP service\n");
  238. state = W4_RFCOMM_CHANNEL;
  239. sdp_client_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, peer_addr, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT);
  240. break;
  241. default:
  242. break;
  243. }
  244. if (state == W4_PEER_COD){
  245. }
  246. break;
  247. case HCI_EVENT_PIN_CODE_REQUEST:
  248. // inform about pin code request
  249. printf("Pin code request - using '0000'\n");
  250. hci_event_pin_code_request_get_bd_addr(packet, event_addr);
  251. gap_pin_code_response(event_addr, "0000");
  252. break;
  253. case HCI_EVENT_USER_CONFIRMATION_REQUEST:
  254. // inform about user confirmation request
  255. printf("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", little_endian_read_32(packet, 8));
  256. printf("SSP User Confirmation Auto accept\n");
  257. break;
  258. case RFCOMM_EVENT_INCOMING_CONNECTION:
  259. // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
  260. rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr);
  261. rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet);
  262. rfcomm_cid = rfcomm_event_incoming_connection_get_rfcomm_cid(packet);
  263. printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr));
  264. rfcomm_accept_connection(rfcomm_cid);
  265. break;
  266. case RFCOMM_EVENT_CHANNEL_OPENED:
  267. // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16)
  268. if (rfcomm_event_channel_opened_get_status(packet)) {
  269. printf("RFCOMM channel open failed, status %u\n", rfcomm_event_channel_opened_get_status(packet));
  270. } else {
  271. rfcomm_cid = rfcomm_event_channel_opened_get_rfcomm_cid(packet);
  272. rfcomm_mtu = rfcomm_event_channel_opened_get_max_frame_size(packet);
  273. printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_cid, rfcomm_mtu);
  274. test_reset();
  275. // disable page/inquiry scan to get max performance
  276. gap_discoverable_control(0);
  277. gap_connectable_control(0);
  278. #if (TEST_MODE & TEST_MODE_SEND)
  279. // configure test data
  280. spp_test_data_len = rfcomm_mtu;
  281. if (spp_test_data_len > sizeof(test_data)){
  282. spp_test_data_len = sizeof(test_data);
  283. }
  284. spp_create_test_data();
  285. // start sending
  286. rfcomm_request_can_send_now_event(rfcomm_cid);
  287. #endif
  288. }
  289. break;
  290. #if (TEST_MODE & TEST_MODE_SEND)
  291. case RFCOMM_EVENT_CAN_SEND_NOW:
  292. spp_send_packet();
  293. break;
  294. #endif
  295. case RFCOMM_EVENT_CHANNEL_CLOSED:
  296. printf("RFCOMM channel closed\n");
  297. rfcomm_cid = 0;
  298. // re-enable page/inquiry scan again
  299. gap_discoverable_control(1);
  300. gap_connectable_control(1);
  301. break;
  302. default:
  303. break;
  304. }
  305. break;
  306. case RFCOMM_DATA_PACKET:
  307. test_track_transferred(size);
  308. #if 0
  309. printf("RCV: '");
  310. for (i=0;i<size;i++){
  311. putchar(packet[i]);
  312. }
  313. printf("'\n");
  314. #endif
  315. break;
  316. default:
  317. break;
  318. }
  319. }
  320. /*
  321. * @section Main Application Setup
  322. *
  323. * @text As with the packet and the heartbeat handlers, the combined app setup contains the code from the individual example setups.
  324. */
  325. /* LISTING_START(MainConfiguration): Init L2CAP RFCOMM SDO SM ATT Server and start heartbeat timer */
  326. int btstack_main(int argc, const char * argv[]);
  327. int btstack_main(int argc, const char * argv[]){
  328. UNUSED(argc);
  329. (void)argv;
  330. l2cap_init();
  331. rfcomm_init();
  332. #ifdef ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE_FOR_RFCOMM
  333. // setup ERTM management
  334. rfcomm_enable_l2cap_ertm(&rfcomm_ertm_request_handler, &rfcomm_ertm_released_handler);
  335. #endif
  336. // register for HCI events
  337. hci_event_callback_registration.callback = &packet_handler;
  338. hci_add_event_handler(&hci_event_callback_registration);
  339. // init SDP
  340. gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
  341. // turn on!
  342. hci_power_control(HCI_POWER_ON);
  343. return 0;
  344. }
  345. /* LISTING_END */
  346. /* EXAMPLE_END */