/******************************************************************************
|
*
|
* Copyright (C) 2020-2021 SeekWave Technology
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
******************************************************************************/
|
|
/******************************************************************************
|
*
|
* Filename: scom_vendor.c
|
*
|
* Description: serials communication operation
|
*
|
******************************************************************************/
|
|
#include <pthread.h>
|
#include <utils/Log.h>
|
#include <termios.h>
|
#include <fcntl.h>
|
#include <errno.h>
|
#include <stdio.h>
|
#include <sys/eventfd.h>
|
#include <cutils/sockets.h>
|
#include <sys/poll.h>
|
#include <assert.h>
|
#include "scom_vendor.h"
|
#include "bt_hci_bdroid.h"
|
#include "skw_common.h"
|
#include "bt_vendor_skw.h"
|
#include "skw_btsnoop.h"
|
#include "skw_log.h"
|
#include "skw_gen_addr.h"
|
#include "skw_ext.h"
|
|
|
|
bt_hw_cfg_cb_st hw_cfg_cb;
|
scomm_vnd_st scomm_vnd[BT_COM_PORT_SIZE];
|
skw_socket_object_st skw_socket_object;
|
uint8_t recvSocketBuf[4096];
|
static pthread_mutex_t write2host_lock;
|
uint16_t chip_version = 0;
|
Wakeup_ADV_Info_St wakeup_ADV_Info = {0};
|
extern char btcp_log_en;
|
|
|
#define SKWBT_NV_FILE_PATH "/vendor/etc/bluetooth"
|
|
|
static const uint8_t hci_preamble_sizes[] =
|
{
|
0,
|
HCI_COMMAND_PKT_PREAMBLE_SIZE,
|
HCI_ACLDATA_PKT_PREAMBLE_SIZE,
|
HCI_SCODATA_PKT_PREAMBLE_SIZE,
|
HCI_EVENT_PKT_PREAMBLE_SIZE,
|
HCI_ISODATA_PKT_PREAMBLE_SIZE,
|
0,
|
HCI_EVENT_SKWLOG_PREAMBLE_SIZE
|
};
|
|
|
static tUSERIAL_CFG userial_H4_cfg =
|
{
|
(USERIAL_DATABITS_8 | USERIAL_PARITY_NONE | USERIAL_STOPBITS_1),
|
//USERIAL_BAUD_115200,
|
USERIAL_BAUD_3M,
|
//USERIAL_BAUD_1_5M,
|
USERIAL_HW_FLOW_CTRL_ON
|
};
|
|
|
char hex2char(int num)
|
{
|
char ch;
|
if(num >= 0 && num <= 9)
|
{
|
ch = num + 48;
|
}
|
else if(num > 9 && num <= 15)
|
{
|
ch = (num - 10) + 65;
|
}
|
else
|
{
|
ch = '\0';
|
}
|
return ch;
|
}
|
|
void hex2String(unsigned char hex[], unsigned char str[], int N)
|
{
|
int i = 0, j;
|
for(i = 0, j = 0; i < N; i++, j += 2)
|
{
|
str[j] = hex2char((hex[i] & 0xF0) >> 4);
|
str[j + 1] = hex2char(hex[i] & 0x0F);
|
}
|
str[N << 1] = 0;
|
}
|
|
unsigned char char2hex(char ch)
|
{
|
unsigned char num = 0;
|
if(ch >= '0' && ch <= '9')
|
{
|
num = ch - 48;//0:48
|
}
|
else if(ch >= 'a' && ch <= 'f')
|
{
|
num = ch + 10 - 97;//a:97
|
}
|
else if(ch >= 'A' && ch <= 'F')
|
{
|
num = ch + 10 - 65;//A:65
|
}
|
return num;
|
}
|
|
void scomm_vendor_init()
|
{
|
uint8_t i;
|
for(i = 0; i < BT_COM_PORT_SIZE; i++)
|
{
|
memset(&scomm_vnd[i], 0, sizeof(scomm_vnd_st));
|
scomm_vnd[i].fd = -1;
|
scomm_vnd[i].driver_state = FALSE;
|
scomm_vnd[i].mode = O_RDWR;
|
}
|
|
pthread_mutex_init(&write2host_lock, NULL);
|
}
|
|
void scomm_vendor_set_port_name(uint8_t port_index, char *port_name, int mode)
|
{
|
int len = strlen(port_name);
|
memset(scomm_vnd[port_index].port_name, 0, DEVICE_NODE_MAX_LEN);
|
if(len < DEVICE_NODE_MAX_LEN)
|
{
|
memcpy(scomm_vnd[port_index].port_name, port_name, len);
|
}
|
scomm_vnd[port_index].mode = mode;
|
|
ALOGD("skwbt_device_node:%s, port:%d", port_name, port_index);
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_tcio_baud
|
**
|
** Description helper function converts USERIAL baud rates into TCIO
|
** conforming baud rates
|
**
|
** Returns TRUE/FALSE
|
**
|
*******************************************************************************/
|
uint8_t scomm_vendor_tcio_baud(uint8_t cfg_baud, uint32_t *baud)
|
{
|
if (cfg_baud == USERIAL_BAUD_115200)
|
{
|
*baud = B115200;
|
}
|
else if (cfg_baud == USERIAL_BAUD_4M)
|
{
|
*baud = B4000000;
|
}
|
else if (cfg_baud == USERIAL_BAUD_3M)
|
{
|
*baud = B3000000;
|
}
|
else if (cfg_baud == USERIAL_BAUD_2M)
|
{
|
*baud = B2000000;
|
}
|
else if (cfg_baud == USERIAL_BAUD_1M)
|
{
|
*baud = B1000000;
|
}
|
else if (cfg_baud == USERIAL_BAUD_1_5M)
|
{
|
*baud = B1500000;
|
}
|
else if (cfg_baud == USERIAL_BAUD_921600)
|
{
|
*baud = B921600;
|
}
|
else if (cfg_baud == USERIAL_BAUD_460800)
|
{
|
*baud = B460800;
|
}
|
else if (cfg_baud == USERIAL_BAUD_230400)
|
{
|
*baud = B230400;
|
}
|
else if (cfg_baud == USERIAL_BAUD_57600)
|
{
|
*baud = B57600;
|
}
|
else if (cfg_baud == USERIAL_BAUD_19200)
|
{
|
*baud = B19200;
|
}
|
else if (cfg_baud == USERIAL_BAUD_9600)
|
{
|
*baud = B9600;
|
}
|
else if (cfg_baud == USERIAL_BAUD_1200)
|
{
|
*baud = B1200;
|
}
|
else if (cfg_baud == USERIAL_BAUD_600)
|
{
|
*baud = B600;
|
}
|
else
|
{
|
ALOGE( "userial vendor open: unsupported baud idx %i", cfg_baud);
|
*baud = B115200;
|
return FALSE;
|
}
|
|
return TRUE;
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_uart_open
|
**
|
** Description Uart Open
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
int scomm_vendor_uart_open(uint8_t port_index)
|
{
|
tUSERIAL_CFG *p_cfg = &userial_H4_cfg;
|
uint32_t baud;
|
uint8_t data_bits;
|
uint16_t parity;
|
uint8_t stop_bits;
|
|
scomm_vnd[port_index].fd = -1;
|
|
if (!scomm_vendor_tcio_baud(p_cfg->baud, &baud))
|
{
|
return -1;
|
}
|
|
if(p_cfg->fmt & USERIAL_DATABITS_8)
|
{
|
data_bits = CS8;
|
}
|
else if(p_cfg->fmt & USERIAL_DATABITS_7)
|
{
|
data_bits = CS7;
|
}
|
else if(p_cfg->fmt & USERIAL_DATABITS_6)
|
{
|
data_bits = CS6;
|
}
|
else if(p_cfg->fmt & USERIAL_DATABITS_5)
|
{
|
data_bits = CS5;
|
}
|
else
|
{
|
ALOGE("userial vendor open: unsupported data bits");
|
return -1;
|
}
|
|
if(p_cfg->fmt & USERIAL_PARITY_NONE)
|
{
|
parity = 0;
|
}
|
else if(p_cfg->fmt & USERIAL_PARITY_EVEN)
|
{
|
parity = PARENB;
|
}
|
else if(p_cfg->fmt & USERIAL_PARITY_ODD)
|
{
|
parity = (PARENB | PARODD);
|
}
|
else
|
{
|
ALOGE("userial vendor open: unsupported parity bit mode");
|
return -1;
|
}
|
|
if(p_cfg->fmt & USERIAL_STOPBITS_1)
|
{
|
stop_bits = 0;
|
}
|
else if(p_cfg->fmt & USERIAL_STOPBITS_2)
|
{
|
stop_bits = CSTOPB;
|
}
|
else
|
{
|
ALOGE("userial vendor open: unsupported stop bits");
|
return -1;
|
}
|
|
ALOGI("userial vendor open: opening %s,baud:%d", scomm_vnd[port_index].port_name, p_cfg->baud);
|
|
if ((scomm_vnd[port_index].fd = open(scomm_vnd[port_index].port_name, O_RDWR)) == -1)
|
{
|
ALOGE("userial vendor open: unable to open %s, %s", scomm_vnd[port_index].port_name, strerror(errno));
|
return -1;
|
}
|
|
tcflush(scomm_vnd[port_index].fd, TCIOFLUSH);
|
|
tcgetattr(scomm_vnd[port_index].fd, &scomm_vnd[port_index].termios);
|
cfmakeraw(&scomm_vnd[port_index].termios);
|
|
if(p_cfg->hw_fctrl == USERIAL_HW_FLOW_CTRL_ON)
|
{
|
ALOGI("userial vendor open: with HW flowctrl ON");
|
scomm_vnd[port_index].termios.c_cflag |= (CRTSCTS | stop_bits | parity);
|
}
|
else
|
{
|
ALOGI("userial vendor open: with HW flowctrl OFF");
|
scomm_vnd[port_index].termios.c_cflag &= ~CRTSCTS;
|
scomm_vnd[port_index].termios.c_cflag |= (stop_bits | parity);
|
|
}
|
|
tcsetattr(scomm_vnd[port_index].fd, TCSANOW, &scomm_vnd[port_index].termios);
|
tcflush(scomm_vnd[port_index].fd, TCIOFLUSH);
|
|
tcsetattr(scomm_vnd[port_index].fd, TCSANOW, &scomm_vnd[port_index].termios);
|
tcflush(scomm_vnd[port_index].fd, TCIOFLUSH);
|
tcflush(scomm_vnd[port_index].fd, TCIOFLUSH);
|
|
/* set input/output baudrate */
|
cfsetospeed(&scomm_vnd[port_index].termios, baud);
|
cfsetispeed(&scomm_vnd[port_index].termios, baud);
|
tcsetattr(scomm_vnd[port_index].fd, TCSANOW, &scomm_vnd[port_index].termios);
|
|
|
ALOGE("UART device fd = %d Open", scomm_vnd[port_index].fd);
|
|
|
return scomm_vnd[port_index].fd;
|
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_usbsdio_open
|
**
|
** Description check port name valid
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
uint8_t scomm_vendor_check_port_valid(uint8_t port_index)
|
{
|
if(scomm_vnd[port_index].port_name[0] == 0)
|
{
|
return FALSE;
|
}
|
return TRUE;
|
}
|
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_usbsdio_open
|
**
|
** Description USB/SDIO Open
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
int scomm_vendor_usbsdio_open(uint8_t port_index)
|
{
|
if ((scomm_vnd[port_index].fd = open(scomm_vnd[port_index].port_name, O_RDWR)) == -1)
|
{
|
ALOGE("%s: unable to open %s: %s", __func__, scomm_vnd[port_index].port_name, strerror(errno));
|
return -1;
|
}
|
ALOGD("USB/SDIO device[%d], %s fd = %d open", port_index, scomm_vnd[port_index].port_name, scomm_vnd[port_index].fd);
|
|
return scomm_vnd[port_index].fd;
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_recv_rawdata
|
**
|
** Description recv data from host and process
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
uint8_t pkt_cnts = 0;
|
static void scomm_vendor_recv_rawdata(void *context)
|
{
|
SKW_UNUSED(context);
|
uint8_t port_index = 0;//(uint8_t)context;
|
ssize_t bytes_read = 0;
|
uint8_t pkt_type = 0, offset = 1;
|
uint16_t need_read_lens = 0, total_len = 0;
|
int16_t rev_len = 0;
|
|
//SKWBT_LOG("%s [%d] start", __func__, port_index);
|
|
//do{
|
|
//-----------type-------------//
|
//do
|
{
|
rev_len = read(scomm_vnd[port_index].socket_fd[1], recvSocketBuf, 1);
|
if(rev_len <= 0)
|
{
|
ALOGE("%s type read err, rev_len:%d", __func__, rev_len);
|
return ;
|
}
|
bytes_read += rev_len;
|
}
|
//while (bytes_read < 1);
|
|
total_len = 1;
|
pkt_type = recvSocketBuf[0];
|
if((pkt_type == HCI_COMMAND_PKT) || (pkt_type == HCI_ACLDATA_PKT) || (pkt_type == HCI_SCODATA_PKT) || (pkt_type == HCI_ISO_PKT))
|
{
|
need_read_lens = hci_preamble_sizes[pkt_type];
|
}
|
else
|
{
|
ALOGE("%s invalid data type: %d", __func__, pkt_type);
|
assert(0);
|
}
|
offset += need_read_lens;
|
|
//SKWBT_LOG("pkt_type:%d, bytes_read:%zd, offset:%d", pkt_type, bytes_read, offset);
|
|
//-----------header-------------//
|
bytes_read = 0;
|
offset = 1;
|
do
|
{
|
rev_len = read(scomm_vnd[port_index].socket_fd[1], recvSocketBuf + offset + bytes_read, need_read_lens);
|
if(rev_len < 0)
|
{
|
ALOGE("%s header read err, rev_len:%d", __func__, rev_len);
|
return ;
|
}
|
bytes_read += rev_len;
|
//SKWBT_LOG("header bytes_read:%zd, need_read_lens:%d", bytes_read, need_read_lens);
|
} while (bytes_read < need_read_lens);
|
offset += need_read_lens;
|
|
total_len += need_read_lens;
|
//get payload length
|
if((pkt_type == HCI_ACLDATA_PKT) || (pkt_type == HCI_ISO_PKT))
|
{
|
need_read_lens = *(uint16_t *)&recvSocketBuf[HCI_COMMON_DATA_LENGTH_INDEX];
|
}
|
else if((pkt_type == HCI_EVENT_PKT) || (pkt_type == HCI_EVENT_SKWLOG))
|
{
|
need_read_lens = recvSocketBuf[HCI_EVENT_DATA_LENGTH_INDEX];
|
}
|
else//cmd/sco
|
{
|
need_read_lens = recvSocketBuf[HCI_COMMON_DATA_LENGTH_INDEX];
|
}
|
//-----------payload-------------//
|
bytes_read = 0;
|
if(need_read_lens > 0)
|
{
|
do
|
{
|
rev_len = read(scomm_vnd[port_index].socket_fd[1], recvSocketBuf + offset + bytes_read, need_read_lens);
|
if(rev_len < 0)
|
{
|
ALOGE("%s header read err, rev_len:%d", __func__, rev_len);
|
return ;
|
}
|
bytes_read += rev_len;
|
//SKWBT_LOG("payload bytes_read:%zd, need_read_lens:%d", bytes_read, need_read_lens);
|
} while (bytes_read < need_read_lens);
|
total_len += need_read_lens;
|
}
|
|
|
uint8_t str_buffer[200] = {0};
|
hex2String(recvSocketBuf, str_buffer, (total_len > 64) ? 64 : total_len);
|
|
if((skwbt_transtype & SKWBT_TRANS_TYPE_UART) && (skwbtuartonly == FALSE) && (skwbtNoSleep == FALSE) && (btpw_fp > 0))//uart
|
{
|
char tmp_buf[6] = {0};
|
int r_len;
|
|
tmp_buf[0] = pkt_cnts ++;
|
r_len = write(btpw_fp, tmp_buf, 1);
|
SKWBT_LOG("r_len:%d, btpw_fp:%d, pkt_cnts:%d", r_len, btpw_fp, pkt_cnts);
|
}
|
|
uint16_t length = total_len;
|
uint16_t transmitted_length = 0;
|
uint8_t send_port = BT_COM_PORT_CMDEVT;
|
if(skwbt_transtype & SKWBT_TRANS_TYPE_SDIO)
|
{
|
switch(recvSocketBuf[0])
|
{
|
case HCI_ACLDATA_PKT:
|
send_port = BT_COM_PORT_ACL;
|
break;
|
case HCI_SCODATA_PKT:
|
send_port = BT_COM_PORT_AUDIO;
|
break;
|
case HCI_ISO_PKT:
|
send_port = BT_COM_PORT_ISO;
|
break;
|
default:
|
send_port = BT_COM_PORT_CMDEVT;
|
break;
|
}
|
}
|
|
|
SKWBT_LOG("total_len:%d, port:%d, %s", total_len, send_port, str_buffer);
|
|
skw_btsnoop_capture(recvSocketBuf, FALSE);
|
|
|
|
while((length > 0) && scomm_vnd[send_port].driver_state)
|
{
|
ssize_t ret = write(scomm_vnd[send_port].fd, recvSocketBuf + transmitted_length, length);
|
|
switch (ret)
|
{
|
case -1:
|
ALOGE("In %s, error writing to the scomm: %s", __func__, strerror(errno));
|
return ;
|
//break;
|
case 0:
|
ALOGE("%s, ret %zd", __func__, ret);
|
break;
|
default:
|
transmitted_length += ret;
|
length -= ret;
|
//break;
|
}
|
}
|
|
//SKWBT_LOG("%s [%d] end", __func__, port_index);
|
}
|
|
static void *scomm_vendor_recv_socket_thread(void *arg)
|
{
|
//SKW_UNUSED(arg);
|
int port_index = (int)((long)arg);//for error: cast from pointer to integer of different size
|
struct epoll_event events[32];
|
scomm_vnd_st *scomm = &scomm_vnd[0];
|
int j, ret;
|
|
ALOGD("%s [%d] start", __func__, port_index);
|
|
while(scomm->thread_running)
|
{
|
do
|
{
|
ret = epoll_wait(scomm->epoll_fd, events, 32, 500);
|
|
//ALOGE("recv_socket_thread ret:%d, state:%d", ret, scomm->thread_running);
|
|
} while(scomm->thread_running && (ret == -1) && (errno == EINTR));
|
|
if (ret < 0)
|
{
|
ALOGE("%s error in epoll_wait:%d, %s", __func__, ret, strerror(errno));
|
break;
|
}
|
for (j = 0; (j < ret) && (scomm->thread_running); ++j)
|
{
|
skw_socket_object_st *object = (skw_socket_object_st *)events[j].data.ptr;
|
if (object == NULL)
|
{
|
continue;
|
}
|
else
|
{
|
if (events[j].events & (EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR) && object->read_ready)
|
{
|
object->read_ready(object->context);
|
//object->read_ready(port_index);
|
}
|
}
|
}
|
}
|
//scomm_vnd[port_index].thread_socket_id = -1;
|
|
ALOGD("%s [%d] exit", __func__, port_index);
|
return NULL;
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_send_to_host
|
**
|
** Description send data to host
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
static void scomm_vendor_send_to_host(uint8_t port_index, unsigned char *buffer, unsigned int total_length)
|
{
|
unsigned int length = total_length;
|
uint16_t transmitted_length = 0;
|
ssize_t ret;
|
|
|
pthread_mutex_lock(&write2host_lock);
|
while ((length > 0) && scomm_vnd[port_index].thread_running)
|
{
|
RW_NO_INTR(ret = write(scomm_vnd[port_index].socket_fd[1], buffer + transmitted_length, length));
|
|
//SKWBT_LOG("write to host ret:%zd", ret);
|
switch (ret)
|
{
|
case -1:
|
ALOGE("In %s, error writing to socket: %s", __func__, strerror(errno));
|
break;
|
case 0:
|
break;
|
default:
|
transmitted_length += ret;
|
length -= ret;
|
break;
|
}
|
}
|
pthread_mutex_unlock(&write2host_lock);
|
|
SKWBT_LOG("write to host[%d] total_length:%d, ret:%zd", port_index, total_length, ret);
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_send_hw_error
|
**
|
** Description send HCI_HW_ERR command to hsot
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
|
void scomm_vendor_send_hw_error()
|
{
|
unsigned char p_buf[10];
|
|
ALOGE("%s, CP Error", __func__);
|
|
p_buf[0] = HCI_EVENT_PKT;//event
|
p_buf[1] = HCI_HARDWARE_ERROR_EVENT;//hardware error
|
p_buf[2] = 0x01;//len
|
p_buf[3] = HWERR_CODE_CP_ERROR;//userial error code
|
scomm_vendor_send_to_host(0, p_buf, 4);
|
}
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_find_valid_type
|
**
|
** Description find the index of valid packet type
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
int scomm_vendor_find_valid_type(uint8_t *buffer, uint16_t len)
|
{
|
int i;
|
for(i = 0; i < len; i++)
|
{
|
switch(buffer[i])
|
{
|
case HCI_EVENT_PKT:
|
case HCI_ACLDATA_PKT:
|
case HCI_SCODATA_PKT:
|
//case HCI_COMMAND_PKT:
|
case HCI_EVENT_SKWLOG:
|
return i;
|
default:
|
break;
|
}
|
#if 0
|
if((HCI_EVENT_PKT == buffer[i]) || (HCI_ACLDATA_PKT == buffer[i]) || (HCI_SCODATA_PKT == buffer[i]) || (HCI_COMMAND_PKT == buffer[i])
|
|| (HCI_EVENT_SKWLOG == buffer[i]))
|
{
|
return i;
|
}
|
#endif
|
}
|
return len;
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_recv_scomm_thread
|
**
|
** Description recv data from UART/USB/SDIO and process
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
static void *scomm_vendor_recv_scomm_thread(void *arg)
|
{
|
//SKW_UNUSED(arg);
|
int port_index = (int)((long)arg);
|
struct pollfd pfd[2];
|
scomm_vnd_st *scomm = &scomm_vnd[port_index];
|
pfd[0].events = POLLIN | POLLHUP | POLLERR | POLLRDHUP;
|
pfd[0].fd = scomm->signal_fd[1];
|
pfd[1].events = POLLIN | POLLHUP | POLLERR | POLLRDHUP;
|
pfd[1].fd = scomm->fd;
|
|
uint8_t read_buffer[SCOM_READ_BUFFER_LEN + 1] = {0};
|
uint8_t str_buffer[SCOM_READ_BUFFER_LEN + 1] = {0};
|
ssize_t bytes_read;
|
uint16_t last_len = 0, rev_len = 0;
|
int ret, is_uart = (skwbt_transtype & SKWBT_TRANS_TYPE_UART);
|
|
scomm->recv_comm_thread_running = TRUE;
|
scomm->read_retry = 0;
|
ALOGD("%s [%d] start, is_uart:%d", __func__, port_index, is_uart);
|
|
while(scomm->thread_running)
|
{
|
if(is_uart)
|
{
|
do
|
{
|
ret = poll(pfd, 2, 500);
|
} while(ret == -1 && errno == EINTR && scomm->thread_running);
|
//exit signal is always at first index
|
if(pfd[0].revents && !scomm->thread_running)
|
{
|
ALOGE("receive exit signal and stop thread ");
|
break;
|
}
|
if (pfd[1].revents & (POLLERR | POLLHUP))
|
{
|
ALOGE("%s poll error, fd : %d", __func__, scomm->fd);
|
scomm->driver_state = FALSE;
|
close(scomm->fd);
|
break;
|
}
|
if(!(pfd[1].revents & POLLIN))
|
{
|
continue;
|
}
|
}
|
|
|
scomm->is_busying = FALSE;
|
bytes_read = read(scomm->fd, read_buffer + last_len, SCOM_READ_BUFFER_LEN - last_len);
|
scomm->is_busying = TRUE;
|
|
if(bytes_read == 0)
|
{
|
if(scomm->thread_running)
|
{
|
continue;
|
}
|
break;
|
}
|
if(bytes_read < 0)
|
{
|
ALOGE("%s, read fail, thread[%d] state:%d, error code:%zd, %s, read_retry:%d", __func__, port_index, (scomm->thread_running), bytes_read, strerror(errno), scomm->read_retry);
|
if((scomm->thread_running) && (0 == port_index))
|
{
|
if(scomm->read_retry == 0)
|
{
|
scomm->read_retry = 1;
|
usleep(200000);
|
continue;
|
}
|
scomm_vendor_send_hw_error();
|
}
|
break;
|
}
|
if(read_buffer[0] != HCI_EVENT_SKWLOG)
|
{
|
hex2String(read_buffer + last_len, str_buffer, (bytes_read > 64) ? 64 : bytes_read);
|
SKWBT_LOG("scomm[%d] read:%zd, last_len:%d, %s", port_index, bytes_read, last_len, str_buffer);
|
}
|
|
//data parse for get a commplete packet and capture the snoop log
|
rev_len = bytes_read + last_len;
|
do
|
{
|
uint8_t pkt_type = read_buffer[0];
|
if((pkt_type == HCI_EVENT_PKT) || (pkt_type == HCI_ACLDATA_PKT) || (pkt_type == HCI_SCODATA_PKT) || (pkt_type == HCI_EVENT_SKWLOG) || (pkt_type == HCI_ISO_PKT))
|
{
|
uint16_t hdr_lens = hci_preamble_sizes[pkt_type] + 1;
|
if(rev_len >= hdr_lens)
|
{
|
uint16_t pkt_len = 0;
|
//get payload length
|
if((pkt_type == HCI_ACLDATA_PKT) || (pkt_type == HCI_ISO_PKT))
|
{
|
pkt_len = *(uint16_t *)&read_buffer[HCI_COMMON_DATA_LENGTH_INDEX];
|
}
|
else if(pkt_type == HCI_EVENT_PKT)
|
{
|
pkt_len = read_buffer[HCI_EVENT_DATA_LENGTH_INDEX];
|
}
|
else if(pkt_type == HCI_EVENT_SKWLOG)
|
{
|
pkt_len = *(uint16_t *)&read_buffer[HCI_SKWLOG_DATA_LENGTH_INDEX];
|
}
|
else//cmd/sco
|
{
|
pkt_len = read_buffer[HCI_COMMON_DATA_LENGTH_INDEX];
|
}
|
|
SKWBT_LOG("rev_len:%d, pkt_type:%d, hdr_lens:%d, pkt_len:%d", rev_len, pkt_type, hdr_lens, pkt_len);
|
|
pkt_len += hdr_lens;
|
if(rev_len >= pkt_len)
|
{
|
if(pkt_type == HCI_EVENT_SKWLOG)
|
{
|
skwlog_write(read_buffer, pkt_len);
|
}
|
else//
|
{
|
|
skw_btsnoop_capture(read_buffer, TRUE);
|
scomm_vendor_send_to_host(0, read_buffer, pkt_len);
|
}
|
last_len = rev_len - pkt_len;
|
if((pkt_len + last_len) <= SCOM_READ_BUFFER_LEN)
|
{
|
memcpy(read_buffer, read_buffer + pkt_len, last_len);
|
}
|
else
|
{
|
ALOGE("%s, err packet, pkt_len:%d, last_len:%d", __func__, pkt_len, last_len);
|
}
|
if(last_len >= 4)
|
{
|
rev_len = last_len;
|
SKWBT_LOG(" more packet, rev_len:%d ", rev_len);
|
continue;
|
}
|
|
}
|
else
|
{
|
last_len = rev_len;
|
SKWBT_LOG("need more, rev_len:%d, pkt_len:%d, last_len:%d", rev_len, pkt_len, last_len);
|
}
|
}
|
else
|
{
|
last_len = rev_len;
|
}
|
}
|
else//invalid data, discard
|
{
|
int vLen = scomm_vendor_find_valid_type(read_buffer, rev_len);
|
ALOGE("invalid type:%02X, vLen:%d", pkt_type, vLen);
|
|
last_len = rev_len - vLen;
|
if(vLen < rev_len)
|
{
|
memcpy(read_buffer, read_buffer + vLen, last_len);
|
}
|
}
|
|
break;
|
} while (1);
|
}
|
|
|
scomm->is_busying = FALSE;
|
scomm->thread_uart_id = -1;
|
|
ALOGD("%s [%d] exit", __func__, port_index);
|
return NULL;
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_socket_open
|
**
|
** Description USB/SDIO Open
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
int scomm_vendor_socket_open(uint8_t port_index)
|
{
|
int ret = 0;
|
struct epoll_event event;
|
if((ret = socketpair(AF_UNIX, SOCK_STREAM, 0, scomm_vnd[port_index].socket_fd)) < 0)
|
{
|
ALOGE("%s, errno : %s", __func__, strerror(errno));
|
return ret;
|
}
|
|
if((ret = socketpair(AF_UNIX, SOCK_STREAM, 0, scomm_vnd[port_index].signal_fd)) < 0)
|
{
|
ALOGE("%s, errno : %s", __func__, strerror(errno));
|
return ret;
|
}
|
|
scomm_vnd[port_index].epoll_fd = epoll_create(4);
|
if (scomm_vnd[port_index].epoll_fd == -1)
|
{
|
ALOGE("%s unable to create epoll instance: %s", __func__, strerror(errno));
|
return -1;
|
}
|
|
scomm_vnd[port_index].thread_running = TRUE;
|
scomm_vnd[port_index].recv_comm_thread_running = FALSE;
|
scomm_vnd[port_index].thread_socket_id = -1;
|
|
pthread_attr_t thread_attr;
|
pthread_attr_init(&thread_attr);
|
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
|
|
memset(&event, 0, sizeof(event));
|
event.events = EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR;
|
event.data.ptr = (void *)&skw_socket_object;
|
if (epoll_ctl(scomm_vnd[port_index].epoll_fd, EPOLL_CTL_ADD, scomm_vnd[port_index].socket_fd[1], &event) == -1)
|
{
|
ALOGE("%s unable to register fd %d to epoll set: %s", __func__, scomm_vnd[port_index].socket_fd[1], strerror(errno));
|
close(scomm_vnd[port_index].epoll_fd);
|
scomm_vnd[port_index].epoll_fd = -1;
|
scomm_vnd[port_index].thread_running = FALSE;
|
return -1;
|
}
|
|
event.data.ptr = NULL;
|
if (epoll_ctl(scomm_vnd[port_index].epoll_fd, EPOLL_CTL_ADD, scomm_vnd[port_index].signal_fd[0], &event) == -1)
|
{
|
ALOGE("%s unable to register signal0 fd %d to epoll set: %s", __func__, scomm_vnd[port_index].signal_fd[0], strerror(errno));
|
close(scomm_vnd[port_index].epoll_fd);
|
scomm_vnd[port_index].epoll_fd = -1;
|
scomm_vnd[port_index].thread_running = FALSE;
|
return -1;
|
}
|
|
|
if(port_index == 0)
|
{
|
skw_socket_object.fd = scomm_vnd[port_index].socket_fd[1];
|
skw_socket_object.read_ready = scomm_vendor_recv_rawdata;
|
skw_socket_object.write_ready = NULL;
|
|
if (pthread_create(&scomm_vnd[port_index].thread_socket_id, &thread_attr, scomm_vendor_recv_socket_thread, (void *)(long)port_index) != 0 )
|
{
|
ALOGE("pthread_create : %s", strerror(errno));
|
close(scomm_vnd[port_index].epoll_fd);
|
scomm_vnd[port_index].epoll_fd = -1;
|
scomm_vnd[port_index].thread_socket_id = -1;
|
scomm_vnd[port_index].thread_running = FALSE;
|
return -1;
|
}
|
}
|
|
|
if (pthread_create(&scomm_vnd[port_index].thread_uart_id, &thread_attr, scomm_vendor_recv_scomm_thread, (void *)(long)port_index) != 0 )
|
{
|
ALOGE("pthread_create : %s", strerror(errno));
|
close(scomm_vnd[port_index].epoll_fd);
|
scomm_vnd[port_index].thread_running = FALSE;
|
pthread_join(scomm_vnd[port_index].thread_socket_id, NULL);
|
scomm_vnd[port_index].thread_socket_id = -1;
|
return -1;
|
}
|
while(!scomm_vnd[port_index].recv_comm_thread_running)
|
{
|
usleep(20);
|
}
|
|
scomm_vnd[port_index].driver_state = TRUE;
|
|
ret = scomm_vnd[port_index].socket_fd[0];
|
|
ALOGD("%s socket_fd:%d", __func__, ret);
|
return ret;
|
}
|
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_socket_close
|
**
|
** Description socket close
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
static void scomm_vendor_socket_close(uint8_t port_index)
|
{
|
int result;
|
|
ALOGE( "%s [%d], thread_socket_id:0x%X", __func__, port_index, (int)scomm_vnd[port_index].thread_socket_id);
|
|
if ((scomm_vnd[port_index].socket_fd[0] > 0) && (result = close(scomm_vnd[port_index].socket_fd[0])) < 0)
|
{
|
ALOGE( "%s (fd:%d) FAILED result:%d", __func__, scomm_vnd[port_index].socket_fd[0], result);
|
}
|
|
if (epoll_ctl(scomm_vnd[port_index].epoll_fd, EPOLL_CTL_DEL, scomm_vnd[port_index].socket_fd[1], NULL) == -1)
|
{
|
ALOGE("%s unable to unregister fd %d from epoll set: %s", __func__, scomm_vnd[port_index].socket_fd[1], strerror(errno));
|
}
|
if (epoll_ctl(scomm_vnd[port_index].epoll_fd, EPOLL_CTL_DEL, scomm_vnd[port_index].signal_fd[0], NULL) == -1)
|
{
|
ALOGE("%s unable to unregister signal0 fd %d from epoll set: %s", __func__, scomm_vnd[port_index].signal_fd[0], strerror(errno));
|
}
|
|
if(scomm_vnd[port_index].epoll_fd > 0)
|
{
|
close(scomm_vnd[port_index].epoll_fd);
|
}
|
|
if(scomm_vnd[port_index].thread_socket_id != -1)
|
{
|
if ((result = pthread_join(scomm_vnd[port_index].thread_socket_id, NULL)) < 0)
|
{
|
ALOGE( "data thread pthread_join() scomm_vnd[port_index].thread_socket_id failed result:%d", result);
|
}
|
else
|
{
|
scomm_vnd[port_index].thread_socket_id = -1;
|
ALOGE( "data thread pthread_join() scomm_vnd[port_index].thread_socket_id pthread_join_success result:%d", result);
|
}
|
}
|
|
if ((scomm_vnd[port_index].socket_fd[1] > 0) && (result = close(scomm_vnd[port_index].socket_fd[1])) < 0)
|
{
|
ALOGE( "%s (fd:%d) FAILED result:%d", __func__, scomm_vnd[port_index].socket_fd[1], result);
|
}
|
|
if ((scomm_vnd[port_index].signal_fd[0] > 0) && (result = close(scomm_vnd[port_index].signal_fd[0])) < 0)
|
{
|
ALOGE( "%s (signal fd[0]:%d) FAILED result:%d", __func__, scomm_vnd[port_index].signal_fd[0], result);
|
}
|
if ((scomm_vnd[port_index].signal_fd[1] > 0) && (result = close(scomm_vnd[port_index].signal_fd[1])) < 0)
|
{
|
ALOGE( "%s (signal fd[1]:%d) FAILED result:%d", __func__, scomm_vnd[port_index].signal_fd[1], result);
|
}
|
|
scomm_vnd[port_index].epoll_fd = -1;
|
scomm_vnd[port_index].socket_fd[0] = -1;
|
scomm_vnd[port_index].socket_fd[1] = -1;
|
scomm_vnd[port_index].signal_fd[0] = -1;
|
scomm_vnd[port_index].signal_fd[1] = -1;
|
ALOGE( "%s [%d] end", __func__, port_index);
|
}
|
|
|
void scomm_vendor_write_bt_state()
|
{
|
//if(skwbt_transtype & SKWBT_TRANS_TYPE_USB)
|
if(chip_version == SKW_CHIPID_6160)
|
{
|
char buffer[10] = {0x01, 0x80, 0xFE, 0x01, 0x00};
|
scomm_vnd_st *scomm = &scomm_vnd[0];
|
write(scomm->fd, buffer, 5);
|
usleep(15000);
|
}
|
}
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_port_close
|
**
|
** Description Conduct vendor-specific close works
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
void scomm_vendor_port_close(uint8_t port_index)
|
{
|
//send close signal
|
unsigned char close_signal = 1;
|
ssize_t ret;
|
int res, i;
|
scomm_vnd_st *scomm = &scomm_vnd[port_index];
|
ALOGD( "%s [%d] start, fd:%d, busy:%d", __func__, port_index, scomm->fd, scomm->is_busying);
|
|
if(scomm->fd == -1)
|
{
|
return;
|
}
|
|
scomm->thread_running = FALSE;
|
scomm->driver_state = FALSE;
|
|
while(scomm->is_busying)
|
{
|
usleep(20);
|
}
|
|
res = ioctl(scomm->fd, 0);
|
//ALOGE("res:%d, %s", res, strerror(errno));
|
|
ALOGE("%s signal_fd:%d", __func__, scomm->signal_fd[1]);
|
RW_NO_INTR(ret = write(scomm->signal_fd[1], &close_signal, 1));
|
|
usleep(300);//wait
|
for(i = 0; (skwbt_transtype & SKWBT_TRANS_TYPE_SDIO) && (i < 2) && (scomm->thread_uart_id != -1); i++)
|
{
|
res = ioctl(scomm->fd, 0);//try again
|
usleep(200);
|
ALOGD("%s,%d times:%d, res:%d, %s", __func__, port_index, i, res, strerror(errno));
|
}
|
|
//scomm close
|
if ((scomm->fd > 0) && (res = close(scomm->fd)) < 0)
|
{
|
ALOGE( "%s (fd:%d) FAILED result:%d", __func__, scomm->fd, res);
|
}
|
|
ALOGD("Run Here");
|
|
if(scomm->thread_uart_id != -1)
|
{
|
pthread_join(scomm->thread_uart_id, NULL);
|
scomm->thread_uart_id = -1;
|
}
|
|
scomm_vendor_socket_close(port_index);
|
|
//close(scomm_vnd[port_index].fd);
|
scomm->fd = -1;
|
|
|
ALOGD( "%s [%d] finish", __func__, port_index);
|
|
}
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_close
|
**
|
** Description Conduct vendor-specific close works
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
void scomm_vendor_close()
|
{
|
int idx = 0;
|
for(idx = 0; idx < BT_COM_PORT_SIZE; idx++)
|
{
|
scomm_vendor_port_close(idx);
|
}
|
if(btpw_fp > 0)
|
{
|
close(btpw_fp);
|
}
|
}
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_init_err
|
**
|
** Description init err
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
void scomm_vendor_init_err(HC_BT_HDR *p_buf)
|
{
|
hw_cfg_cb.state = HW_CFG_INIT;
|
bt_vendor_cbacks->dealloc(p_buf);
|
fclose(hw_cfg_cb.nv_fp);
|
hw_cfg_cb.nv_fp = NULL;
|
|
}
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_config_callback
|
**
|
** Description Callback function for controller configuration
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
void scomm_vendor_config_callback(void *p_mem)
|
{
|
uint8_t status = 0;
|
uint16_t opcode = 0;
|
HC_BT_HDR *p_buf = NULL;
|
HC_BT_HDR *p_evt_buf = NULL;
|
|
|
if(p_mem != NULL)
|
{
|
p_evt_buf = (HC_BT_HDR *) p_mem;
|
status = *((uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_STATUS_OFFSET);
|
uint8_t *p = (uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE_OFFSET;
|
STREAM_TO_UINT16(opcode, p);
|
}
|
|
|
ALOGD("%s status:%d ,opcode:%04X", __func__, status, opcode);
|
if((status == 0) && bt_vendor_cbacks)
|
{
|
p_buf = (HC_BT_HDR *)bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + HCI_CMD_MAX_LEN);
|
}
|
|
if(p_buf)
|
{
|
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
|
p_buf->offset = 0;
|
p_buf->len = 0;
|
p_buf->layer_specific = 0;
|
|
ALOGD("hw_cfg_cb.state = %d", hw_cfg_cb.state);
|
|
switch (hw_cfg_cb.state)
|
{
|
case HW_CFG_START:
|
{
|
uint8_t *ptr = (uint8_t *) (p_buf + 1);
|
UINT16_TO_STREAM(ptr, HCI_READ_LOCAL_VERSION_INFO);
|
UINT8_TO_STREAM(ptr, 0);
|
|
p_buf->len = 3;//packet len
|
bt_vendor_cbacks->xmit_cb(HCI_READ_LOCAL_VERSION_INFO, p_buf, scomm_vendor_config_callback);
|
hw_cfg_cb.state = HW_CFG_READ_HCI_VERSION;
|
break;
|
}
|
case HW_CFG_READ_HCI_VERSION:
|
{
|
char file_name[128] = {0};
|
uint8_t skip_header = 0;
|
uint8_t *p = (uint8_t *)(p_evt_buf + 1) + 7;
|
if(p == NULL)
|
{
|
ALOGE("%s invalid ptr", __func__);
|
bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_FAIL);
|
hw_cfg_cb.state = HW_CFG_INIT;
|
return ;
|
}
|
STREAM_TO_UINT16(chip_version, p);
|
|
ALOGD("chip_version:0x%04X", chip_version);
|
|
switch(chip_version)
|
{
|
case SKW_CHIPID_6316://0x6316
|
{
|
skip_header = 1;
|
sprintf(file_name, "%s/sv6316.nvbin", SKWBT_NV_FILE_PATH);
|
break;
|
}
|
case SKW_CHIPID_6160_LITE:
|
{
|
skip_header = 1;
|
sprintf(file_name, "%s/sv6160lite.nvbin", SKWBT_NV_FILE_PATH);
|
break;
|
}
|
default:
|
{
|
sprintf(file_name, "%s/sv6160.nvbin", SKWBT_NV_FILE_PATH);
|
chip_version = SKW_CHIPID_6160;
|
break;
|
}
|
}
|
|
hw_cfg_cb.nv_fp = fopen(file_name, "rb");
|
if(!hw_cfg_cb.nv_fp)
|
{
|
ALOGE("%s unable to open nv file:%s: %s", __func__, file_name, strerror(errno));
|
bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_FAIL);
|
hw_cfg_cb.state = HW_CFG_INIT;
|
return;
|
}
|
hw_cfg_cb.file_offset = 0;
|
hw_cfg_cb.state = HW_CFG_NV_SEND;
|
if(skip_header)//skip header
|
{
|
char buffer[6];
|
fread(buffer, 1, 4, hw_cfg_cb.nv_fp);
|
}
|
}
|
case HW_CFG_NV_SEND:
|
{
|
uint8_t len = 0, res = 0;
|
uint8_t *ptr = (uint8_t *) (p_buf + 1);//skip header
|
UINT16_TO_STREAM(ptr, HCI_CMD_SKW_BT_NVDS);
|
|
if((chip_version == SKW_CHIPID_6316) || (chip_version == SKW_CHIPID_6160_LITE))//0x6316
|
{
|
uint8_t tmp_buffer[10] = {0};
|
uint8_t *param_buf = ptr + 3;
|
uint8_t nv_tag = 0;
|
int nv_param_len = 0;
|
int total_len = 0;
|
char file_end = 0;
|
int file_ptr;
|
while(1)
|
{
|
file_ptr = ftell(hw_cfg_cb.nv_fp);
|
if(file_ptr < 0)
|
{
|
ALOGE("%s invalid file_ptr:%d", __func__, file_ptr);
|
bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_FAIL);
|
hw_cfg_cb.state = HW_CFG_INIT;
|
return ;
|
}
|
len = fread(tmp_buffer, 1, 3, hw_cfg_cb.nv_fp);
|
if((len < 3) || feof(hw_cfg_cb.nv_fp))
|
{
|
file_end = 1;
|
break;
|
}
|
memcpy(param_buf + total_len, tmp_buffer, 3);
|
nv_param_len = tmp_buffer[2];
|
nv_tag = tmp_buffer[0];
|
ALOGD("tag:%d, nv_param_len:%d, file_ptr:%d", tmp_buffer[0], nv_param_len, file_ptr);
|
|
if((nv_param_len + total_len + 3) > 252)//252 + 3
|
{
|
fseek(hw_cfg_cb.nv_fp, file_ptr, SEEK_SET);
|
break;
|
}
|
total_len += 3;
|
if((nv_param_len > 0) && ((total_len + nv_param_len) < HCI_CMD_MAX_LEN))
|
{
|
len = fread(param_buf + total_len, 1, nv_param_len, hw_cfg_cb.nv_fp);
|
if(len < nv_param_len)
|
{
|
ALOGE("%s, len:%d, nv_param_len:%d", __func__, len, nv_param_len);
|
scomm_vendor_init_err(p_buf);
|
break;
|
}
|
if(nv_tag == NV_TAG_BD_ADDR)
|
{
|
skw_addr_get(param_buf + total_len + 3);
|
}
|
else if(nv_tag == NV_TAG_DSP_LOG_SETTING)
|
{
|
*(param_buf + total_len) = btcp_log_en ? 0 : 1;
|
}
|
total_len += len;
|
}
|
}
|
if(total_len > 0)
|
{
|
ptr[0] = (total_len + 2);//payload len
|
ptr[1] = hw_cfg_cb.file_offset;
|
ptr[2] = total_len;//para len
|
p_buf->len = total_len + 2 + 3;//packet len
|
res = bt_vendor_cbacks->xmit_cb(HCI_CMD_SKW_BT_NVDS, p_buf, scomm_vendor_config_callback);
|
hw_cfg_cb.file_offset ++;
|
if(res == FALSE)//send error
|
{
|
scomm_vendor_init_err(p_buf);
|
break;
|
}
|
}
|
if(file_end)
|
{
|
hw_cfg_cb.state = HW_CFG_WRITE_OS_TYPE;
|
fclose(hw_cfg_cb.nv_fp);
|
hw_cfg_cb.nv_fp = NULL;
|
}
|
}
|
else
|
{
|
len = fread(ptr + 3, 1, NV_FILE_RD_BLOCK_SIZE, hw_cfg_cb.nv_fp);
|
ptr[0] = (len + 2);//payload len
|
ptr[1] = hw_cfg_cb.file_offset;
|
ptr[2] = len;//para len
|
p_buf->len = len + 2 + 3;//packet len
|
if(0 == hw_cfg_cb.file_offset)
|
{
|
//*(ptr + 3 + 3) = 'A';
|
//byte7
|
skw_addr_get(ptr + 3 + 7);
|
}
|
else if(1 == hw_cfg_cb.file_offset)
|
{
|
*(ptr + 3 + 62) |= 0x80;
|
*(ptr + 3 + 53) = btcp_log_en ? 0 : 1;
|
}
|
|
res = bt_vendor_cbacks->xmit_cb(HCI_CMD_SKW_BT_NVDS, p_buf, scomm_vendor_config_callback);
|
|
ALOGD("len:%d, file_offset:%d, plen:%d,%d res:%d", len, hw_cfg_cb.file_offset, p_buf->len, ptr[0], res);
|
|
hw_cfg_cb.file_offset ++;
|
|
if((len < NV_FILE_RD_BLOCK_SIZE) || (len == 0) || feof(hw_cfg_cb.nv_fp))//end of file
|
{
|
hw_cfg_cb.state = HW_CFG_WRITE_BD_ADDR;
|
fclose(hw_cfg_cb.nv_fp);
|
hw_cfg_cb.nv_fp = NULL;
|
}
|
if(res == FALSE)//send error
|
{
|
scomm_vendor_init_err(p_buf);
|
}
|
|
}
|
break;
|
}
|
case HW_CFG_WRITE_OS_TYPE:
|
{
|
if(chip_version != SKW_CHIPID_6160)
|
{
|
uint8_t *ptr = (uint8_t *) (p_buf + 1);
|
UINT16_TO_STREAM(ptr, HCI_CMD_WRITE_OS_TYPE);
|
UINT8_TO_STREAM(ptr, 1);
|
UINT8_TO_STREAM(ptr, 1);
|
|
p_buf->len = 3 + 1;//packet len
|
bt_vendor_cbacks->xmit_cb(HCI_CMD_WRITE_OS_TYPE, p_buf, scomm_vendor_config_callback);
|
hw_cfg_cb.state = HW_CFG_WRITE_BD_ADDR;
|
break;
|
}
|
}
|
case HW_CFG_WRITE_BD_ADDR:
|
{
|
uint8_t *ptr = (uint8_t *) (p_buf + 1);
|
UINT16_TO_STREAM(ptr, HCI_CMD_WRITE_BD_ADDR);
|
UINT8_TO_STREAM(ptr, 6);
|
|
p_buf->len = 3 + 6;//packet len
|
if(skw_addr_from_ap(ptr))
|
{
|
bt_vendor_cbacks->xmit_cb(HCI_CMD_WRITE_BD_ADDR, p_buf, scomm_vendor_config_callback);
|
#if BLE_ADV_WAKEUP_ENABLE
|
hw_cfg_cb.state = HW_CFG_WRITE_WAKEUP_ADV_DATA;
|
#else
|
hw_cfg_cb.state = HW_CFG_NV_SEND_CMPL;
|
#endif
|
break;
|
}
|
}
|
#if BLE_ADV_WAKEUP_ENABLE
|
case HW_CFG_WRITE_WAKEUP_ADV_DATA:
|
{
|
uint8_t adv_data_len = wakeup_ADV_Info.data_len;
|
if(adv_data_len > 0)
|
{
|
uint8_t *ptr = (uint8_t *) (p_buf + 1);
|
uint8_t i, adv_len;
|
uint8_t pld_len = adv_data_len + 4;//add the length of gpio & level & grp nums & total len
|
Wakeup_ADV_Grp_St *adv_grp;
|
UINT16_TO_STREAM(ptr, HCI_CMD_WRITE_WAKEUP_ADV_DATA);
|
UINT8_TO_STREAM(ptr, pld_len);
|
UINT8_TO_STREAM(ptr, wakeup_ADV_Info.gpio_no);
|
UINT8_TO_STREAM(ptr, wakeup_ADV_Info.level);
|
UINT8_TO_STREAM(ptr, wakeup_ADV_Info.grp_nums);
|
UINT8_TO_STREAM(ptr, adv_data_len);
|
for(i = 0; i < wakeup_ADV_Info.grp_nums; i++)
|
{
|
adv_grp = &wakeup_ADV_Info.adv_group[i];
|
UINT8_TO_STREAM(ptr, adv_grp->grp_len);
|
UINT8_TO_STREAM(ptr, adv_grp->addr_offset);
|
adv_len = (adv_grp->grp_len - 2) >> 1;
|
|
SKWBT_LOG("grp len:%d, adv_len:%d", adv_grp->grp_len, adv_len);
|
|
memcpy(ptr, adv_grp->data, adv_len);
|
ptr += adv_len;
|
memcpy(ptr, adv_grp->mask, adv_len);
|
ptr += adv_len;
|
}
|
|
p_buf->len = 3 + pld_len;//packet len
|
bt_vendor_cbacks->xmit_cb(HCI_CMD_WRITE_WAKEUP_ADV_DATA, p_buf, scomm_vendor_config_callback);
|
hw_cfg_cb.state = HW_CFG_NV_SEND_CMPL;
|
break;
|
}
|
}
|
#endif
|
case HW_CFG_NV_SEND_CMPL:
|
{
|
bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
|
bt_vendor_cbacks->dealloc(p_buf);//free buffer
|
hw_cfg_cb.state = HW_CFG_INIT;
|
break;
|
}
|
}
|
}
|
|
|
/* Free the RX event buffer */
|
if ((bt_vendor_cbacks) && (p_evt_buf != NULL))
|
{
|
bt_vendor_cbacks->dealloc(p_evt_buf);
|
}
|
|
|
}
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_config_start
|
**
|
** Description Kick off controller initialization process
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
void scomm_vendor_config_start()
|
{
|
memset(&hw_cfg_cb, 0, sizeof(bt_hw_cfg_cb_st));
|
|
HC_BT_HDR *p_buf = NULL;
|
uint8_t *p;
|
|
hw_cfg_cb.state = HW_CFG_INIT;
|
|
if (bt_vendor_cbacks)
|
{
|
p_buf = (HC_BT_HDR *) bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + HCI_CMD_PREAMBLE_SIZE);
|
if (p_buf)
|
{
|
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
|
p_buf->offset = 0;
|
p_buf->layer_specific = 0;
|
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
|
|
p = (uint8_t *) (p_buf + 1);
|
UINT16_TO_STREAM(p, HCI_RESET);
|
*p = 0; /* parameter length */
|
|
hw_cfg_cb.state = HW_CFG_START;
|
|
bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf, scomm_vendor_config_callback);
|
}
|
else
|
{
|
ALOGE("%s buffer alloc fail", __func__);
|
}
|
}
|
else
|
{
|
ALOGE("%s call back func is null", __func__);
|
}
|
}
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_write_wakeup_adv_data_callback
|
**
|
** Description send wakeup adv data callback
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
void scomm_vendor_write_wakeup_adv_data_callback(void *p_mem)
|
{
|
uint8_t status = 0;
|
uint16_t opcode = 0;
|
HC_BT_HDR *p_evt_buf = NULL;
|
if(p_mem != NULL)
|
{
|
uint8_t *p;
|
p_evt_buf = (HC_BT_HDR *) p_mem;
|
status = *((uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_STATUS_OFFSET);
|
p = (uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE_OFFSET;
|
STREAM_TO_UINT16(opcode, p);
|
|
ALOGD("%s status:%d ,opcode:%04X", __func__, status, opcode);
|
|
if(bt_vendor_cbacks)
|
{
|
bt_vendor_cbacks->dealloc(p_evt_buf);
|
}
|
}
|
else
|
{
|
ALOGE("%s pram is null", __func__);
|
}
|
}
|
|
/*******************************************************************************
|
**
|
** Function scomm_vendor_write_wakeup_adv_enable
|
**
|
** Description send wakeup adv enable
|
**
|
** Returns None
|
**
|
*******************************************************************************/
|
void scomm_vendor_write_wakeup_adv_enable()
|
{
|
HC_BT_HDR *p_buf = (HC_BT_HDR *)bt_vendor_cbacks->alloc(HCI_CMD_MAX_LEN);
|
uint8_t *p;
|
|
if(p_buf)
|
{
|
uint8_t param_len = 1;
|
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
|
p_buf->offset = 0;
|
p_buf->len = 0;
|
p_buf->layer_specific = 0;
|
|
p = (uint8_t *) (p_buf + 1);
|
UINT16_TO_STREAM(p, HCI_CMD_WRITE_WAKEUP_ADV_ENABLE);
|
UINT8_TO_STREAM(p, param_len);
|
UINT8_TO_STREAM(p, 0x01);
|
|
p_buf->len = 3 + param_len;//packet len
|
|
bt_vendor_cbacks->xmit_cb(HCI_CMD_WRITE_WAKEUP_ADV_ENABLE, p_buf, scomm_vendor_write_wakeup_adv_data_callback);
|
}
|
else
|
{
|
ALOGE("%s buffer alloc fail", __func__);
|
}
|
|
|
}
|
|
|
/*
|
data_str = "xxxx;...."
|
*/
|
char *scomm_vendor_config_get_uint8(char *data_str, uint8_t *value)
|
{
|
char *split0 = strchr(data_str, ';');
|
uint8_t len = 0;
|
char buffer[8] = {0};
|
if((split0 == NULL) || (split0 == data_str))
|
{
|
return NULL;
|
}
|
len = split0 - data_str;
|
if(len > 4)//invalid
|
{
|
SKWBT_LOG("%s, invalid str , %s", __func__, data_str);
|
return NULL;
|
}
|
memcpy(buffer, data_str, len);
|
*value = atoi(buffer);
|
return split0 + 1;//skip ;
|
}
|
|
void scomm_vendor_parse_wakeup_adv_conf(char *data_str)
|
{
|
//WakeupADVData=GPIO_No(decimal);Level(decimal);addr offset(decimal);ADVData(Hex);Mask(Hex)
|
int str_len = strlen(data_str);
|
char *base_ptr = data_str;
|
char *split0, *split1;
|
uint8_t adv_grp_nums = 0, adv_len = 0, mask_len;
|
uint8_t gpio_no = 0, level = 0, addr_offset;
|
uint8_t i = 0, j = 0, k;
|
Wakeup_ADV_Grp_St *adv_grp;
|
int total_len = 0;
|
|
wakeup_ADV_Info.data_len = 0;
|
if(str_len > 512)
|
{
|
SKWBT_LOG("%s, invalid config str, %s", __func__, data_str);
|
return ;
|
}
|
if((base_ptr = scomm_vendor_config_get_uint8(base_ptr, &gpio_no)) == NULL)
|
{
|
return ;
|
}
|
if((base_ptr = scomm_vendor_config_get_uint8(base_ptr, &level)) == NULL)
|
{
|
return ;
|
}
|
for(k = 0; k < BLE_ADV_WAKEUP_GRP_NUMS; k++)
|
{
|
//addr offset(decimal);ADVData(Hex);Mask(Hex)
|
if((base_ptr = scomm_vendor_config_get_uint8(base_ptr, &addr_offset)) == NULL)
|
{
|
break;
|
}
|
if((addr_offset == 1) || (addr_offset > 26))
|
{
|
SKWBT_LOG("%s, invalid addr_offset , %s", __func__, data_str);
|
return ;
|
}
|
adv_grp = &wakeup_ADV_Info.adv_group[k];
|
split0 = strchr(base_ptr, ';');
|
if(split0 == NULL)
|
{
|
SKWBT_LOG("%s, invalid config , %s", __func__, data_str);
|
return ;
|
}
|
split1 = strchr(split0 + 1, ';');
|
|
adv_len = split0 - base_ptr;
|
adv_grp->addr_offset = addr_offset;
|
adv_grp->grp_len = adv_len + 2;//add addr_offset & self length
|
|
split0 ++;//skip ;
|
if(split1 == NULL)
|
{
|
mask_len = data_str + str_len - split0;
|
}
|
else
|
{
|
mask_len = split1 - split0;
|
}
|
if(mask_len != adv_len)
|
{
|
SKWBT_LOG("%s, mask_len != adv_len , %s", __func__, data_str);
|
return ;
|
}
|
SKWBT_LOG("grp len:%d, adv_len:%d", adv_grp->grp_len, adv_len);
|
for(i = 0, j = 0; i < adv_len; j ++, i += 2)
|
{
|
adv_grp->data[j] = (char2hex(base_ptr[i]) << 4) | char2hex(base_ptr[i + 1]);
|
adv_grp->mask[j] = (char2hex(split0[i]) << 4) | char2hex(split0[i + 1]);
|
}
|
total_len += adv_grp->grp_len;
|
adv_grp_nums ++;
|
if(split1 == NULL)
|
{
|
break;
|
}
|
base_ptr = split1 + 1;
|
}
|
wakeup_ADV_Info.data_len = total_len;//not contain gpio & level
|
wakeup_ADV_Info.grp_nums = adv_grp_nums;
|
wakeup_ADV_Info.gpio_no = gpio_no;
|
wakeup_ADV_Info.level = level;
|
|
SKWBT_LOG("ADV str len:%d, gpio:%d, level:%d, adv_grp_nums:%d, total_len:%d, Data:%s", str_len, gpio_no, level, adv_grp_nums, total_len, data_str);
|
}
|