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__ "hci_dump_embedded_stdout.c"
39
40 /*
41 * Dump HCI trace on stdout
42 */
43
44 #include "btstack_config.h"
45
46 #ifdef ENABLE_SEGGER_RTT
47
48 #include "hci_dump.h"
49 #include "btstack_util.h"
50 #include "hci.h"
51 #include "hci_dump_segger_rtt_stdout.h"
52
53 #include "SEGGER_RTT.h"
54
55 // [00:00:37.514] LOG -- xxx\n
56 #define ADDITIONAL_CHARS_PER_LINE (14+1+6+1+1)
57
58 static void hci_dump_segger_rtt_stdout_timestamp(void);
59
60 #if SEGGER_RTT_MODE_DEFAULT != SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
61
62 // avoid trunkated log lines by calculating requires space in rtt buffer
63 // if message needs to be skipped, report number skipped messages
64
65 static const char * message_packets_skipped = "RTT buffer full, %3u packet(s) skipped";
66
hci_dump_segger_prepare_message(uint16_t message_size)67 static bool hci_dump_segger_prepare_message(uint16_t message_size){
68
69 static uint32_t hci_dump_rtt_num_skipped = 0;
70
71 // calculate num chars required for line
72 uint16_t required_space = ADDITIONAL_CHARS_PER_LINE + message_size;
73
74 // if we have dropped packets before, we want to print a warning
75 if (hci_dump_rtt_num_skipped > 0){
76 required_space += ADDITIONAL_CHARS_PER_LINE + strlen(message_packets_skipped);
77 }
78
79 // not enough space yet, drop message
80 if (required_space > SEGGER_RTT_GetAvailWriteSpace(0)){
81 hci_dump_rtt_num_skipped++;
82 return false;
83 }
84
85 // print message if dropped packets
86 if (hci_dump_rtt_num_skipped > 0){
87 hci_dump_segger_rtt_stdout_timestamp();
88 SEGGER_RTT_printf(0, "LOG -- ");
89 SEGGER_RTT_printf(0, message_packets_skipped, hci_dump_rtt_num_skipped);
90 SEGGER_RTT_printf(0, "\n");
91 hci_dump_rtt_num_skipped = 0;
92 }
93 return true;
94 }
95 #endif
96
hci_dump_segger_rtt_hexdump(const void * data,int size)97 static void hci_dump_segger_rtt_hexdump(const void *data, int size){
98 char buffer[4];
99 buffer[2] = ' ';
100 buffer[3] = 0;
101 const uint8_t * ptr = (const uint8_t *) data;
102 while (size > 0){
103 uint8_t byte = *ptr++;
104 buffer[0] = char_for_nibble(byte >> 4);
105 buffer[1] = char_for_nibble(byte & 0x0f);
106 SEGGER_RTT_Write(0, buffer, 3);
107 size--;
108 }
109 SEGGER_RTT_PutChar(0, '\n');
110 }
111
hci_dump_segger_rtt_stdout_timestamp(void)112 static void hci_dump_segger_rtt_stdout_timestamp(void){
113 uint32_t time_ms = btstack_run_loop_get_time_ms();
114 int seconds = time_ms / 1000u;
115 int minutes = seconds / 60;
116 unsigned int hours = minutes / 60;
117
118 uint16_t p_ms = time_ms - (seconds * 1000u);
119 uint16_t p_seconds = seconds - (minutes * 60);
120 uint16_t p_minutes = minutes - (hours * 60u);
121 SEGGER_RTT_printf(0, "[%02u:%02u:%02u.%03u] ", hours, p_minutes, p_seconds, p_ms);
122 }
123
hci_dump_segger_rtt_stdout_packet(uint8_t packet_type,uint8_t in,uint8_t * packet,uint16_t len)124 static void hci_dump_segger_rtt_stdout_packet(uint8_t packet_type, uint8_t in, uint8_t * packet, uint16_t len){
125 switch (packet_type){
126 case HCI_COMMAND_DATA_PACKET:
127 SEGGER_RTT_printf(0, "CMD => ");
128 break;
129 case HCI_EVENT_PACKET:
130 SEGGER_RTT_printf(0, "EVT <= ");
131 break;
132 case HCI_ACL_DATA_PACKET:
133 if (in) {
134 SEGGER_RTT_printf(0, "ACL <= ");
135 } else {
136 SEGGER_RTT_printf(0, "ACL => ");
137 }
138 break;
139 case HCI_SCO_DATA_PACKET:
140 if (in) {
141 SEGGER_RTT_printf(0, "SCO <= ");
142 } else {
143 SEGGER_RTT_printf(0, "SCO => ");
144 }
145 break;
146 case HCI_ISO_DATA_PACKET:
147 if (in) {
148 SEGGER_RTT_printf(0, "ISO <= ");
149 } else {
150 SEGGER_RTT_printf(0, "ISO => ");
151 }
152 break;
153 default:
154 return;
155 }
156 hci_dump_segger_rtt_hexdump(packet, len);
157 }
158
hci_dump_segger_rtt_stdout_log_packet(uint8_t packet_type,uint8_t in,uint8_t * packet,uint16_t len)159 static void hci_dump_segger_rtt_stdout_log_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
160 #if SEGGER_RTT_MODE_DEFAULT != SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
161 uint16_t message_len = len * 3;
162 bool ready = hci_dump_segger_prepare_message(message_len);
163 if (!ready) return;
164 #endif
165
166 // print packet
167 hci_dump_segger_rtt_stdout_timestamp();
168 hci_dump_segger_rtt_stdout_packet(packet_type, in, packet, len);
169 }
170
hci_dump_segger_rtt_stdout_log_message(int log_level,const char * format,va_list argptr)171 static void hci_dump_segger_rtt_stdout_log_message(int log_level, const char * format, va_list argptr){
172 UNUSED(log_level);
173 #if SEGGER_RTT_MODE_DEFAULT != SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
174 // to avoid using snprintf for this, we cheat and assume that the messages is less then HCI_DUMP_MAX_MESSAGE_LEN
175 bool ready = hci_dump_segger_prepare_message(HCI_DUMP_MAX_MESSAGE_LEN);
176 if (!ready) return;
177 #endif
178
179 // print message
180 hci_dump_segger_rtt_stdout_timestamp();
181 SEGGER_RTT_printf(0, "LOG -- ");
182 SEGGER_RTT_vprintf(0, format, &argptr);
183 SEGGER_RTT_printf(0, "\n");
184 }
185
hci_dump_segger_rtt_stdout_get_instance(void)186 const hci_dump_t * hci_dump_segger_rtt_stdout_get_instance(void){
187 static const hci_dump_t hci_dump_instance = {
188 // void (*reset)(void);
189 NULL,
190 // void (*log_packet)(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len);
191 &hci_dump_segger_rtt_stdout_log_packet,
192 // void (*log_message)(int log_level, const char * format, va_list argptr);
193 &hci_dump_segger_rtt_stdout_log_message,
194 };
195 return &hci_dump_instance;
196 }
197
198 #endif
199