/****************************************************************************** * * Copyright (C) 2019-2021 Aicsemi Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #define LOG_TAG "aic_btsnoop_net" #include "aic_btsnoop_net.h" #include #define AIC_NO_INTR(fn) do {} while ((fn) == -1 && errno == EINTR) #define DATA_DIRECT_2_ELLISY 1 #define HCI_COMMAND 0x01 #define HCI_ACL_DATA_H2C 0x02 #define HCI_ACL_DATA_C2H 0x82 #define HCI_SCO_DATA_H2C 0x03 #define HCI_SCO_DATA_C2H 0x83 #define HCI_EVENT 0x84 #define HCI_COMMAND_PKT 0x01 #define HCI_ACLDATA_PKT 0x02 #define HCI_SCODATA_PKT 0x03 #define HCI_EVENT_PKT 0x04 unsigned int aicbt_h5logfilter = 0x01; bool aic_btsnoop_dump = false; bool aic_btsnoop_net_dump = false; bool aic_btsnoop_save_log = false; char aic_btsnoop_path[1024] = {'\0'}; static pthread_mutex_t btsnoop_log_lock; static void aic_safe_close_(int *fd); static void *aic_listen_fn_(void *context); static const char *AIC_LISTEN_THREAD_NAME_ = "aic_btsnoop_net"; static const int AIC_LOCALHOST_ = 0xC0A80AE2; // 192.168.10.226 static const int AIC_LISTEN_PORT_ = 8872; static const int AIC_REMOTEHOST_ = 0xC0A80A03; // 192.168.10.21 static const int AIC_REMOTE_PORT_ = 24352; static pthread_t aic_listen_thread_; static bool aic_listen_thread_valid_ = false; static pthread_mutex_t aic_client_socket_lock_ = PTHREAD_MUTEX_INITIALIZER; static int aic_listen_socket_ = -1; // File descriptor for btsnoop file. static int hci_btsnoop_fd = -1; // Epoch in microseconds since 01/01/0000. static const uint64_t BTSNOOP_EPOCH_DELTA = 0x00dcddb30f2f8000ULL; static uint64_t aic_btsnoop_timestamp(void) { struct timeval tv; gettimeofday(&tv, NULL); // Timestamp is in microseconds. uint64_t timestamp = tv.tv_sec * 1000LL * 1000LL; timestamp += tv.tv_usec; timestamp += BTSNOOP_EPOCH_DELTA; return timestamp; } void aic_btsnoop_open() { pthread_mutex_init(&btsnoop_log_lock, NULL); char last_log_path[PATH_MAX]; uint64_t timestamp; uint32_t usec; if (hci_btsnoop_fd != -1) { ALOGE("%s btsnoop log file is already open.", __func__); return; } if (aic_btsnoop_save_log) { time_t current_time = time(NULL); struct tm *time_created = localtime(¤t_time); char config_time_created[sizeof("YYYY-MM-DD-HH:MM:SS")]; strftime(config_time_created, sizeof("YYYY-MM-DD-HH:MM:SS"), "%Y-%m-%d-%H:%M:%S", time_created); timestamp = aic_btsnoop_timestamp() - BTSNOOP_EPOCH_DELTA; usec = (uint32_t)(timestamp % 1000000LL); snprintf(last_log_path, PATH_MAX, "%s.%s:%dUS", aic_btsnoop_path, config_time_created, usec); if (!rename(aic_btsnoop_path, last_log_path) && errno != ENOENT) ALOGE("%s unable to rename '%s' to '%s': %s", __func__, aic_btsnoop_path, last_log_path, strerror(errno)); } else { snprintf(last_log_path, PATH_MAX, "%s.last", aic_btsnoop_path); if (!rename(aic_btsnoop_path, last_log_path) && errno != ENOENT) ALOGE("%s unable to rename '%s' to '%s': %s", __func__, aic_btsnoop_path, last_log_path, strerror(errno)); } hci_btsnoop_fd = open(aic_btsnoop_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if (hci_btsnoop_fd == -1) { ALOGE("%s unable to open '%s': %s", __func__, aic_btsnoop_path, strerror(errno)); return; } write(hci_btsnoop_fd, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16); } void aic_btsnoop_close(void) { pthread_mutex_destroy(&btsnoop_log_lock); if (hci_btsnoop_fd != -1) close(hci_btsnoop_fd); hci_btsnoop_fd = -1; } static void aic_btsnoop_write(const void *data, size_t length) { if (hci_btsnoop_fd != -1) write(hci_btsnoop_fd, data, length); } static void aic_btsnoop_write_packet(serial_data_type_t type, const uint8_t *packet, bool is_received) { int length_he = 0; int length; int flags; int drops = 0; pthread_mutex_lock(&btsnoop_log_lock); switch (type) { case HCI_COMMAND_PKT: length_he = packet[2] + 4; flags = 2; break; case HCI_ACLDATA_PKT: length_he = (packet[3] << 8) + packet[2] + 5; flags = is_received; break; case HCI_SCODATA_PKT: length_he = packet[2] + 4; flags = is_received; break; case HCI_EVENT_PKT: length_he = packet[1] + 3; flags = 3; break; default: break; } uint64_t timestamp = aic_btsnoop_timestamp(); uint32_t time_hi = timestamp >> 32; uint32_t time_lo = timestamp & 0xFFFFFFFF; length = htonl(length_he); flags = htonl(flags); drops = htonl(drops); time_hi = htonl(time_hi); time_lo = htonl(time_lo); aic_btsnoop_write(&length, 4); aic_btsnoop_write(&length, 4); aic_btsnoop_write(&flags, 4); aic_btsnoop_write(&drops, 4); aic_btsnoop_write(&time_hi, 4); aic_btsnoop_write(&time_lo, 4); aic_btsnoop_write(&type, 1); aic_btsnoop_write(packet, length_he - 1); pthread_mutex_unlock(&btsnoop_log_lock); } void aic_btsnoop_capture(const HC_BT_HDR *p_buf, bool is_rcvd) { const uint8_t *p = (const uint8_t *)(p_buf + 1) + p_buf->offset; if (hci_btsnoop_fd == -1) return; switch (p_buf->event & MSG_EVT_MASK) { case MSG_HC_TO_STACK_HCI_EVT: if ((*(p + 3) == 0x94) && (*(p + 4) == 0xfc) && (*(p + 5) == 0x00) && (aicbt_h5logfilter & 1)) { // noting to do } else { aic_btsnoop_write_packet(HCI_EVENT_PKT, p, false); } break; case MSG_HC_TO_STACK_HCI_ACL: case MSG_STACK_TO_HC_HCI_ACL: aic_btsnoop_write_packet(HCI_ACLDATA_PKT, p, is_rcvd); break; case MSG_HC_TO_STACK_HCI_SCO: case MSG_STACK_TO_HC_HCI_SCO: aic_btsnoop_write_packet(HCI_SCODATA_PKT, p, is_rcvd); break; case MSG_STACK_TO_HC_HCI_CMD: if (((aicbt_h5logfilter & 1) == 0) || (*p != 0x94) || (*(p + 1) != 0xfc)) aic_btsnoop_write_packet(HCI_COMMAND_PKT, p, true); break; } } void aic_btsnoop_net_open(void) { aic_listen_thread_valid_ = (pthread_create(&aic_listen_thread_, NULL, aic_listen_fn_, NULL) == 0); if (!aic_listen_thread_valid_) { ALOGE("%s pthread_create failed: %s", __func__, strerror(errno)); } else { ALOGD("initialized"); } } void aic_btsnoop_net_close(void) { if (aic_listen_thread_valid_) { shutdown(aic_listen_socket_, SHUT_RDWR); pthread_join(aic_listen_thread_, NULL); aic_listen_thread_valid_ = false; } } void aic_btsnoop_net_write(serial_data_type_t type, uint8_t *data, bool is_received) { if (aic_listen_socket_ == -1) { return; } int length = 0; uint8_t *p = data; switch (type) { case HCI_COMMAND_PKT: if (((aicbt_h5logfilter & 1) == 0) || (*p != 0x94) || (*(p + 1) != 0xfc)) length = data[2] + 3; else return; break; case HCI_ACLDATA_PKT: length = (data[3] << 8) + data[2] + 4; break; case HCI_SCODATA_PKT: length = data[2] + 3; break; case HCI_EVENT_PKT: if ((*(p + 3) == 0x94) && (*(p + 4) == 0xfc) && (*(p + 5) == 0x00)&&(aicbt_h5logfilter&1)) return; else length = data[1] + 2; break; default: break; } uint8_t buffer[4126] = {0}; //uint8_t test_buffer[] = {0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x00}; //uint8_t test_buffer2[] = {0x01, 0x10, 0x00}; struct sockaddr_in client_addr; int i = 0; #if DATA_DIRECT_2_ELLISY uint8_t bit_rate[4] = {0x00, 0x1b, 0x37, 0x4b}; struct tm *t; time_t tt; time(&tt); t = localtime(&tt); struct timeval tv; gettimeofday(&tv, NULL); uint64_t nano_time = (t->tm_hour * 3600 + t->tm_min * 60 + t->tm_sec) * 1000 * 1000LL * 1000 + tv.tv_usec * 1000; uint16_t year = (t->tm_year + 1900) & 0xFFFF; uint8_t month = (t->tm_mon+1) & 0xFF; uint8_t day = buffer[0] = 0x02; buffer[1] = 0x00; buffer[2] = 0x01; buffer[3] = 0x02; //time memcpy(&buffer[4], &year, 2); buffer[6] = month; buffer[7] = day; memcpy(&buffer[8], &nano_time, 6); //bit rate buffer[14] = 0x80; memcpy(&buffer[15], bit_rate, 4); //type buffer[19] = 0x81; i = 20; #else memcpy(&buffer[i], &length, sizeof(int)); i = 4; #endif switch (type) { case HCI_COMMAND_PKT: buffer[i] = HCI_COMMAND; break; case HCI_ACLDATA_PKT: if (is_received) { buffer[i] = HCI_ACL_DATA_C2H; } else { buffer[i] = HCI_ACL_DATA_H2C; } break; case HCI_SCODATA_PKT: if (is_received) { buffer[i] = HCI_SCO_DATA_C2H; } else { buffer[i] = HCI_SCO_DATA_H2C; } break; case HCI_EVENT_PKT: buffer[i] = HCI_EVENT; break; default: buffer[i] = 0; break; } #if DATA_DIRECT_2_ELLISY //buffer[i] = HCI_COMMAND; buffer[21] = 0x82; i = 22; #else i = 5; #endif memcpy(&buffer[i], data, length); //memcpy(&buffer[i], test_buffer2, 3); memset(&client_addr, 0, sizeof(client_addr)); client_addr.sin_family = AF_INET; client_addr.sin_addr.s_addr = htonl(AIC_REMOTEHOST_); client_addr.sin_port = htons(AIC_REMOTE_PORT_); pthread_mutex_lock(&aic_client_socket_lock_); int ret; AIC_NO_INTR(ret = sendto(aic_listen_socket_, buffer, (length+i), 0,(struct sockaddr*)&client_addr, sizeof(struct sockaddr_in))); //sendto(aic_listen_socket_, buffer, 25, 0,(struct sockaddr*)&client_addr, sizeof(struct sockaddr_in)); pthread_mutex_unlock(&aic_client_socket_lock_); } static void *aic_listen_fn_(void *context) { AIC_UNUSED(context); prctl(PR_SET_NAME, (unsigned long)AIC_LISTEN_THREAD_NAME_, 0, 0, 0); aic_listen_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (aic_listen_socket_ == -1) { ALOGE("%s socket creation failed: %s", __func__, strerror(errno)); goto cleanup; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(AIC_LOCALHOST_); addr.sin_port = htons(AIC_LISTEN_PORT_); struct sockaddr_in client_addr; memset(&client_addr, 0, sizeof(client_addr)); client_addr.sin_family = AF_INET; client_addr.sin_addr.s_addr = htonl(AIC_REMOTEHOST_); client_addr.sin_port = htons(AIC_REMOTE_PORT_); if (bind(aic_listen_socket_, (struct sockaddr *)&addr, sizeof(addr)) == -1) { ALOGE("%s unable to bind listen socket: %s", __func__, strerror(errno)); goto cleanup; } return NULL; cleanup: aic_safe_close_(&aic_listen_socket_); return NULL; } static void aic_safe_close_(int *fd) { assert(fd != NULL); if (*fd != -1) { close(*fd); *fd = -1; } }