/******************************************************************************
*
* Copyright (C) 2009-2012 Broadcom 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.
*
******************************************************************************/
/******************************************************************************
*
* Filename: bte_conf.c
*
* Description: Contains functions to conduct run-time module configuration
* based on entries present in the .conf file
*
******************************************************************************/
#define LOG_TAG "bte_conf"
#include <utils/Log.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "bt_target.h"
#include "bta_api.h"
/******************************************************************************
** Externs
******************************************************************************/
extern BOOLEAN hci_logging_enabled;
extern char hci_logfile[256];
extern BOOLEAN trace_conf_enabled;
void bte_trace_conf(char *p_name, char *p_conf_value);
int device_name_cfg(char *p_conf_name, char *p_conf_value);
int device_class_cfg(char *p_conf_name, char *p_conf_value);
int logging_cfg_onoff(char *p_conf_name, char *p_conf_value);
int logging_set_filepath(char *p_conf_name, char *p_conf_value);
int trace_cfg_onoff(char *p_conf_name, char *p_conf_value);
BD_NAME local_device_default_name = BTM_DEF_LOCAL_NAME;
DEV_CLASS local_device_default_class = {0x40, 0x02, 0x0C};
/******************************************************************************
** Local type definitions
******************************************************************************/
#define CONF_DBG 0
#define info(format, ...) ALOGI (format, ## __VA_ARGS__)
#define debug(format, ...) if (CONF_DBG) ALOGD (format, ## __VA_ARGS__)
#define error(format, ...) ALOGE (format, ## __VA_ARGS__)
#define CONF_KEY_LEN 32
#define CONF_VALUE_LEN 96
#define CONF_COMMENT '#'
#define CONF_DELIMITERS " =\n\r\t"
#define CONF_VALUES_DELIMITERS "\"=\n\r\t"
#define CONF_COD_DELIMITERS " {,}\t"
#define CONF_MAX_LINE_LEN 255
typedef int (conf_action_t)(char *p_conf_name, char *p_conf_value);
typedef struct {
const char *conf_entry;
conf_action_t *p_action;
} conf_entry_t;
typedef struct {
char key[CONF_KEY_LEN];
char value[CONF_VALUE_LEN];
} tKEY_VALUE_PAIRS;
enum {
CONF_DID,
CONF_DID_RECORD_NUM,
CONF_DID_PRIMARY_RECORD,
CONF_DID_VENDOR_ID,
CONF_DID_VENDOR_ID_SOURCE,
CONF_DID_PRODUCT_ID,
CONF_DID_VERSION,
CONF_DID_CLIENT_EXECUTABLE_URL,
CONF_DID_SERVICE_DESCRIPTION,
CONF_DID_DOCUMENTATION_URL,
CONF_DID_MAX
};
typedef UINT8 tCONF_DID;
/******************************************************************************
** Static variables
******************************************************************************/
/*
* Current supported entries and corresponding action functions
*/
/* TODO: Name and Class are duplicated with NVRAM adapter_info. Need to be sorted out */
static const conf_entry_t conf_table[] = {
/*{"Name", device_name_cfg},
{"Class", device_class_cfg},*/
{"BtSnoopLogOutput", logging_cfg_onoff},
{"BtSnoopFileName", logging_set_filepath},
{"TraceConf", trace_cfg_onoff},
{(const char *) NULL, NULL}
};
static tKEY_VALUE_PAIRS did_conf_pairs[CONF_DID_MAX] = {
{ "[DID]", "" },
{ "recordNumber", "" },
{ "primaryRecord", "" },
{ "vendorId", "" },
{ "vendorIdSource", "" },
{ "productId", "" },
{ "version", "" },
{ "clientExecutableURL", "" },
{ "serviceDescription", "" },
{ "documentationURL", "" },
};
/*****************************************************************************
** FUNCTIONS
*****************************************************************************/
int device_name_cfg(char *p_conf_name, char *p_conf_value)
{
strcpy((char *)local_device_default_name, p_conf_value);
return 0;
}
int device_class_cfg(char *p_conf_name, char *p_conf_value)
{
char *p_token;
unsigned int x;
p_token = strtok(p_conf_value, CONF_COD_DELIMITERS);
sscanf(p_token, "%x", &x);
local_device_default_class[0] = (UINT8) x;
p_token = strtok(NULL, CONF_COD_DELIMITERS);
sscanf(p_token, "%x", &x);
local_device_default_class[1] = (UINT8) x;
p_token = strtok(NULL, CONF_COD_DELIMITERS);
sscanf(p_token, "%x", &x);
local_device_default_class[2] = (UINT8) x;
return 0;
}
int logging_cfg_onoff(char *p_conf_name, char *p_conf_value)
{
if (strcmp(p_conf_value, "true") == 0)
hci_logging_enabled = TRUE;
else
hci_logging_enabled = FALSE;
return 0;
}
int logging_set_filepath(char *p_conf_name, char *p_conf_value)
{
strcpy(hci_logfile, p_conf_value);
return 0;
}
int trace_cfg_onoff(char *p_conf_name, char *p_conf_value)
{
trace_conf_enabled = (strcmp(p_conf_value, "true") == 0) ? TRUE : FALSE;
return 0;
}
/*****************************************************************************
** CONF INTERFACE FUNCTIONS
*****************************************************************************/
/*******************************************************************************
**
** Function bte_load_conf
**
** Description Read conf entry from p_path file one by one and call
** the corresponding config function
**
** Returns None
**
*******************************************************************************/
void bte_load_conf(const char *p_path)
{
FILE *p_file;
char *p_name;
char *p_value;
conf_entry_t *p_entry;
char line[CONF_MAX_LINE_LEN+1]; /* add 1 for \0 char */
BOOLEAN name_matched;
ALOGI("Attempt to load stack conf from %s", p_path);
if ((p_file = fopen(p_path, "r")) != NULL)
{
/* read line by line */
while (fgets(line, CONF_MAX_LINE_LEN+1, p_file) != NULL)
{
if (line[0] == CONF_COMMENT)
continue;
p_name = strtok(line, CONF_DELIMITERS);
if (NULL == p_name)
{
continue;
}
p_value = strtok(NULL, CONF_VALUES_DELIMITERS);
if (NULL == p_value)
{
ALOGW("bte_load_conf: missing value for name: %s", p_name);
continue;
}
name_matched = FALSE;
p_entry = (conf_entry_t *)conf_table;
while (p_entry->conf_entry != NULL)
{
if (strcmp(p_entry->conf_entry, (const char *)p_name) == 0)
{
name_matched = TRUE;
if (p_entry->p_action != NULL)
p_entry->p_action(p_name, p_value);
break;
}
p_entry++;
}
if ((name_matched == FALSE) && (trace_conf_enabled == TRUE))
{
/* Check if this is a TRC config item */
bte_trace_conf(p_name, p_value);
}
}
fclose(p_file);
}
else
{
ALOGI( "bte_load_conf file >%s< not found", p_path);
}
}
/*******************************************************************************
**
** Function bte_parse_did_conf
**
** Description Read conf entry from p_path file one by one and get
** the corresponding config value
**
** Returns TRUE if success, else FALSE
**
*******************************************************************************/
static BOOLEAN bte_parse_did_conf (const char *p_path, UINT32 num,
tKEY_VALUE_PAIRS *conf_pairs, UINT32 conf_pairs_num)
{
UINT32 i, param_num=0, count=0, start_count=0, end_count=0, conf_num=0;
BOOLEAN key=TRUE, conf_found=FALSE;
FILE *p_file;
char *p;
char line[CONF_MAX_LINE_LEN+1]; /* add 1 for \0 char */
ALOGI("Attempt to load did conf from %s", p_path);
if ((p_file = fopen(p_path, "r")) != NULL)
{
/* read line by line */
while (fgets(line, CONF_MAX_LINE_LEN+1, p_file) != NULL)
{
count++;
if (line[0] == CONF_COMMENT)
continue;
if (conf_found && (conf_num == num) && (*line == '[')) {
conf_found = FALSE;
end_count = count-1;
break;
}
p = strtok(line, CONF_DELIMITERS);
while (p != NULL) {
if (conf_num <= num) {
if (key) {
if (!strcmp(p, conf_pairs[0].key)) {
if (++conf_num == num) {
conf_found = TRUE;
start_count = count;
strncpy(conf_pairs[0].value, "1", CONF_VALUE_LEN);
}
} else {
if (conf_num == num) {
for (i=1; i<conf_pairs_num; i++) {
if (!strcmp(p, conf_pairs[i].key)) {
param_num = i;
break;
}
}
if (i == conf_pairs_num) {
error("Attribute %s does not belong to %s configuration",
p, conf_pairs[0].key);
fclose(p_file);
return FALSE;
}
}
key = FALSE;
}
} else {
if ((conf_num == num) && param_num) {
strncpy(conf_pairs[param_num].value, p, CONF_VALUE_LEN-1);
param_num = 0;
}
key = TRUE;
}
}
p = strtok(NULL, CONF_DELIMITERS);
}
}
fclose(p_file);
}
else
{
ALOGI( "bte_parse_did_conf file >%s< not found", p_path);
}
if (!end_count)
end_count = count;
if (start_count) {
debug("Read %s configuration #%u from lines %u to %u in file %s",
conf_pairs[0].key, (unsigned int)num, (unsigned int)start_count,
(unsigned int)end_count, p_path);
return TRUE;
}
error("%s configuration not found in file %s", conf_pairs[0].key, p_path);
return FALSE;
}
/*******************************************************************************
**
** Function bte_load_did_conf
**
** Description Set local Device ID records, reading from configuration files
**
** Returns None
**
*******************************************************************************/
void bte_load_did_conf (const char *p_path)
{
tBTA_DI_RECORD rec;
UINT32 rec_num, i, j;
for (i=1; i<=BTA_DI_NUM_MAX; i++) {
for (j=0; j<CONF_DID_MAX; j++) {
*did_conf_pairs[j].value = 0;
}
if (bte_parse_did_conf(p_path, i, did_conf_pairs, CONF_DID_MAX)) {
memset(&rec, 0, sizeof(rec));
if (*did_conf_pairs[CONF_DID_RECORD_NUM].value) {
rec_num = (UINT32)(strtoul(did_conf_pairs[CONF_DID_RECORD_NUM].value, NULL, 0)-1);
} else {
debug("[%d] Unknown %s", (unsigned int)i, did_conf_pairs[CONF_DID_RECORD_NUM].key);
continue;
}
if (*did_conf_pairs[CONF_DID_VENDOR_ID].value) {
rec.vendor = (UINT16)strtoul(did_conf_pairs[CONF_DID_VENDOR_ID].value, NULL, 0);
} else {
rec.vendor = LMP_COMPID_BROADCOM;
}
if (*did_conf_pairs[CONF_DID_VENDOR_ID_SOURCE].value) {
rec.vendor_id_source = (UINT16)strtoul(did_conf_pairs[CONF_DID_VENDOR_ID_SOURCE].value, NULL, 0);
} else {
rec.vendor_id_source = DI_VENDOR_ID_SOURCE_BTSIG;
}
if ((*did_conf_pairs[CONF_DID].value == 0) ||
(rec_num >= BTA_DI_NUM_MAX) ||
(!((rec.vendor_id_source >= DI_VENDOR_ID_SOURCE_BTSIG) &&
(rec.vendor_id_source <= DI_VENDOR_ID_SOURCE_USBIF))) ||
(rec.vendor == DI_VENDOR_ID_DEFAULT)) {
error("DID record #%u not set", (unsigned int)i);
for (j=0; j<CONF_DID_MAX; j++) {
error("%s:%s", did_conf_pairs[j].key, did_conf_pairs[j].value);
}
continue;
}
rec.product = (UINT16)strtoul(did_conf_pairs[CONF_DID_PRODUCT_ID].value, NULL, 0);
rec.version = (UINT16)strtoul(did_conf_pairs[CONF_DID_VERSION].value, NULL, 0);
strncpy(rec.client_executable_url,
did_conf_pairs[CONF_DID_CLIENT_EXECUTABLE_URL].value,
SDP_MAX_ATTR_LEN);
strncpy(rec.service_description,
did_conf_pairs[CONF_DID_SERVICE_DESCRIPTION].value,
SDP_MAX_ATTR_LEN);
strncpy(rec.documentation_url,
did_conf_pairs[CONF_DID_DOCUMENTATION_URL].value,
SDP_MAX_ATTR_LEN);
for (j=0; j<strlen(did_conf_pairs[CONF_DID_PRIMARY_RECORD].value); j++) {
did_conf_pairs[CONF_DID_PRIMARY_RECORD].value[j] =
tolower(did_conf_pairs[CONF_DID_PRIMARY_RECORD].value[j]);
}
if ((!strcmp(did_conf_pairs[CONF_DID_PRIMARY_RECORD].value, "true")) ||
(!strcmp(did_conf_pairs[CONF_DID_PRIMARY_RECORD].value, "1"))) {
rec.primary_record = TRUE;
} else {
rec.primary_record = FALSE;
}
info("[%u] primary_record=%d vendor_id=0x%04X vendor_id_source=0x%04X product_id=0x%04X version=0x%04X",
(unsigned int)rec_num+1, rec.primary_record, rec.vendor,
rec.vendor_id_source, rec.product, rec.version);
if (*rec.client_executable_url) {
info(" client_executable_url=%s", rec.client_executable_url);
}
if (*rec.service_description) {
info(" service_description=%s", rec.service_description);
}
if (*rec.documentation_url) {
info(" documentation_url=%s", rec.documentation_url);
}
if (BTA_DmSetLocalDiRecord(&rec, &rec_num) != BTA_SUCCESS) {
error("SetLocalDiInfo failed for #%u!", (unsigned int)i);
}
}
}
}