1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <cstdint>
20 #include <iomanip>
21 #include <iostream>
22 #include <sstream>
23 #include <string>
24 
25 #include "common/time_util.h"
26 
27 #define BTM_PKT_STATUS_LEN 64
28 #define BTM_PKT_STATUS_WBS_FRAME_US 7500
29 
30 /* Object to log consecutive packets' status */
31 struct tBTM_SCO_PKT_STATUS {
32   // Bytes to store packets' status.
33   uint8_t data[BTM_PKT_STATUS_LEN];
34   // Total number of bits in |data|.
35   int size;
36   // Position of the next bit to log packet status.
37   int offset;
38   // Whether the ring buffer is full to be wrapped.
39   bool is_full;
40   // The timestamp of the first bit of |data|'s last update.
41   uint64_t ts;
42 
43  public:
inittBTM_SCO_PKT_STATUS44   void init() {
45     std::fill(std::begin(data), std::end(data), 0);
46     size = BTM_PKT_STATUS_LEN * 8;
47     offset = 0;
48     is_full = false;
49     ts = 0;
50   }
51 
updatetBTM_SCO_PKT_STATUS52   void update(bool is_lost) {
53     if (is_lost) {
54       data[offset / 8] |= 1UL << (offset % 8);
55     } else {
56       data[offset / 8] &= ~(1UL << (offset % 8));
57     }
58     if (offset == 0) {
59       ts = bluetooth::common::time_gettimeofday_us();
60     }
61     offset++;
62     if (offset == size) {
63       offset = 0;
64       is_full = true;
65     }
66   }
67 
68   /* Rewinds logger's time stamp to calculate the beginning.
69    * If logger's ring buffer hasn't wrapped, simply return ts.
70    * Otherwise begin_ts = ts - WBS_FRAME_US * (size - offset)
71    */
begin_ts_raw_ustBTM_SCO_PKT_STATUS72   uint64_t begin_ts_raw_us() {
73     return !is_full ? ts : ts - BTM_PKT_STATUS_WBS_FRAME_US * (size - offset);
74   }
75 
76   /* Fast-forwards the logger's time stamp to calculate the end.
77    * In other words, end_ts = logger_ts + WBS_FRAME_US * wp
78    */
end_ts_raw_ustBTM_SCO_PKT_STATUS79   uint64_t end_ts_raw_us() { return ts + BTM_PKT_STATUS_WBS_FRAME_US * offset; }
80 
data_to_hex_stringtBTM_SCO_PKT_STATUS81   std::string data_to_hex_string() {
82     int i;
83     int len = is_full ? size : offset;
84     int head = is_full ? offset : 0;
85     uint8_t byte = 0;
86     std::stringstream oss;
87 
88     for (i = 0; i < len; ++i) {
89       int j = (head + i) % size;
90       byte |= (1U << (j % 8)) & data[j / 8];
91 
92       if ((i + 1) % 8 == 0) {
93         // +(byte) to prevent an uint8_t to be interpreted as a char
94         oss << std::hex << std::setw(2) << std::setfill('0') << +(byte);
95         byte = 0;
96       }
97     }
98 
99     if (i % 8) oss << std::hex << std::setw(2) << std::setfill('0') << +(byte);
100 
101     return oss.str();
102   }
103 
data_to_binary_stringtBTM_SCO_PKT_STATUS104   std::string data_to_binary_string() {
105     int head = is_full ? offset : 0;
106     int len = is_full ? size : offset;
107     std::string s;
108 
109     for (int i = 0; i < len; ++i) {
110       int j = (head + i) % size;
111       s += std::to_string(((data[j / 8] >> (j % 8)) & 1U));
112     }
113 
114     return s;
115   }
116 };
117