blob: 558f681539f4cd4ca3dcbf465413123019e55147 [file] [log] [blame]
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* FMhal service used to access RFKILL
**/
#include <cutils/log.h>
#include <sys/socket.h>
#include <cutils/sockets.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/un.h>
#include <sys/eventfd.h>
#include <errno.h>
#include <string.h>
#include <cutils/properties.h>
#include "private/android_filesystem_config.h"
//#include "bt_hci_bdroid.h"
#include "bt_vendor_lib.h"
#include "fm_hci.h"
#include "wcnss_fmhci.h"
#include <dlfcn.h>
#define LOG_TAG "FMHalService"
#define FM_HAL_SOCK "fmhal_sock"
#define BT_SSR_TRIGGERED 0xee
#define FM_POWER_OFF 0x01
#define FM_POWER_ON 0x02
#define FM_USERIAL_OPEN 0x03
#define FM_USERIAL_CLOSE 0x04
#define FM_CMD_PACKET_TYPE 0x11
#define FM_EVT_PACKET_TYPE 0x14
#ifndef BLUETOOTH_UID
#define BLUETOOTH_UID 1002
#endif
#ifndef SYSTEM_UID
#define SYSTEM_UID 1000
#endif
#ifndef ROOT_UID
#define ROOT_UID 0
#endif
pthread_mutex_t signal_mutex;
bt_vendor_interface_t *fm_if = NULL;
int remote_fm_hal_fd;
int do_write(int fd, unsigned char *buf,int len);
unsigned char reset_cmpl[] = {0x04, 0x0e, 0x04, 0x01,0x03, 0x0c, 0x00};
static int extract_uid(int uuid)
{
int userid;
int appid;
appid = userid = uuid % AID_USER;
if (userid > BLUETOOTH_UID)
{
appid = userid % AID_APP;
}
ALOGD("%s appid = %d",__func__,appid);
return appid;
}
void service_cleanup()
{
char ref_count[PROPERTY_VALUE_MAX];
char cleanup[PROPERTY_VALUE_MAX];
int ref_val,clean;
ALOGE("Service is stopped ");
property_get("wc_transport.clean_up", cleanup, "0");
property_set("wc_transport.fm_service_status", "0");
property_set("wc_transport.start_fmhci", "0");
property_set("wc_transport.fm_power_status", "0");
clean = atoi(cleanup);
ALOGE("clean Value = %d",clean);
}
static int establish_fm_remote_socket(char *name)
{
int fd = -1;
struct sockaddr_un client_address;
socklen_t clen;
int sock_id, ret;
struct ucred creds;
int c_uid;
ALOGI("%s(%s) Entry ", __func__, name);
sock_id = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sock_id < 0) {
ALOGE("%s: server Socket creation failure", __func__);
return fd;
}
ALOGI("convert name to android abstract name:%s %d", name, sock_id);
if (socket_local_server_bind(sock_id,
name, ANDROID_SOCKET_NAMESPACE_ABSTRACT) >= 0) {
if (listen(sock_id, 5) == 0) {
ALOGI("listen to local socket:%s, fd:%d", name, sock_id);
} else {
ALOGE("listen to local socket:failed");
close(sock_id);
return fd;
}
} else {
close(sock_id);
ALOGE("%s: server bind failed for socket : %s", __func__, name);
return fd;
}
clen = sizeof(client_address);
/*Indicate that, server is ready to accept*/
property_set("wc_transport.fm_service_status", "1");
ALOGI("%s: wc_transport.fm_service_status set to 1 ", __func__);
ALOGI("%s: before accept_server_socket", name);
fd = accept(sock_id, (struct sockaddr *)&client_address, &clen);
if (fd > 0) {
ALOGI("%s accepted fd:%d for server fd:%d", name, fd, sock_id);
close(sock_id);
memset(&creds, 0, sizeof(creds));
socklen_t szCreds = sizeof(creds);
ret = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
if (ret < 0) {
ALOGE("%s: error getting remote socket creds: %d\n", __func__, ret);
close(fd);
return -1;
}
c_uid = creds.uid;
if (c_uid > BLUETOOTH_UID)
c_uid = extract_uid(creds.uid);
if (c_uid != BLUETOOTH_UID && c_uid != SYSTEM_UID
&& c_uid != ROOT_UID) {
ALOGE("%s: client doesn't have required credentials", __func__);
ALOGE("<%s req> client uid: %d", name, creds.uid);
close(fd);
return -1;
}
ALOGI("%s: Remote socket credentials: %d\n", __func__, creds.uid);
return fd;
} else {
ALOGE("BTC accept failed fd:%d sock d:%d error %s", fd, sock_id, strerror(errno));
close(sock_id);
return fd;
}
close(sock_id);
return fd;
}
int handle_fmcommand_writes(int fd) {
ALOGI("%s: ", __func__);
unsigned char first_byte;
int retval,val;
int fd_array[CH_MAX];
ALOGE("%s: FMHAL: read 1st byte to determine the power on/off ", __func__);
retval = read (fd, &first_byte, 1);
if (retval < 0) {
ALOGE("%s:read returns err: %d\n", __func__,retval);
return -1;
}
if (retval == 0) {
ALOGE("%s: This indicates the close of other end", __func__);
return -99;
}
ALOGE("%s: FM command type: 0x%x", __func__, first_byte);
switch(first_byte) {
case FM_POWER_OFF:
ALOGE("%s: Received power off command from FM stack: %d", __func__, first_byte);
val = 0;
retval = fm_if->op(BT_VND_OP_POWER_CTRL, &val);
if (retval < 0)
{
ALOGE("Failed to turn off power from bt vendor interface");
// return -1;
}
else {
property_set("wc_transport.fm_power_status", "0");
retval = -99;
}
break;
case FM_POWER_ON:
ALOGE("%s: Received power ON command from FM stack: %d", __func__, first_byte);
val = 1;
retval =fm_if->op(FM_VND_OP_POWER_CTRL, &val);
if (retval < 0)
{
ALOGE("Failed to turn on power from bt vendor interface");
}
else
property_set("wc_transport.fm_power_status", "1");
break;
default:
ALOGE("%s: Unexpected data format!!",__func__);
retval = -1;
}
return retval;
}
int do_read(int fd, unsigned char* buf, size_t len) {
int bytes_left, bytes_read = 0, read_offset;
bytes_left = len;
read_offset = 0;
do {
bytes_read = read(fd, buf+read_offset, bytes_left);
if (bytes_read < 0) {
ALOGE("%s: Read error: %d (%s)", __func__, bytes_left, strerror(errno));
return -1;
} else if (bytes_read == 0) {
ALOGE("%s: read returned 0, err = %s, read bytes: %d, expected: %d",
__func__, strerror(errno), (len-bytes_left), len);
return (len-bytes_left);
}
else {
if (bytes_read < bytes_left) {
ALOGV("Still there are %d bytes to read", bytes_left-bytes_read);
bytes_left = bytes_left-bytes_read;
read_offset = read_offset+bytes_read;
} else {
ALOGV("%s: done with read",__func__);
break;
}
}
}while(1);
return len;
}
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("%s: write failed ret = %d err = %s",__func__,ret,strerror(errno));
return -1;
} else if (ret == 0) {
ALOGE("%s: Write returned 0, err = %s, Written bytes: %d, expected: %d",
__func__, strerror(errno), (len-write_len), len);
return (len-write_len);
} else {
if (ret < write_len)
{
ALOGD("%s, Write pending,do write ret = %d err = %s",__func__,ret,
strerror(errno));
write_len = write_len - ret;
write_offset = ret;
} else if (ret > write_len) {
ALOGE("%s: FATAL wrote more than expected: written bytes: %d expected: %d",
__func__, write_len, ret);
break;
} else {
ALOGV("Write successful");
break;
}
}
} while(1);
return len;
}
void vnd_load_if()
{
void *dlhandle;
unsigned char bdaddr[] = {0xaa, 0xbb, 0xcc, 0x11, 0x22, 0x33};
dlhandle = dlopen("libbt-vendor.so", RTLD_NOW);
if (!dlhandle)
{
ALOGE("!!! Failed to load libbt-vendor.so !!!");
return;
}
fm_if = (bt_vendor_interface_t *) dlsym(dlhandle, "BLUETOOTH_VENDOR_LIB_INTERFACE");
if (!fm_if)
{
ALOGE("!!! Failed to get bt vendor interface !!!");
return;
}
ALOGI("FM-HCI: Registering the WCNSS HAL library by passing CBs and BD addr.");
fm_if->init(&fmhci_vendor_callbacks, bdaddr);
}
int main() {
fd_set client_fds;
int retval, n;
ALOGI("%s: Entry ", __func__);
ALOGI("FM HAL SERVICE: Loading the WCNSS HAL library...");
vnd_load_if();
ALOGI("create socket");
remote_fm_hal_fd = establish_fm_remote_socket(FM_HAL_SOCK);
if (remote_fm_hal_fd < 0) {
ALOGE("%s: invalid remote socket", __func__);
return -1;
}
FD_ZERO(&client_fds);
FD_SET(remote_fm_hal_fd, &client_fds);
do {
ALOGI("%s: Step 1-FM-HAL SERVICE: Waiting for FM HAL cmd ", __func__);
n = select(remote_fm_hal_fd+1, &client_fds, NULL, NULL, NULL);
if(n < 0){
ALOGE("Select: failed: %s", strerror(errno));
break;
}
ALOGI("%s: Step 2-FM-HAL SERVICE: FM POWER CMD available for processing...\n", __func__);
if (FD_ISSET(remote_fm_hal_fd, &client_fds)) {
retval = handle_fmcommand_writes(remote_fm_hal_fd);
ALOGI("%s: handle_fmcommand_writes . %d", __func__, retval);
if(retval < 0) {
if (retval == -99) {
ALOGI("%s:End of wait loop", __func__);
break;
}
ALOGI("%s: handle_fmcommand_writes returns: %d: ", __func__, retval);
// break;
}
}
} while(1);
service_cleanup();
ALOGI("%s: FM turned off or power off failed .service kill itself", __func__);
close(remote_fm_hal_fd);
remote_fm_hal_fd = 0;
ALOGI("%s: Exit: %d", __func__, retval);
return retval;
}