Files
BPI-CM9-Linux/aml-comp/hardware/bluetooth/amlogic/aml_hciattach/aml_comm.c
T

357 lines
8.8 KiB
C

/******************************************************************************
*
* Copyright (C) 2021-2021 amlogic 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 _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "aml_comm.h"
#include "aml_multibt.h"
#include "aml_conf.h"
/*****************************************************************************
** COMMON FUNCTIONS DEFINE
*****************************************************************************/
static int mkdir_p(const char *path, mode_t mode)
{
char tmp[256];
char *p = NULL;
size_t len;
if (!path || !*path) {
ALOGE("path err");
return -1;
}
snprintf(tmp, sizeof(tmp), "%s", path);
len = strlen(tmp);
if (tmp[len - 1] == '/')
tmp[len - 1] = '\0';
for (p = tmp + 1; *p; p++) {
if (*p == '/') {
*p = '\0';
if (mkdir(tmp, mode) && errno != EEXIST) {
ALOGE("mkdir %s err", tmp);
return -1;
}
*p = '/';
}
}
if (mkdir(tmp, mode) && errno != EEXIST) {
ALOGE("mkdir %s err", tmp);
return -1;
}
return 0;
}
int amlbt_write_file(const char* fpath, const char *str, int len)
{
int ret = -1;
int fd;
char path_copy[BUF_MAX_LEN];
char *dir;
if (!fpath || !str || len <= 0) {
ALOGE("invalid input: path=%p str=%p size=%d", fpath, str, len);
return -1;
}
snprintf(path_copy, sizeof(path_copy), "%s", fpath);
dir = dirname(path_copy);
if (mkdir_p(dir, 0755) < 0) {
ALOGE("mkdir_p fail for %s", dir);
return -1;
}
fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
ALOGE("open:%s fail", fpath);
return -1;
}
ret = write(fd, str, len);
if (ret != len) {
ALOGE("write fail: expect:%d, get:%d", len, ret);
ret = -1;
}
close(fd);
return ret;
}
int amlbt_get_random_addr(unsigned char *bdaddr, enum addr_mode mode)
{
int fd;
ssize_t cnt, need;
unsigned char buffer[BT_MAC_LEN] = {0};
fd = open(AMLBT_RANDOM_DEV, O_RDONLY);
if (fd < 0) {
ALOGE("open:%s fail", AMLBT_RANDOM_DEV);
return -1;
}
need = sizeof(buffer);
cnt = 0;
while (cnt < need) {
ssize_t n = read(fd, buffer + cnt, need - cnt);
if (n <= 0) {
ALOGE("read :%s fail, cnt:%zd", AMLBT_RANDOM_DEV, cnt);
close(fd);
return -1;
}
cnt += n;
}
close(fd);
if (mode == ADDR_FIXED_PREFIX) {
// Prefix 0x22:22, last 4 bytes are random
bdaddr[0] = 0x22;
bdaddr[1] = 0x22;
memcpy(bdaddr + 2, buffer + 2, 4);
} else {
// Fully random 6 bytes
memcpy(bdaddr, buffer, BT_MAC_LEN);
// Ensure locally administered + unicast
bdaddr[0] &= 0xFE; // clear bit0 → unicast
bdaddr[0] |= 0x02; // set bit1 → locally administered
}
return 0;
}
static char *amlbt_read_file(const char *path)
{
int fd = open(path, O_RDONLY);
if (fd < 0) {
ALOGE("open %s failed: %s", path, strerror(errno));
return NULL;
}
struct stat st;
if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode) || st.st_size == 0) {
ALOGE("stat err");
close(fd);
return NULL;
}
char *buf = calloc(1, st.st_size + 1);
if (!buf) {
ALOGE("calloc %zu bytes fail: %s", (size_t)st.st_size + 1, strerror(errno));
close(fd);
return NULL;
}
ssize_t total = 0;
while (total < st.st_size) {
ssize_t r = read(fd, buf + total, st.st_size - total);
if (r <= 0) {
ALOGE("read fail: %zd", r);
free(buf);
close(fd);
return NULL;
}
total += r;
}
close(fd);
return buf; // Caller is responsible for free()
}
static char *amlbt_get_value(const char *file_buf, const char *key)
{
static char val[BUF_MAX_LEN];
char *pos = strstr(file_buf, key);
if (!pos)
return NULL;
pos = strchr(pos, ':');
if (!pos)
return NULL;
pos++;
while (*pos == ' ') pos++;
char *end = strchr(pos, '\n');
if (!end)
end = (char *)(file_buf + strlen(file_buf));
size_t len = end - pos;
if (len >= sizeof(val))
len = sizeof(val) - 1;
strncpy(val, pos, len);
val[len] = '\0';
return val;
}
int amlbt_info_update(const char *key, const char *new_val)
{
char *buf = amlbt_read_file(AMLBT_INFO_PATH);
char new_content[BUF_MAX_LEN*4] = {0};
char old_val[BUF_MAX_LEN] = {0};
char name_val[BUF_MAX_LEN] = {0};
char addr_val[BUF_MAX_LEN] = {0};
char fw_val[BUF_MAX_LEN] = {0};
if (buf) {
char *temp = amlbt_get_value(buf, key);
if (temp && strcmp(temp, new_val) == 0) {
ALOGD("%s not changed, skip write", key);
free(buf);
return 0;
}
strncpy(old_val, temp ? temp : "", sizeof(old_val)-1);
old_val[BUF_MAX_LEN-1] = '\0';
char *tmp;
tmp = amlbt_get_value(buf, AMLBT_NAME);
strncpy(name_val, tmp ? tmp : "", sizeof(name_val)-1);
tmp = amlbt_get_value(buf, AMLBT_ADDR);
strncpy(addr_val, tmp ? tmp : "", sizeof(addr_val)-1);
tmp = amlbt_get_value(buf, AMLBT_FW_VER);
strncpy(fw_val, tmp ? tmp : "", sizeof(fw_val)-1);
free(buf);
} else {
old_val[0] = '\0';
}
if (strcmp(key, AMLBT_NAME) == 0) strncpy(name_val, new_val, sizeof(name_val)-1);
if (strcmp(key, AMLBT_ADDR) == 0) strncpy(addr_val, new_val, sizeof(addr_val)-1);
if (strcmp(key, AMLBT_FW_VER) == 0) strncpy(fw_val, new_val, sizeof(fw_val)-1);
snprintf(new_content, sizeof(new_content),
"amlbt_name: %s\n"
"addr: %s\n"
"fw_version: %s\n",
name_val,
addr_val,
fw_val);
int fd = open(AMLBT_INFO_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
ALOGE("open %s fail: %s", AMLBT_INFO_PATH, strerror(errno));
return -1;
}
size_t len = strlen(new_content);
size_t written = 0;
while (written < len) {
ssize_t r = write(fd, new_content + written, len - written);
if (r < 0) {
ALOGE("write %s fail: %s", AMLBT_INFO_PATH, strerror(errno));
close(fd);
return -1;
}
written += r;
}
fsync(fd);
close(fd);
ALOGD("update %s: %s -> %s", key, old_val, new_val);
return 0;
}
int amlbt_get_fw_ver(const unsigned char *str)
{
if (!str) {
ALOGE("parameters err");
return -1;
}
char *fw_version = NULL;
const unsigned char *fw_ptr = str + 7; // skip 7 bytes
int ret = asprintf(&fw_version,
"date:%02x.%02x, number:0x%02x%02x",
*(fw_ptr + 1), *fw_ptr, *(fw_ptr + 3), *(fw_ptr + 2));
if (ret == -1 || !fw_version) {
ALOGE("get fw version fail");
return -1;
}
ret = amlbt_info_update(AMLBT_FW_VER, fw_version);
if (ret < 0) {
ALOGE("update %s fail", AMLBT_FW_VER);
free(fw_version);
return ret;
}
free(fw_version);
return 0;
}
int amlbt_read_addr(unsigned char *addr)
{
if (!addr) {
ALOGE("parameters err");
return -1;
}
char *buf = amlbt_read_file(AMLBT_INFO_PATH);
if (!buf) {
ALOGE("read %s fail", AMLBT_INFO_PATH);
return -1;
}
char *addr_str = amlbt_get_value(buf, AMLBT_ADDR);
if (!addr_str || strlen(addr_str) != BT_MAC_BUF_LEN) {
ALOGE("%s key not found or invalid: %s", AMLBT_ADDR, addr_str ? addr_str : "(null)");
free(buf);
return -1;
}
unsigned int val[BT_MAC_LEN];
int n = sscanf(addr_str, "%2x:%2x:%2x:%2x:%2x:%2x",
&val[0], &val[1], &val[2],
&val[3], &val[4], &val[5]);
if (n != BT_MAC_LEN) {
ALOGE("invalid addr format: %s", addr_str);
free(buf);
return -1;
}
for (int i = 0; i < BT_MAC_LEN; i++)
addr[i] = (unsigned char)val[i];
free(buf);
return 0;
}