390 lines
12 KiB
C
390 lines
12 KiB
C
/*
|
|
* 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 = -1, 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;
|
|
}
|
|
|