FM power on/off sequence changes for cherokee
This include adding fmhal service for access RFKill driver
to do power ON/OFF operations during FM enable/disable.
Change-Id: I77c100533ca47bf0e53bccdbb1d93c08058df4bf
diff --git a/fmhalService/main.c b/fmhalService/main.c
new file mode 100644
index 0000000..558f681
--- /dev/null
+++ b/fmhalService/main.c
@@ -0,0 +1,390 @@
+/*
+ * 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;
+}
+