1739 lines
45 KiB
C
1739 lines
45 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
|
|
|
|
// Standard C library headers
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <glob.h>
|
|
|
|
// POSIX/system headers
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/ioctl.h>
|
|
#include <dirent.h>
|
|
#include <pthread.h>
|
|
|
|
// Project-specific headers
|
|
#include "aml_conf.h"
|
|
#include "aml_multibt.h"
|
|
|
|
/******************************************************************************
|
|
** Constants & Macros
|
|
******************************************************************************/
|
|
#ifndef __NR_delete_module
|
|
#if defined(__x86_64__)
|
|
#define __NR_delete_module 176
|
|
#elif defined(__i386__)
|
|
#define __NR_delete_module 129
|
|
#elif defined(__aarch64__)
|
|
#define __NR_delete_module 176
|
|
#else
|
|
#error "Need __NR_delete_module for this arch"
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef MAILBOX_MOD_NAME
|
|
#define MBOX_USER_MAX_LEN 96
|
|
#define PATH_MAX_LEN 64
|
|
#define ARMV8_TO_AOCPU "/dev/ree2aocpu"
|
|
#define CMD_SET_MID 0xFC
|
|
#endif
|
|
|
|
#define PORT_NAME_MAX_LEN 256
|
|
#define BUF_MAX_LEN 256
|
|
|
|
#define ENUM_DIR_PCI "/sys/bus/pci/devices"
|
|
#define ENUM_DIR_SDIO "/sys/bus/sdio/devices"
|
|
#define ENUM_DIR_USB "/sys/bus/usb/devices"
|
|
|
|
#define DBG_CFG_PATH "/etc/bluetooth/main.conf"
|
|
#define RFKILL_PATH "/sys/class/rfkill"
|
|
#define WIFI_POWER_DEV "/dev/wifi_power"
|
|
|
|
#ifdef AUDIO_PROJECT
|
|
#define WIFI_DRV_PATH "/lib/modules/*/kernel/amlogic/wifi"
|
|
#define BT_DRV_PATH "/lib/modules/*/kernel/amlogic/bt"
|
|
#elif defined(CAMERA_PROJECT)
|
|
#define WIFI_DRV_PATH "/amlogic/ext_modules"
|
|
#define BT_DRV_PATH "/amlogic/ext_modules"
|
|
#else
|
|
#define WIFI_DRV_PATH "/lib/modules/extramodules"
|
|
#define BT_DRV_PATH "/lib/modules/*/kernel/drivers/amlogic/bt"
|
|
#endif
|
|
|
|
#define AMLBT_IF_TYPE "amlbt_if_type="
|
|
|
|
#define POWER_EVENT_DEF '0'
|
|
#define POWER_EVENT_RESET '1'
|
|
#define POWER_EVENT_EN '2'
|
|
|
|
/******************************************************************************
|
|
** Local type definitions
|
|
******************************************************************************/
|
|
/* serial control block */
|
|
typedef struct
|
|
{
|
|
int fd; /* fd to Bluetooth device */
|
|
struct termios termios; /* serial terminal of BT port */
|
|
char port_name[PORT_NAME_MAX_LEN];
|
|
} uart_cb;
|
|
|
|
typedef struct {
|
|
unsigned int vid;
|
|
char *dev_name;
|
|
char power_type;
|
|
} dev_info_uart;
|
|
|
|
typedef struct {
|
|
char *mod_name;
|
|
char *mod_arg;
|
|
} mod_info;
|
|
|
|
typedef struct {
|
|
char *dev_name;
|
|
mod_info mod[MOD_MAX_NUM];
|
|
} drv_info;
|
|
|
|
typedef int (*op_cb)(void);
|
|
|
|
typedef struct {
|
|
op_steps step;
|
|
op_cb tag_ops;
|
|
} steps_table;
|
|
|
|
/******************************************************************************
|
|
** Function Declaration
|
|
******************************************************************************/
|
|
static int distinguish_bt_mod(void);
|
|
static int set_power_type(void);
|
|
static int mailbox_mod_name_thread(void);
|
|
static int amlbt_drv_insmod(void);
|
|
static int amlbt_drv_modprobe(void);
|
|
static void clr_bt_power_bit(char power_type);
|
|
|
|
/******************************************************************************
|
|
** Static variables
|
|
******************************************************************************/
|
|
static const uart_cfg uart_cfg_h5 =
|
|
{
|
|
(USERIAL_DATABITS_8 | USERIAL_PARITY_EVEN | USERIAL_STOPBITS_1),
|
|
USERIAL_BAUD_115200,
|
|
};
|
|
|
|
static const uart_cfg uart_cfg_h4 =
|
|
{
|
|
(USERIAL_DATABITS_8 | USERIAL_PARITY_NONE | USERIAL_STOPBITS_1),
|
|
USERIAL_BAUD_115200,
|
|
};
|
|
|
|
static const steps_table steps_table_exp[] = {
|
|
{op_distinguish_bt, distinguish_bt_mod},
|
|
{op_set_power_type, set_power_type},
|
|
#ifdef MAILBOX_MOD_NAME
|
|
{op_mailbox_mod_name, mailbox_mod_name_thread},
|
|
#endif
|
|
{op_amlbt_drv_insmod, amlbt_drv_insmod},
|
|
{op_steps_max, NULL},
|
|
};
|
|
|
|
static bool dbg_flag = false;
|
|
|
|
static uart_cb bt_uart_cb;
|
|
static char *rfkill_state_path = NULL;
|
|
static bool pci_flag = false;
|
|
dev_item amlbt_dev = { 0 };
|
|
|
|
/******************************************************************************
|
|
** init variables
|
|
******************************************************************************/
|
|
static uint8_t vendor_info[] = {0x01, 0x01, 0x10, 0x00};
|
|
static uint8_t vendor_reset[] = {0x01, 0x03, 0x0c, 0x00};
|
|
static uint8_t vendor_sync[] = {0xc0, 0x00, 0x2f, 0x00, 0xd0, 0x01, 0x7e, 0xc0}; //{0x01, 0x7E}
|
|
//static uint8_t vendor_sync_rsp[] = {0xc0,0x00,0x2f,0x00,0xd0,0x02,0x7d,0xc0}; //{0x02, 0x7D}
|
|
|
|
/******************************************************************************
|
|
** Bt device info of pci interface
|
|
******************************************************************************/
|
|
static const dev_info bt_dev_pci[] = {
|
|
// amlogic pice modules
|
|
{{0x1F35, 0x0602}, {"aml_w2_p", POWER_EVENT_RESET, W2_UART, 256, AML_W2_BT_FW_UART_FILE}},
|
|
{{0x1F35, 0x0642}, {"aml_w2_p", POWER_EVENT_RESET, W2_UART, 256, AML_W2_BT_FW_UART_FILE}},
|
|
{{0x1F35, 0x0682}, {"aml_w2_p", POWER_EVENT_RESET, W2_UART, 256, AML_W2_BT_REVC_FW_UART_FILE}},
|
|
};
|
|
|
|
/******************************************************************************
|
|
** Bt device info of sdio interface
|
|
******************************************************************************/
|
|
static const dev_info bt_dev_sdio[] = {
|
|
// amlogic sdio modules
|
|
{{0x8888, 0x8888}, {"aml_w1", POWER_EVENT_EN, W1_UART, 256, AML_W1_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0500}, {"aml_w1u_s", POWER_EVENT_DEF, W1U_UART, 256, AML_W1U_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0540}, {"aml_w1u_s", POWER_EVENT_DEF, W1U_UART, 256, AML_W1U_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0600}, {"aml_w2_s", POWER_EVENT_DEF, W2_UART, 256, AML_W2_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0640}, {"aml_w2_s", POWER_EVENT_DEF, W2_UART, 256, AML_W2_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0680}, {"aml_w2_s", POWER_EVENT_DEF, W2_UART, 256, AML_W2_BT_REVC_FW_UART_FILE}},
|
|
{{0x1B8E, 0x8881}, {"aml_w2_s", POWER_EVENT_DEF, W2_UART, 256, AML_W2_BT_REVC_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0800}, {"aml_w2l_s", POWER_EVENT_DEF, W2L_UART, 384, AML_W2L_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0810}, {"aml_w2l_s", POWER_EVENT_DEF, W2L_UART, 384, AML_W2L_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0808}, {"aml_w2l_s", POWER_EVENT_DEF, W2L_UART, 384, AML_W2L_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0840}, {"aml_w2l_s", POWER_EVENT_DEF, W2L_UART, 384, AML_W2L_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0848}, {"aml_w2l_s", POWER_EVENT_DEF, W2L_UART, 384, AML_W2L_BT_FW_UART_FILE}},
|
|
{{0x1B8E, 0x0850}, {"aml_w2l_s", POWER_EVENT_DEF, W2L_UART, 384, AML_W2L_BT_FW_UART_FILE}},
|
|
};
|
|
|
|
/******************************************************************************
|
|
** Bt device info of usb interface
|
|
******************************************************************************/
|
|
static const dev_info bt_dev_usb[] = {
|
|
// amlogic usb modules
|
|
{{0x1B8E, 0x4C55}, {"aml_w1u", POWER_EVENT_EN, W1U_USB, 256, AML_W1U_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0541}, {"aml_w1u", POWER_EVENT_EN, W1U_USB, 256, AML_W1U_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0601}, {"aml_w2_u", POWER_EVENT_EN, W2_USB, 256, AML_W2_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0641}, {"aml_w2_u", POWER_EVENT_EN, W2_USB, 256, AML_W2_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0681}, {"aml_w2_u", POWER_EVENT_EN, W2_USB, 256, AML_W2_BT_REVC_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0801}, {"aml_w2l_u", POWER_EVENT_EN, W2L_USB, 384, AML_W2L_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0809}, {"aml_w2l_u", POWER_EVENT_EN, W2L_USB, 384, AML_W2L_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0811}, {"aml_w2l_u", POWER_EVENT_EN, W2L_USB, 384, AML_W2L_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0841}, {"aml_w2l_u", POWER_EVENT_EN, W2L_USB, 384, AML_W2L_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0849}, {"aml_w2l_u", POWER_EVENT_EN, W2L_USB, 384, AML_W2L_BT_FW_USB_FILE}},
|
|
{{0x1B8E, 0x0851}, {"aml_w2l_u", POWER_EVENT_EN, W2L_USB, 384, AML_W2L_BT_FW_USB_FILE}},
|
|
};
|
|
|
|
static const dev_item bt_dev_comm[] = {
|
|
{"aml_w1", POWER_EVENT_EN, W1_UART, 256, AML_W1_BT_FW_UART_FILE},
|
|
{"aml_w1u", POWER_EVENT_EN, W1U_USB, 256, AML_W1U_BT_FW_UART_FILE},
|
|
{"aml_w1u_s", POWER_EVENT_DEF, W1U_UART, 256, AML_W1U_BT_FW_UART_FILE},
|
|
{"aml_w2_u", POWER_EVENT_EN, W2_USB, 256, AML_W2_BT_FW_USB_FILE},
|
|
{"aml_w2_s", POWER_EVENT_DEF, W2_UART, 256, AML_W2_BT_FW_UART_FILE},
|
|
};
|
|
|
|
/******************************************************************************
|
|
** Bt device info of uart interface
|
|
******************************************************************************/
|
|
static const dev_info_uart bt_dev_uart[] = {
|
|
{BT_VID_BROADCOM, "bcm_bt", POWER_EVENT_RESET},
|
|
{BT_VID_QUALCOMM, "qca_bt", POWER_EVENT_RESET},
|
|
{BT_VID_REALTECK, "rtl_bt", POWER_EVENT_RESET},
|
|
{BT_VID_MEDIATEK, "mtk_bt", POWER_EVENT_RESET},
|
|
{BT_VID_AMLOGIC, "aml_bt", POWER_EVENT_RESET},
|
|
{BT_VID_UNISOC, "uwe_bt", POWER_EVENT_RESET},
|
|
};
|
|
|
|
static const drv_info aml_drv[] = {
|
|
{"aml_w1", {{"aml_sdio", ""}, {"sdio_bt", ""}}},
|
|
{"aml_w1u", {{"w1u_comm", "bus_type=usb"}, {"w1u_bt", AMLBT_IF_TYPE"1281"}}},
|
|
{"aml_w1u_s", {{"w1u_comm", "bus_type=sdio"}, {"w1u_bt", AMLBT_IF_TYPE"1280"}}},
|
|
{"aml_w2_s", {{"w2_comm", "bus_type=sdio"}, {"w2_bt", AMLBT_IF_TYPE"1536"}}},
|
|
{"aml_w2_p", {{"w2_comm", "bus_type=pci"}, {"w2_bt", AMLBT_IF_TYPE"1538"}}},
|
|
{"aml_w2_u", {{"w2_comm", "bus_type=usb"}, {"w2_bt", AMLBT_IF_TYPE"1537"}}},
|
|
{"aml_w2l_s", {{"w2l_comm", "bus_type=sdio"}, {"w2l_bt", AMLBT_IF_TYPE"2048"}}},
|
|
{"aml_w2l_u", {{"w2l_comm", "bus_type=usb"}, {"w2l_bt", AMLBT_IF_TYPE"2049"}}},
|
|
};
|
|
|
|
void set_dbg_flag(int val)
|
|
{
|
|
if (val == 1) {
|
|
dbg_flag = true;
|
|
} else {
|
|
dbg_flag = false;
|
|
}
|
|
|
|
ALOGI("dbg_flag: %s", dbg_flag ? "true" : "false");
|
|
}
|
|
|
|
bool get_dbg_flag(void)
|
|
{
|
|
return dbg_flag;
|
|
}
|
|
|
|
#ifdef MAILBOX_MOD_NAME
|
|
static void* mailbox_mod_name(void *arg)
|
|
{
|
|
struct merge_info {
|
|
int cmd;
|
|
char msg[MBOX_USER_MAX_LEN];
|
|
} merge_data = {0};
|
|
|
|
int fd = -1, ret = -1;
|
|
unsigned int len = 0;
|
|
char path[PATH_MAX_LEN] = {'\0'};
|
|
char bt_name[MOD_NAME_MAX_LEN + 1] = {'\0'};
|
|
const char *dev_name = (char *)arg;
|
|
|
|
if (!dev_name) {
|
|
ALOGE("dev_name is null");
|
|
return NULL;
|
|
}
|
|
|
|
snprintf(path, sizeof(path), "%s", ARMV8_TO_AOCPU);
|
|
ALOGD("open %s", path);
|
|
|
|
fd = open(path, O_RDWR);
|
|
if (fd < 0) {
|
|
ALOGE("open %s failed: %s (%d)", path, strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
merge_data.cmd = CMD_SET_MID;
|
|
strncpy(merge_data.msg, dev_name, MBOX_USER_MAX_LEN - 1);
|
|
merge_data.msg[MBOX_USER_MAX_LEN - 1] = '\0';
|
|
len = sizeof(merge_data.cmd) + strnlen(merge_data.msg, MBOX_USER_MAX_LEN);
|
|
if (len > sizeof(merge_data))
|
|
len = sizeof(merge_data);
|
|
|
|
ret = write(fd, &merge_data, len);
|
|
if (ret < 0) {
|
|
ALOGE("write failed: %s (%d)", strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
ret = read(fd, bt_name, MOD_NAME_MAX_LEN);
|
|
if (ret < 0) {
|
|
ALOGE("read failed: %s (%d)", strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
ALOGI("write ret:%d, buf:%s", ret, bt_name);
|
|
|
|
exit:
|
|
if (fd >= 0)
|
|
close(fd);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int mailbox_mod_name_thread(void)
|
|
{
|
|
pthread_t thread_id;
|
|
int ret;
|
|
|
|
ret = pthread_create(&thread_id, NULL, mailbox_mod_name, amlbt_dev.dev_name);
|
|
if (ret != 0) {
|
|
ALOGE("pthread_create fail: %s", strerror(ret));
|
|
return -1;
|
|
} else {
|
|
ret = pthread_detach(thread_id);
|
|
if (ret != 0) {
|
|
ALOGE("pthread_detach fail: %s", strerror(ret));
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static bool get_pci_flag(void)
|
|
{
|
|
return pci_flag;
|
|
}
|
|
|
|
static void set_pci_flag(bool val)
|
|
{
|
|
pci_flag = val;
|
|
}
|
|
|
|
static void set_amlbt_dev_item(const dev_item *dev)
|
|
{
|
|
if ((dev == NULL)) {
|
|
ALOGE("parameters err");
|
|
return;
|
|
}
|
|
|
|
amlbt_dev = *dev;
|
|
ALOGD("amlbt_name:%s", amlbt_dev.dev_name);
|
|
|
|
if (amlbt_info_update(AMLBT_NAME, amlbt_dev.dev_name) < 0) {
|
|
ALOGE("update: %s fail", AMLBT_NAME);
|
|
}
|
|
}
|
|
|
|
dev_item* get_amlbt_dev_item(void)
|
|
{
|
|
ALOGD("amlbt_name:%s", amlbt_dev.dev_name);
|
|
return &amlbt_dev ;
|
|
}
|
|
|
|
static bool matching_dev_id(const dev_info *dev, unsigned int dev_size, const dev_id *mod_id)
|
|
{
|
|
unsigned int cnt;
|
|
bool ret = false;
|
|
|
|
if ((dev == NULL) || (mod_id == NULL)) {
|
|
ALOGE("parameters err");
|
|
return ret;
|
|
}
|
|
|
|
for (cnt = 0; cnt < dev_size; cnt++) {
|
|
if ((dev[cnt].mod_id.vid == mod_id->vid) && (dev[cnt].mod_id.pid == mod_id->pid)) {
|
|
ALOGI("matched vid:%04x, pid:%04x, dev_name:%s, cnt:%u",
|
|
dev[cnt].mod_id.vid, dev[cnt].mod_id.pid, dev[cnt].mod_item.dev_name, cnt);
|
|
set_amlbt_dev_item(&dev[cnt].mod_item);
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool matching_dev_id_uart(unsigned int vid)
|
|
{
|
|
unsigned int cnt;
|
|
bool ret = false;
|
|
|
|
for (cnt = 0; cnt < ARRAY_SIZE(bt_dev_uart); cnt++) {
|
|
if (bt_dev_uart[cnt].vid == vid) {
|
|
ALOGI("matched vid:%04x, dev_name:%s, cnt:%u",bt_dev_uart[cnt].vid,
|
|
bt_dev_uart[cnt].dev_name, cnt);
|
|
strcpy(amlbt_dev.dev_name, bt_dev_uart[cnt].dev_name);
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool matching_dev_name(const dev_item *dev, unsigned int dev_size, const char *val)
|
|
{
|
|
int cnt;
|
|
bool ret = false;
|
|
|
|
if ((dev == NULL) || (val == NULL)) {
|
|
ALOGE("parameters err");
|
|
return ret;
|
|
}
|
|
|
|
for (cnt = 0; cnt < dev_size; cnt++) {
|
|
if (!strcmp(dev[cnt].dev_name, val)) {
|
|
ALOGI("matched dev_name:%s, cnt:%d", dev[cnt].dev_name, cnt);
|
|
set_amlbt_dev_item(&dev[cnt]);
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool amlbt_get_name(void)
|
|
{
|
|
int fd;
|
|
bool ret = false;
|
|
char str[MOD_NAME_MAX_LEN + 1] = {'\0'};
|
|
|
|
fd = open(WIFI_POWER_DEV, O_RDWR);
|
|
if (fd < 0) {
|
|
ALOGE("open (%s) failed: %s (%d)", WIFI_POWER_DEV, strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
if (ioctl (fd, GET_AML_WIFI_MODULE, str) < 0) {
|
|
ALOGE("ioctl GET_AML_WIFI_MODULE failed: %s (%d)", strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
ALOGI("get bt module: %s", str);
|
|
|
|
if (!strncmp(str, "aml" ,3)) {
|
|
ret = matching_dev_name(bt_dev_comm, ARRAY_SIZE(bt_dev_comm), str);
|
|
}
|
|
|
|
exit:
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool write_power_type(const char *power_type, unsigned int len)
|
|
{
|
|
int fd;
|
|
bool ret = false;
|
|
|
|
if (!power_type) {
|
|
ALOGE("power_type is null");
|
|
goto exit;
|
|
}
|
|
|
|
if (access(BT_POWER_EVT_1, F_OK) == 0) {
|
|
fd = open(BT_POWER_EVT_1, O_WRONLY);
|
|
} else {
|
|
fd = open(BT_POWER_EVT_2, O_WRONLY);
|
|
}
|
|
|
|
if (fd < 0) {
|
|
ALOGE("open btpower_evt failed: %s (%d)", strerror(errno), errno);
|
|
goto exit;
|
|
} else {
|
|
if (write(fd, power_type, len) < len) {
|
|
ALOGE("write btpower_evt failed: %s (%d)", strerror(errno), errno);
|
|
} else {
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int set_power_type(void)
|
|
{
|
|
if (!write_power_type(&(amlbt_dev.power_type), sizeof(char))) {
|
|
ALOGE("write power type failed");
|
|
return -1;
|
|
}
|
|
|
|
clr_bt_power_bit(amlbt_dev.power_type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool find_target_file(const char *path, const char * file)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *next;
|
|
bool ret = false;
|
|
|
|
if ((path == NULL) || (file == NULL)) {
|
|
ALOGE("parameters err");
|
|
return ret;
|
|
}
|
|
|
|
dir = opendir(path);
|
|
if (dir == NULL) {
|
|
ALOGE("opendir (%s) failed: %s (%d)", path, strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
while ((next = readdir(dir)) != NULL) {
|
|
if (!strncmp(next->d_name, file, strlen(file))) {
|
|
ret = true;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (dir) {
|
|
closedir(dir);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void clr_bt_power_bit(char power_type)
|
|
{
|
|
int fd;
|
|
|
|
if (get_pci_flag()) {
|
|
ALOGI("pcie needn't clr bt power bit");
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(amlbt_dev.dev_name, "aml_w2_s")) {
|
|
ALOGI("aml w2_s needn't clr bt power bit");
|
|
return;
|
|
}
|
|
|
|
if (power_type == POWER_EVENT_RESET) {
|
|
ALOGI("power_type:(%c) on separately", power_type);
|
|
} else {
|
|
ALOGI("power_type:(%c) not on separately", power_type);
|
|
return;
|
|
}
|
|
|
|
fd = open(WIFI_POWER_DEV, O_RDWR);
|
|
if (fd < 0) {
|
|
ALOGE("open (%s) failed: %s (%d)", WIFI_POWER_DEV, strerror(errno), errno);
|
|
return;
|
|
}
|
|
|
|
if (ioctl(fd, CLR_BT_POWER_BIT) < 0) {
|
|
ALOGE("ioctl CLR_BT_POWER_BIT failed: %s (%d)", strerror(errno), errno);
|
|
}
|
|
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static bool distinguish_bt_mod_pci(void)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *next;
|
|
FILE *fp = NULL;
|
|
bool ret = false;
|
|
|
|
dir = opendir(ENUM_DIR_PCI);
|
|
if (!dir) {
|
|
ALOGE("opendir (%s) failed: %s (%d)", ENUM_DIR_PCI, strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
while ((next = readdir(dir)) != NULL) {
|
|
char line[BUF_MAX_LEN] = {'\0'};
|
|
char uevent_file[BUF_MAX_LEN*2] = {'\0'};
|
|
|
|
/* Read pci uevent file, uevent's data like selow:
|
|
* DRIVER=w2_comm
|
|
* PCI_CLASS=78000
|
|
* PCI_ID=1F35:0602
|
|
* PCI_SUBSYS_ID=1556:1111
|
|
* PCI_SLOT_NAME=0000:01:00.0
|
|
* MODALIAS=pci:v00001F35d00000602sv00001556sd00001111bc07sc80i00
|
|
*/
|
|
snprintf(uevent_file, sizeof(uevent_file), "%s/%s/uevent", ENUM_DIR_PCI, next->d_name);
|
|
fp = fopen(uevent_file, "r");
|
|
if (fp == NULL) {
|
|
continue;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), fp)) {
|
|
dev_id mod_id = {0, 0};
|
|
char *pos = NULL;
|
|
|
|
pos = strstr(line, "PCI_ID=");
|
|
if (!pos) {
|
|
continue;
|
|
}
|
|
|
|
if (sscanf(pos + 7, "%x:%x", &(mod_id.vid), &(mod_id.pid)) <= 0) {
|
|
continue;
|
|
}
|
|
|
|
ALOGD("list vid:0x%04x, pid:0x%04x", mod_id.vid, mod_id.pid);
|
|
ret = matching_dev_id(bt_dev_pci, ARRAY_SIZE(bt_dev_pci), &mod_id);
|
|
if (ret) {
|
|
set_pci_flag(true);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (fp) {
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (fp) {
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
|
|
if (dir) {
|
|
closedir(dir);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool distinguish_bt_mod_sdio(void)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *next;
|
|
FILE *fp = NULL;
|
|
bool ret = false;
|
|
|
|
dir = opendir(ENUM_DIR_SDIO);
|
|
if (!dir) {
|
|
ALOGE("opendir (%s) failed: %s (%d)", ENUM_DIR_SDIO, strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
while ((next = readdir(dir)) != NULL) {
|
|
char line[BUF_MAX_LEN] = {'\0'};
|
|
char uevent_file[BUF_MAX_LEN*2] = {'\0'};
|
|
|
|
/* Read sdio uevent file, uevent's data like below:
|
|
* DRIVER=aml_sdio
|
|
* OF_NAME=wifi
|
|
* OF_FULLNAME=/soc/sdio@fe088000/wifi@1
|
|
* OF_COMPATIBLE_0=brcm,bcm4329-fmac
|
|
* OF_COMPATIBLE_N=1
|
|
* SDIO_CLASS=07
|
|
* SDIO_ID=1B8E:0540
|
|
* MODALIAS=sdio:c07v1B8Ed0540
|
|
*/
|
|
snprintf(uevent_file, sizeof(uevent_file), "%s/%s/uevent", ENUM_DIR_SDIO, next->d_name);
|
|
fp = fopen(uevent_file, "r");
|
|
if (fp == NULL) {
|
|
continue;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), fp)) {
|
|
dev_id mod_id = {0, 0};
|
|
char *pos = NULL;
|
|
|
|
pos = strstr(line, "SDIO_ID=");
|
|
if (!pos) {
|
|
continue;
|
|
}
|
|
|
|
if (sscanf(pos + 8, "%x:%x", &(mod_id.vid), &(mod_id.pid)) <= 0) {
|
|
continue;
|
|
}
|
|
|
|
ALOGD("list vid:0x%04x, pid:0x%04x", mod_id.vid, mod_id.pid);
|
|
ret = matching_dev_id(bt_dev_sdio, ARRAY_SIZE(bt_dev_sdio), &mod_id);
|
|
if (ret) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (fp) {
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (fp) {
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
|
|
if (dir) {
|
|
closedir(dir);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool distinguish_bt_mod_usb(void)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *next;
|
|
FILE *fp = NULL;
|
|
bool ret = false;
|
|
|
|
dir = opendir(ENUM_DIR_USB);
|
|
if (!dir) {
|
|
ALOGE("opendir (%s) failed: %s (%d)", ENUM_DIR_USB, strerror(errno), errno);
|
|
goto exit;
|
|
}
|
|
|
|
while ((next = readdir(dir)) != NULL) {
|
|
char line[BUF_MAX_LEN] = {'\0'};
|
|
char uevent_file[BUF_MAX_LEN*2] = {'\0'};
|
|
|
|
/* Read usb uevent file, uevent's data like below:
|
|
* MAJOR=189
|
|
* MINOR=1
|
|
* DEVNAME=bus/usb/001/002
|
|
* DEVTYPE=usb_device
|
|
* DRIVER=usb
|
|
* PRODUCT=1b8e/601/101
|
|
* TYPE=0/0/0
|
|
* BUSNUM=001
|
|
* DEVNUM=002
|
|
*/
|
|
snprintf(uevent_file, sizeof(uevent_file), "%s/%s/uevent", ENUM_DIR_USB, next->d_name);
|
|
fp = fopen(uevent_file, "r");
|
|
if (fp == NULL) {
|
|
continue;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), fp)) {
|
|
unsigned int bcdev;
|
|
dev_id mod_id = {0, 0};
|
|
char *pos = NULL;
|
|
|
|
pos = strstr(line, "PRODUCT=");
|
|
if (!pos) {
|
|
continue;
|
|
}
|
|
|
|
if (sscanf(pos + 8, "%x/%x/%x", &(mod_id.vid), &(mod_id.pid), &bcdev) <= 0) {
|
|
continue;
|
|
}
|
|
|
|
ALOGD("list vid:0x%04x, pid:0x%04x", mod_id.vid, mod_id.pid);
|
|
ret = matching_dev_id(bt_dev_usb, ARRAY_SIZE(bt_dev_usb), &mod_id);
|
|
if (ret) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (fp) {
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (fp) {
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
|
|
if (dir) {
|
|
closedir(dir);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function init_rfkill_aml_bt
|
|
**
|
|
** Description Traverse rfkill file path to obtain rfkill node named bt-dev
|
|
**
|
|
** Returns true : success
|
|
** false : fail
|
|
**
|
|
*******************************************************************************/
|
|
static bool init_rfkill_aml_bt(void)
|
|
{
|
|
glob_t globbuf;
|
|
FILE *fp = NULL;
|
|
bool ret = false;
|
|
int i;
|
|
|
|
char pattern[BUF_MAX_LEN];
|
|
snprintf(pattern, sizeof(pattern), "%s/rfkill*/name", RFKILL_PATH);
|
|
|
|
if (glob(pattern, 0, NULL, &globbuf) != 0) {
|
|
ALOGE("glob failed: %s", strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < globbuf.gl_pathc; ++i) {
|
|
char line[BUF_MAX_LEN] = {0};
|
|
|
|
fp = fopen(globbuf.gl_pathv[i], "r");
|
|
if (!fp) {
|
|
continue;
|
|
}
|
|
|
|
if (fgets(line, sizeof(line), fp)) {
|
|
if (strncmp(line, "bt-dev", 6) == 0) {
|
|
// Get the parent path: .../rfkill*/name -> .../rfkill*/state
|
|
char *slash = strrchr(globbuf.gl_pathv[i], '/');
|
|
if (slash) {
|
|
*slash = '\0'; // Trim "/name"
|
|
(void)asprintf(&rfkill_state_path, "%s/state", globbuf.gl_pathv[i]);
|
|
ALOGI("RFKILL_STATE_PATH: %s", rfkill_state_path);
|
|
ret = true;
|
|
fclose(fp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
globfree(&globbuf);
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function upio_set_bluetooth_power
|
|
**
|
|
** Description Interact with low layer driver to set Bluetooth power
|
|
** on/off.
|
|
**
|
|
** Returns 0 : SUCCESS or Not-Applicable
|
|
** <0 : ERROR
|
|
**
|
|
*******************************************************************************/
|
|
static int upio_set_bluetooth_power(int on)
|
|
{
|
|
int fd;
|
|
int len = -1;
|
|
int ret = -1;
|
|
|
|
if (!init_rfkill_aml_bt()) {
|
|
ALOGE("init rfkill failed");
|
|
return ret;
|
|
}
|
|
|
|
fd = open(rfkill_state_path, O_WRONLY);
|
|
if (fd < 0) {
|
|
ALOGE("open (%s) failed: %s (%d)", rfkill_state_path, strerror(errno), errno);
|
|
return ret;
|
|
}
|
|
|
|
if (on == UPIO_BT_POWER_OFF) {
|
|
len = write(fd, "0", 1);
|
|
} else if (on == UPIO_BT_POWER_ON) {
|
|
len = write(fd, "1", 1);
|
|
}
|
|
|
|
if (len < 1) {
|
|
ALOGE("write (%s) failed: %s (%d)", rfkill_state_path, strerror(errno), errno);
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function userial_to_tcio_baud
|
|
**
|
|
** Description helper function converts USERIAL baud rates into TCIO
|
|
** conforming baud rates
|
|
**
|
|
** Returns TRUE/FALSE
|
|
**
|
|
*******************************************************************************/
|
|
static bool userial_to_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_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;
|
|
}
|
|
|
|
#if (BT_WAKE_VIA_USERIAL_IOCTL==TRUE)
|
|
/*******************************************************************************
|
|
**
|
|
** Function userial_ioctl_init_bt_wake
|
|
**
|
|
** Description helper function to set the open state of the bt_wake if ioctl
|
|
** is used. it should not hurt in the rfkill case but it might
|
|
** be better to compile it out.
|
|
**
|
|
** Returns none
|
|
**
|
|
*******************************************************************************/
|
|
static void userial_ioctl_init_bt_wake(int fd)
|
|
{
|
|
uint32_t bt_wake_state;
|
|
|
|
#if (BT_WAKE_USERIAL_LDISC == TRUE)
|
|
int ldisc = N_BRCM_HCI; /* brcm sleep mode support line discipline */
|
|
|
|
/* attempt to load enable discipline driver */
|
|
if (ioctl(bt_uart_cb.fd, TIOCSETD, &ldisc) < 0)
|
|
{
|
|
ALOGI("USERIAL_Open():fd %d, TIOCSETD failed: error %d for ldisc: %d",
|
|
fd, errno, ldisc);
|
|
}
|
|
#endif
|
|
|
|
/* assert BT_WAKE through ioctl */
|
|
ioctl(fd, USERIAL_IOCTL_BT_WAKE_ASSERT, NULL);
|
|
ioctl(fd, USERIAL_IOCTL_BT_WAKE_GET_ST, &bt_wake_state);
|
|
ALOGI("userial_ioctl_init_bt_wake read back BT_WAKE state=%i", \
|
|
bt_wake_state);
|
|
}
|
|
#endif // (BT_WAKE_VIA_USERIAL_IOCTL==TRUE)
|
|
|
|
/*****************************************************************************
|
|
** Userial Vendor API Functions
|
|
*****************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function userial_vendor_init
|
|
**
|
|
** Description Initialize userial vendor-specific control block
|
|
**
|
|
** Returns None
|
|
**
|
|
*******************************************************************************/
|
|
static void userial_vendor_init(void)
|
|
{
|
|
bt_uart_cb.fd = -1;
|
|
snprintf(bt_uart_cb.port_name, PORT_NAME_MAX_LEN, "%s", UART_DEV_PORT_BT);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function userial_vendor_open
|
|
**
|
|
** Description Open the serial port with the given configuration
|
|
**
|
|
** Returns device fd
|
|
**
|
|
*******************************************************************************/
|
|
static int userial_vendor_open(uart_cfg *p_cfg)
|
|
{
|
|
uint32_t baud;
|
|
uint8_t data_bits;
|
|
uint16_t parity;
|
|
uint8_t stop_bits;
|
|
|
|
bt_uart_cb.fd = -1;
|
|
|
|
if (!userial_to_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;
|
|
}
|
|
|
|
ALOGI("data_bits:%u", data_bits);
|
|
|
|
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", bt_uart_cb.port_name);
|
|
|
|
if ((bt_uart_cb.fd = open(bt_uart_cb.port_name, O_RDWR)) < 0)
|
|
{
|
|
ALOGE("userial vendor open: unable to open %s", bt_uart_cb.port_name);
|
|
return -1;
|
|
}
|
|
|
|
ALOGI("userial vendor open success!!");
|
|
|
|
tcflush(bt_uart_cb.fd, TCIOFLUSH);
|
|
|
|
tcgetattr(bt_uart_cb.fd, &bt_uart_cb.termios);
|
|
cfmakeraw(&bt_uart_cb.termios);
|
|
|
|
/* Set UART Control Modes */
|
|
bt_uart_cb.termios.c_cflag |= CLOCAL;
|
|
bt_uart_cb.termios.c_cflag |= (CRTSCTS | stop_bits| parity);
|
|
|
|
|
|
tcsetattr(bt_uart_cb.fd, TCSANOW, &bt_uart_cb.termios);
|
|
|
|
/* set input/output baudrate */
|
|
cfsetospeed(&bt_uart_cb.termios, baud);
|
|
cfsetispeed(&bt_uart_cb.termios, baud);
|
|
tcsetattr(bt_uart_cb.fd, TCSANOW, &bt_uart_cb.termios);
|
|
|
|
#if (BT_WAKE_VIA_USERIAL_IOCTL==TRUE)
|
|
userial_ioctl_init_bt_wake(bt_uart_cb.fd);
|
|
#endif
|
|
tcflush(bt_uart_cb.fd, TCIOFLUSH);
|
|
|
|
ALOGI("device fd = %d open", bt_uart_cb.fd);
|
|
|
|
return bt_uart_cb.fd;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function do_write
|
|
**
|
|
** Description write
|
|
**
|
|
** Returns len
|
|
**
|
|
*******************************************************************************/
|
|
static int do_write(int fd, unsigned char *buf,int len)
|
|
{
|
|
int ret = 0;
|
|
int write_offset = 0;
|
|
int write_len = len;
|
|
do {
|
|
ret = write(fd,buf+write_offset,write_len);
|
|
if (ret < 0)
|
|
{
|
|
ALOGE("write failed: %s (%d)", strerror(errno), errno);
|
|
return -1;
|
|
} else if (ret == 0) {
|
|
ALOGE("write failed: %s (%d)", strerror(errno), errno);
|
|
return 0;
|
|
} else {
|
|
if (ret < write_len) {
|
|
ALOGI("write pending, do write ret: %d, %s (%d)", ret, strerror(errno), errno);
|
|
write_len = write_len - ret;
|
|
write_offset = ret;
|
|
} else {
|
|
ALOGI("Write successful");
|
|
break;
|
|
}
|
|
}
|
|
} while(1);
|
|
return len;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function check vendor event
|
|
**
|
|
** Description check info
|
|
**
|
|
** Returns success return 1
|
|
**
|
|
*******************************************************************************/
|
|
static int check_event(unsigned char * rsp, int size, unsigned char *cmd)
|
|
{
|
|
for (int i = 0; i < size; i++) {
|
|
ALOGD("%02x", rsp[i]);
|
|
}
|
|
|
|
if (!(size >= 7))
|
|
return 0;
|
|
|
|
if (rsp[4] != cmd[1] || rsp[5] != cmd[2] || rsp[6] != 0X00)
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function read_vendor_event
|
|
**
|
|
** Description read_vendor_event
|
|
**
|
|
** Returns str
|
|
**
|
|
*******************************************************************************/
|
|
static int read_vendor_event(int fd, unsigned char* buf, int size)
|
|
{
|
|
int remain, r;
|
|
int count = 0;
|
|
|
|
if (size <= 0)
|
|
return -1;
|
|
|
|
struct pollfd pfd;
|
|
int poll_ret;
|
|
pfd.fd = fd;
|
|
pfd.events = POLLIN | POLLHUP;
|
|
|
|
poll_ret = poll(&pfd, 1, 100);
|
|
|
|
if (poll_ret <= 0) {
|
|
ALOGE("receive hci event timeout! ret=%d", poll_ret);
|
|
return -1;
|
|
}
|
|
ALOGD("poll ret=%d", poll_ret);
|
|
|
|
while (1) {
|
|
r = read(fd, buf, 1);
|
|
if (r <= 0)
|
|
return -1;
|
|
if (buf[0] == 0x04)
|
|
break;
|
|
}
|
|
count++;
|
|
|
|
while (count < 3) {
|
|
r = read(fd, buf + count, 3 - count);
|
|
if (r <= 0)
|
|
return -1;
|
|
count += r;
|
|
}
|
|
|
|
if (buf[2] < (size - 3))
|
|
remain = buf[2];
|
|
else
|
|
remain = size - 3;
|
|
|
|
while ((count - 3) < remain) {
|
|
r = read(fd, buf + count, remain - (count - 3));
|
|
if (r <= 0)
|
|
return -1;
|
|
count += r;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function h5_read_vendor_event
|
|
**
|
|
** Description h5_read_vendor_event
|
|
**
|
|
** Returns count
|
|
**
|
|
*******************************************************************************/
|
|
static int h5_read_vendor_event(int fd, unsigned char* buf, int size)
|
|
{
|
|
int r;
|
|
int count = 0;
|
|
|
|
if (size <= 0)
|
|
return -1;
|
|
|
|
struct pollfd pfd;
|
|
int poll_ret;
|
|
pfd.fd = fd;
|
|
pfd.events = POLLIN | POLLHUP;
|
|
|
|
poll_ret = poll(&pfd, 1, 100);
|
|
|
|
if (poll_ret <= 0) {
|
|
ALOGE("receive hci event timeout! ret=%d", poll_ret);
|
|
return -1;
|
|
}
|
|
ALOGD("poll ret=%d", poll_ret);
|
|
|
|
while (1) {
|
|
r = read(fd, buf, 1);
|
|
if (r <= 0)
|
|
return -1;
|
|
if (buf[0] == 0xc0)
|
|
break;
|
|
}
|
|
count++;
|
|
|
|
while (count < 2) {
|
|
r = read(fd, buf + count, 2 - count);
|
|
if (r <= 0)
|
|
return -1;
|
|
count += r;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static int h5_send_vendor_cmd(int fd, unsigned char* cmd, int size)
|
|
{
|
|
if (do_write(fd, cmd, size) != size)
|
|
{
|
|
ALOGE("cmd send is error");
|
|
goto error;
|
|
}
|
|
return 0;
|
|
error:
|
|
return 1;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function hci_vendor_reset
|
|
**
|
|
** Description hci_reset
|
|
**
|
|
** Returns int
|
|
**
|
|
*******************************************************************************/
|
|
static int start_vendor_cmd(int fd, unsigned char* cmd, int size)
|
|
{
|
|
int rsp_size = 0;
|
|
|
|
unsigned char rsp[HCI_MAX_EVENT_SIZE];
|
|
|
|
memset(rsp, 0x0, HCI_MAX_EVENT_SIZE);
|
|
if (do_write(fd, cmd, size) != size)
|
|
{
|
|
ALOGE("cmd send is error");
|
|
goto error;
|
|
}
|
|
|
|
rsp_size = read_vendor_event(fd, rsp, HCI_MAX_EVENT_SIZE);
|
|
|
|
if (rsp_size < 0)
|
|
{
|
|
ALOGE("read vendor event error");
|
|
goto error;
|
|
}
|
|
|
|
if (!check_event(rsp, rsp_size, cmd))
|
|
{
|
|
ALOGE("rsp event is error");
|
|
goto error;
|
|
}
|
|
return 0;
|
|
error:
|
|
return 1;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function get_vendor_info
|
|
**
|
|
** Description get_vendor_info
|
|
**
|
|
** Returns str
|
|
**
|
|
*******************************************************************************/
|
|
static unsigned char * get_vendor_info(int fd, unsigned char * cmd, int size, \
|
|
unsigned char * event, int *event_size)
|
|
{
|
|
int rsp_size = 0;
|
|
unsigned char rsp[HCI_MAX_EVENT_SIZE];
|
|
|
|
ALOGD();
|
|
|
|
memset(rsp, 0x0, HCI_MAX_EVENT_SIZE);
|
|
if (do_write(fd, cmd, size) != size)
|
|
{
|
|
ALOGE("cmd send is error");
|
|
goto error;
|
|
}
|
|
|
|
rsp_size = read_vendor_event(fd, rsp, HCI_MAX_EVENT_SIZE);
|
|
|
|
if (rsp_size < 0)
|
|
{
|
|
ALOGE("get_vendor_info error");
|
|
goto error;
|
|
}
|
|
|
|
if (!check_event(rsp, rsp_size, cmd))
|
|
{
|
|
ALOGE("rsp event is error");
|
|
goto error;
|
|
}
|
|
|
|
*event_size = rsp_size;
|
|
memcpy(event, rsp, rsp_size);
|
|
|
|
return event;
|
|
error:
|
|
return NULL;
|
|
}
|
|
|
|
static bool matching_mfrs_vid(const unsigned char *buf, int size)
|
|
{
|
|
bool ret = false;
|
|
unsigned int vid = 0;
|
|
|
|
if (size >= 13) {
|
|
vid = (((unsigned int)buf[11])<< 8) | ((unsigned int)buf[12]);
|
|
} else {
|
|
if ((buf[0] == 0xc0) && (buf[1] == 0x00)) {
|
|
vid = BT_VID_REALTECK;
|
|
}
|
|
}
|
|
|
|
if (matching_dev_id_uart(vid)) {
|
|
ret = true;
|
|
} else {
|
|
ALOGE("vendor id don't match :0x%04x", vid);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function userial_vendor_close
|
|
**
|
|
** Description Conduct vendor-specific close work
|
|
**
|
|
** Returns None
|
|
**
|
|
*******************************************************************************/
|
|
static void userial_vendor_close(void)
|
|
{
|
|
int result;
|
|
|
|
if (bt_uart_cb.fd == -1)
|
|
return;
|
|
|
|
#if (BT_WAKE_VIA_USERIAL_IOCTL==TRUE)
|
|
/* de-assert bt_wake BEFORE closing port */
|
|
ioctl(bt_uart_cb.fd, USERIAL_IOCTL_BT_WAKE_DEASSERT, NULL);
|
|
#endif
|
|
|
|
ALOGI("device fd = %d close", bt_uart_cb.fd);
|
|
// flush Tx before close to make sure no chars in buffer
|
|
tcflush(bt_uart_cb.fd, TCIOFLUSH);
|
|
if ((result = close(bt_uart_cb.fd)) < 0)
|
|
ALOGE( "close(fd:%d) FAILED result:%d", bt_uart_cb.fd, result);
|
|
|
|
bt_uart_cb.fd = -1;
|
|
}
|
|
|
|
static bool distinguish_bt_mod_uart_h4(void)
|
|
{
|
|
int fd;
|
|
int event_size = 0;
|
|
unsigned char event[HCI_MAX_EVENT_SIZE] = {'\0'};
|
|
bool ret = false;
|
|
|
|
ALOGD("start uart h4 init");
|
|
|
|
userial_vendor_init();
|
|
fd = userial_vendor_open((uart_cfg *) &uart_cfg_h4);
|
|
if (fd < 0) {
|
|
ALOGE("open uart h4 fail");
|
|
goto exit;
|
|
}
|
|
|
|
if (start_vendor_cmd(fd,(unsigned char *)vendor_reset, sizeof(vendor_reset))) {
|
|
ALOGE("start_vendor_cmd err");
|
|
goto exit;
|
|
}
|
|
|
|
if (get_vendor_info(fd, (unsigned char *)vendor_info, sizeof(vendor_info), event, &event_size) == NULL) {
|
|
ALOGE("get_vendor_info err");
|
|
goto exit;
|
|
}
|
|
|
|
if (matching_mfrs_vid(event, event_size)) {
|
|
ALOGI("uart h4 matching vid success");
|
|
ret = true;
|
|
} else {
|
|
ALOGE("uart h4 matching vid fail");
|
|
}
|
|
|
|
exit:
|
|
userial_vendor_close();
|
|
return ret;
|
|
}
|
|
|
|
static bool distinguish_bt_mod_uart_h5(void)
|
|
{
|
|
int fd;
|
|
int event_size = 0;
|
|
unsigned char event[HCI_MAX_EVENT_SIZE] = {'\0'};
|
|
bool ret = false;
|
|
|
|
ALOGD("start uart h5 init");
|
|
|
|
userial_vendor_init();
|
|
fd = userial_vendor_open((uart_cfg *) &uart_cfg_h5);
|
|
if (fd < 0) {
|
|
ALOGE("open uart h5 fail");
|
|
goto exit;
|
|
}
|
|
|
|
if (h5_send_vendor_cmd(fd, (unsigned char *)vendor_sync, sizeof(vendor_sync))) {
|
|
ALOGE("h5_send_vendor_cmd err");
|
|
goto exit;
|
|
}
|
|
|
|
event_size = h5_read_vendor_event(fd, event, HCI_MAX_EVENT_SIZE);
|
|
if (event_size < 0) {
|
|
ALOGE("h5_read_vendor_event err");
|
|
goto exit;
|
|
}
|
|
|
|
if (matching_mfrs_vid(event, event_size)) {
|
|
ALOGI("uart h5 matching vid success");
|
|
ret = true;
|
|
goto exit;
|
|
} else {
|
|
ALOGE("uart h5 matching vid fail");
|
|
}
|
|
|
|
exit:
|
|
userial_vendor_close();
|
|
return ret;
|
|
}
|
|
|
|
static bool distinguish_bt_mod_uart(void)
|
|
{
|
|
bool ret;
|
|
|
|
ret = distinguish_bt_mod_uart_h4();
|
|
if (!ret) {
|
|
ret = distinguish_bt_mod_uart_h5();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int distinguish_bt_mod(void)
|
|
{
|
|
ALOGD();
|
|
|
|
if (amlbt_get_name()) {
|
|
return 0;
|
|
}
|
|
|
|
if (distinguish_bt_mod_pci()) {
|
|
return 0;
|
|
}
|
|
|
|
if (distinguish_bt_mod_sdio()) {
|
|
return 0;
|
|
}
|
|
|
|
if (distinguish_bt_mod_usb()) {
|
|
return 0;;
|
|
}
|
|
|
|
#ifdef DISTINGUISH_BT_MOD_UART
|
|
if (distinguish_bt_mod_uart()) {
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int insmod(const char *filename, const char *args)
|
|
{
|
|
int fd, ret;
|
|
|
|
if (!filename) {
|
|
ALOGE("parameters err");
|
|
return -1;
|
|
}
|
|
|
|
fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
|
|
if (fd < 0) {
|
|
ALOGE("Failed to open %s", filename);
|
|
return -1;
|
|
}
|
|
|
|
ret = syscall(__NR_finit_module, fd, args, 0);
|
|
if (ret != 0) {
|
|
if (errno == EEXIST) {
|
|
ALOGD("module already loaded: %s", filename);
|
|
ret = 0;
|
|
} else {
|
|
ALOGE("finit_module failed: %s", strerror(errno));
|
|
}
|
|
} else {
|
|
ALOGD("insmod ok: %s %s", filename, args ? args : "");
|
|
usleep(100000);
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* name: Module name (not a path, for example, after insmod xxx.ko, pass "xxx" here)
|
|
* flags: Passing 0 is usually sufficient; If you want to force uninstallation of
|
|
* transferable O-TRUNC (i.e. KMOD-REMOVE-FORCE).
|
|
*/
|
|
static int rmmod(const char *name, int flags)
|
|
{
|
|
int ret;
|
|
|
|
if (!name) {
|
|
ALOGE("parameters err");
|
|
return -1;
|
|
}
|
|
|
|
ret = syscall(__NR_delete_module, name, flags);
|
|
if (ret != 0) {
|
|
ALOGE("remove module %s fail, %s", name, strerror(errno));
|
|
} else {
|
|
ALOGI("remove module %s success", name);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int amlbt_drv_insmod(void)
|
|
{
|
|
size_t i, j, k;
|
|
int ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(aml_drv); i++) {
|
|
if (strcmp(amlbt_dev.dev_name, aml_drv[i].dev_name) != 0)
|
|
continue;
|
|
|
|
ALOGD("Matched dev_name: %s", aml_drv[i].dev_name);
|
|
|
|
glob_t wifi_glob, bt_glob;
|
|
ret = glob(WIFI_DRV_PATH, 0, NULL, &wifi_glob);
|
|
if (ret != 0) {
|
|
ALOGE("Can't find WiFi driver path: %s", WIFI_DRV_PATH);
|
|
return 0;
|
|
}
|
|
|
|
ret = glob(BT_DRV_PATH, 0, NULL, &bt_glob);
|
|
if (ret != 0) {
|
|
ALOGE("Can't find BT driver path: %s", BT_DRV_PATH);
|
|
globfree(&wifi_glob);
|
|
return 0;
|
|
}
|
|
|
|
for (j = 0; j < MOD_MAX_NUM; j++) {
|
|
if (strlen(aml_drv[i].mod[j].mod_name) == 0)
|
|
continue;
|
|
|
|
int found = 0;
|
|
glob_t *pglob = (j == 0) ? &wifi_glob : &bt_glob;
|
|
|
|
for (k = 0; k < pglob->gl_pathc; k++) {
|
|
char ko_path[BUF_MAX_LEN * 2];
|
|
snprintf(ko_path, sizeof(ko_path), "%s/%s.ko",
|
|
pglob->gl_pathv[k],
|
|
aml_drv[i].mod[j].mod_name);
|
|
|
|
if (access(ko_path, F_OK) == 0) {
|
|
ALOGD("Found drv: %s", ko_path);
|
|
insmod(ko_path, aml_drv[i].mod[j].mod_arg);
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
ALOGE("Can't find module: %s, skip to next", aml_drv[i].mod[j].mod_name);
|
|
}
|
|
}
|
|
|
|
globfree(&wifi_glob);
|
|
globfree(&bt_glob);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amlbt_drv_modprobe(void)
|
|
{
|
|
int i, j;
|
|
char cmd[BUF_MAX_LEN] = {0};
|
|
|
|
for (i = 0; i < ARRAY_SIZE(aml_drv); i++) {
|
|
if (strcmp(amlbt_dev.dev_name, aml_drv[i].dev_name) != 0) {
|
|
continue;
|
|
}
|
|
|
|
ALOGD("Matched dev_name: %s", aml_drv[i].dev_name);
|
|
|
|
for (j = 0; j < MOD_MAX_NUM; j++) {
|
|
if (strlen(aml_drv[i].mod[j].mod_name) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (strlen(aml_drv[i].mod[j].mod_arg) > 0) {
|
|
snprintf(cmd, sizeof(cmd), "modprobe %s %s", aml_drv[i].mod[j].mod_name,
|
|
aml_drv[i].mod[j].mod_arg);
|
|
} else {
|
|
snprintf(cmd, sizeof(cmd), "modprobe %s", aml_drv[i].mod[j].mod_name);
|
|
}
|
|
|
|
ALOGD("exec: %s", cmd);
|
|
|
|
int sys_ret = system(cmd);
|
|
if (sys_ret != 0) {
|
|
ALOGE("modprobe: %s fail", cmd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void amlbt_drv_rmmod(void)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(aml_drv); i++) {
|
|
if (strcmp(amlbt_dev.dev_name, aml_drv[i].dev_name) != 0)
|
|
continue;
|
|
|
|
ALOGD("Matched dev_name: %s", aml_drv[i].dev_name);
|
|
|
|
if (aml_drv[i].mod[1].mod_name) {
|
|
rmmod(aml_drv[i].mod[1].mod_name, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
int steps_table_poll(void)
|
|
{
|
|
int ret = -1;
|
|
int i;
|
|
|
|
// todo: get steps from conf
|
|
|
|
for (i = 0; steps_table_exp[i].tag_ops; i++) {
|
|
ret = steps_table_exp[i].tag_ops();
|
|
if (ret != 0) {
|
|
ALOGE("fail");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|