// Copyright (c) 2010, Atmel Corporation.
// 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 Atmel 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
#include <signal.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <linux/netlink.h>
#include <cutils/properties.h>
#include <hardware_legacy/power.h>
#include <stdlib.h>
#include <time.h>
#include <linux/capability.h>
#include <linux/prctl.h>
#include <private/android_filesystem_config.h>
#include <linux/spi/cpcap.h>
#include <linux/hidraw.h>
#include "SA_Phys_Linux.h"
#include "SHA_CommMarshalling.h"
#include "SHA_Status.h"
#include "SHA_Comm.h"
#include "SHA_TimeUtils.h"
#include "Whisper_AccyMain.h"
/*==================================================================================================
LOCAL CONSTANTS
==================================================================================================*/
static const char xintToChar[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
static const char wakeLockString[] = "ACCYDET";
static const uint8_t hidStatusQuery[] = {0x3f, 0x30, 0x00, 0x00, 0x42, 0x99};
static const uint8_t hidIdQuery[] = {0x3f, 0x12, 0x00, 0x01, 0xf1, 0xe2};
/*==================================================================================================
LOCAL MACROS
==================================================================================================*/
#define MAX_TRY_WAKEUP 4
#define MAX_TRY_COMM 2
#define UART_SWITCH_STATE_PATH "/sys/class/switch/whisper/state"
#define HID_SWITCH_STATE_PATH "/sys/class/switch/whisper_hid/state"
#define DOCK_TYPE_OFFSET 27
#define DOCK_ATTACHED '1'
#define DOCK_NOT_ATTACHED '0'
#define HID_SUCCESS 1
#define HID_FAILURE 0
#define ID_SUCCESS 1
#define IOCTL_SUCCESS 0
#define MAX_TRY_IOCTL 5
#define HID_STATUS_QUERY_LENGTH 64
#define HID_ID_QUERY_LENGTH 64
#define HID_MAC_QUERY_LENGTH 64
#define HID_STATUS_MSG_LENGTH 64
#define HID_ID_MSG_LENGTH 64
#define HID_MAC_MSG_LENGTH 64
#define LOG_FILE_NAME "/data/whisper/whisperd.log"
#define LOG_FILE_PATH "/data/whisper"
/*==================================================================================================
EXTERNAL VARIABLES
==================================================================================================*/
/*==================================================================================================
LOCAL FUNCTION PROTOTYPES
==================================================================================================*/
static void copyResults(int8_t cmdSize, uint8_t *out);
static int accyInit(void);
static void accySigHandler(signed int signal);
static void accyProtDaemon(void *arg);
static void readSwitchState(int);
static int accySpawnThread();
static void createOutput(uint8_t *inp, char *out, int bytes);
static void waitForUevents();
static void doIoctl(int cmd, unsigned int data, char *dev_id, char *dev_prop);
/*==================================================================================================
LOCAL VARIABLES
==================================================================================================*/
static SHA_CommParameters* mainparms;
static sem_t SigAccyProtStart;
static int ueventFd;
static int wakeLock = 0;
static int globalState;
static int globalProtocol;
static int cpcapFd = -1;
static int hidFd = -1;
/*==================================================================================================
GLOBAL VARIABLES
==================================================================================================*/
FILE *logFp = NULL;
int currLogSize = 0;
/*==================================================================================================
LOCAL FUNCTIONS
==================================================================================================*/
static void readSwitchState(int protocolType) {
const int SIZE = 16;
int switchFd = -1;
char buf[SIZE];
int count;
if (protocolType == PROTOCOL_UART) {
switchFd = open(UART_SWITCH_STATE_PATH, O_RDONLY, 0);
if (switchFd == -1) {
DBG_ERROR("Failed opening %s, errno = %s", UART_SWITCH_STATE_PATH, strerror(errno));
return;
}
}
else if (protocolType == PROTOCOL_HID) {
switchFd = open(HID_SWITCH_STATE_PATH, O_RDONLY, 0);
if (switchFd == -1) {
DBG_ERROR("Failed opening %s, errno = %s", HID_SWITCH_STATE_PATH, strerror(errno));
return;
}
}
do {
count = read(switchFd, buf, SIZE);
} while (count < 0 && errno == EINTR);
if (count < 1) {
DBG_ERROR("Error reading switch, returned %d", count);
close(switchFd);
return;
}
if (buf[0] == DOCK_ATTACHED) {
globalState = GLOBAL_STATE_DOCKED;
if (protocolType == PROTOCOL_HID) {
char hidDevice[] = "/dev/hidraw0";
int i;
struct hidraw_devinfo hiddevinfo;
int status;
globalProtocol = PROTOCOL_HID;
DBG_TRACE("HID Dock Attached");
for (i = 0; i < HIDRAW_MAX_DEVICES; i++) {
hidFd = open(hidDevice, O_RDWR);
if(hidFd < 0) {
DBG_ERROR("Failed to open HID Device:%s", hidDevice);
break;
}
else {
status = ioctl(hidFd, HIDIOCGRAWINFO, &hiddevinfo);
if (status != IOCTL_SUCCESS) {
DBG_ERROR("ioctl cmd:HIDIOCGRAWINFO failed for %s", hidDevice);
}
else {
if(hiddevinfo.vendor == 0x22b8 && hiddevinfo.product == 0x0938) {
DBG_TRACE("Found HD Dock: %s", hidDevice);
break;
}
else {
DBG_ERROR("Device: %s not a HD dock", hidDevice);
close(hidDevice);
}
}
// TODO: You cant do this.
hidDevice[11]++;
}
}
hidDevice[11] = '0';
}
else if (protocolType == PROTOCOL_UART) {
globalProtocol = PROTOCOL_UART;
}
}
else if (buf[0] == DOCK_NOT_ATTACHED) {
globalState = GLOBAL_STATE_UNDOCKED;
}
close(switchFd);
}
static void waitForUevents() {
fd_set accySet;
char msg[1024];
int nready, status;
FD_ZERO(&accySet);
FD_SET(ueventFd, &accySet);
/* at powerup, we might have missed the uevent. So, read switch */
readSwitchState(PROTOCOL_HID);
if (globalState == GLOBAL_STATE_DOCKED) {
DBG_TRACE("HID Dock attached at Power up");
sem_post(&SigAccyProtStart);
}
else {
readSwitchState(PROTOCOL_UART);
if (globalState == GLOBAL_STATE_DOCKED) {
DBG_TRACE("UART Dock attached at Power up");
sem_post(&SigAccyProtStart);
}
}
while(1) {
nready = select(ueventFd+1, &accySet, NULL, NULL, NULL);
if (nready > 0) {
if (FD_ISSET(ueventFd, &accySet)) {
status = recv(ueventFd, msg, sizeof(msg), MSG_DONTWAIT);
if ((status > 0) && (strcasestr(msg, "whisper_hid")) && (strcasestr(msg, "switch"))) {
readSwitchState(PROTOCOL_HID);
DBG_TRACE("HID: SEM POST after readSwitchState %d", globalState);
sem_post(&SigAccyProtStart);
}
else if ((status > 0) && (strcasestr(msg, "whisper")) && (strcasestr(msg, "switch"))) {
readSwitchState(PROTOCOL_UART);
DBG_TRACE("UART: SEM POST after readSwitchState %d", globalState);
sem_post(&SigAccyProtStart);
}
}
}
else {
DBG_ERROR("Select errored out. nready = %d, errno = %s", nready, strerror(errno));
}
}
}
static int accySpawnThread() {
pthread_t id;
if (pthread_create(&id, NULL, (void *(*)(void *))accyProtDaemon, NULL) != 0) {
DBG_ERROR("Pthread create failed. errno = %s", strerror(errno));
return 0;
}
return 1;
}
/** Responsible for protocol communication */
static void accyProtDaemon(void *arg) {
struct sched_param sched;
int currentPolicy, tryWakeup, tryComm;
pthread_t threadId;
uint8_t wakeupSuccess;
unsigned int dockDetails;
uint8_t dockType = NO_DOCK;
int status;
struct timespec ts;
struct timeval tv;
uint8_t statusFuse[8], FSNo[8], RomSN[8], RomRNo[8];
char devInfo[32];
char devProp[8];
threadId = pthread_self();
status = pthread_getschedparam(threadId, ¤tPolicy, &sched);
if (status != 0) {
DBG_ERROR("pthread_getschedparam error. erno = %s", strerror(status));
return;
}
currentPolicy = SCHED_RR;
sched.sched_priority = 70;
status = pthread_setschedparam(threadId, currentPolicy, &sched);
if (status != 0) {
DBG_ERROR("pthread_setschedparam error. erno = %s", strerror(status));
return;
}
switchUser();
while(1) {
do {
status = sem_wait(&SigAccyProtStart);
} while (status < 0 && errno == EINTR);
if (status == -1) {
DBG_ERROR("SEM WAIT failed with -1. errno = %s", strerror(errno));
break;
}
/* If already undocked, why do anything */
if (globalState == GLOBAL_STATE_UNDOCKED) {
DBG_TRACE("Thread. GLOBAL_STATE_UNDOCKED");
continue;
}
if (globalProtocol == PROTOCOL_UART) {
doIoctl(CPCAP_IOCTL_ACCY_WHISPER, CPCAP_WHISPER_ENABLE_UART, NULL, NULL);
}
wakeLock = acquire_wake_lock(PARTIAL_WAKE_LOCK, wakeLockString);
tryComm = 1;
while (tryComm && (globalState == GLOBAL_STATE_DOCKED)) {
if (globalProtocol == PROTOCOL_UART) {
if (SHA_SUCCESS == SHAP_OpenChannel()) {
tryWakeup = 1;
wakeupSuccess = 0;
while (tryWakeup) {
if (SHAC_Wakeup() == SHA_SUCCESS) {
DBG_TRACE("WAKEUP SUCCESS %d ", tryWakeup);
tryWakeup = 0;
wakeupSuccess = 1;
}
else {
if (tryWakeup == MAX_TRY_WAKEUP) {
DBG_ERROR("GIVING UP WAKEUP after %d tries", tryWakeup);
tryWakeup = 0;
}
else {
DBG_TRACE("TRYING WAKEUP ONCE MORE");
ts.tv_sec = 0;
ts.tv_nsec = 10000000; // 10 ms
nanosleep(&ts, NULL);
tryWakeup++;
}
}
if (globalState == GLOBAL_STATE_UNDOCKED) {
tryWakeup = 0;
}
}
if ((wakeupSuccess) && (globalState == GLOBAL_STATE_DOCKED)) {
DBG_TRACE("Reading Status & MfgId");
// Read Status fuses and MfgId fuses
status = SHAC_Read(0x01, 0x0002);
if (status == SHA_SUCCESS) {
copyResults(8, statusFuse);
// TODO: bytes in wrong order for some reason??
uint8_t temp[2];
temp[0] = statusFuse[1];
temp[1] = statusFuse[3];
statusFuse[1] = temp[1];
statusFuse[3] = temp[0];
}
if (globalState == GLOBAL_STATE_DOCKED && status == SHA_SUCCESS) {
DBG_TRACE("Reading Serial Number");
// Read Fuse Serial number value
status = SHAC_Read(0x01, 0x0003);
if (status == SHA_SUCCESS) {
copyResults(8, FSNo);
}
}
if (globalState == GLOBAL_STATE_DOCKED && status == SHA_SUCCESS) {
DBG_TRACE("Reading ROM MfgId and ROM SN");
// ROM MfgId and ROM SN
status = SHAC_Read(0x00, 0x0000);
if (status == SHA_SUCCESS) {
copyResults(8, RomSN);
DBG_TRACE("Authentication succeed");
globalState = GLOBAL_STATE_DOCKED_IDSUCC;
}
}
}
}
SHAP_CloseChannel();
}
else if (globalProtocol == PROTOCOL_HID) {
uint8_t writebuff[65] = {0x0};
uint8_t readbuff[65] = {0x0};
uint8_t displaybuff[65] = {0x0};
int hidStatus;
DBG_TRACE("HID: Sending status query");
hidStatus = HID_FAILURE;
memset(writebuff,0x00,sizeof(writebuff));
memcpy(writebuff, hidStatusQuery, sizeof(hidStatusQuery));
status = write(hidFd, writebuff, HID_STATUS_QUERY_LENGTH);
if (status != HID_STATUS_QUERY_LENGTH) {
DBG_ERROR("Failed writing status query (errno = %s)", strerror(errno));
}
else {
hidStatus = HID_SUCCESS;
}
if (globalState == GLOBAL_STATE_DOCKED && hidStatus == HID_SUCCESS) {
DBG_TRACE("HID: Reading status query response");
status = read(hidFd, readbuff, HID_STATUS_MSG_LENGTH);
if (status != HID_STATUS_MSG_LENGTH) {
DBG_ERROR("HID: Failed reading status query response, errno = %s", strerror(errno));
hidStatus = HID_FAILURE;
}
else {
DBG_TRACE("Contents of receive buffer");
DBG_TRACE("first 3 bytes: %02X%02X%02X", readbuff[0], readbuff[1], readbuff[2]);
DBG_TRACE("Status: %02X%02X", readbuff[3], readbuff[4]);
DBG_TRACE("Ref Num: %02X%02X", readbuff[5], readbuff[6]);
DBG_TRACE("Version: %s", &readbuff[6]);
}
}
if (globalState == GLOBAL_STATE_DOCKED && hidStatus == HID_SUCCESS) {
DBG_TRACE("HID: Sending ID query");
memset(writebuff,0x00,sizeof(writebuff));
memcpy(writebuff, hidIdQuery, sizeof(hidIdQuery));
status = write(hidFd, writebuff, HID_ID_QUERY_LENGTH);
if (status != HID_ID_QUERY_LENGTH) {
DBG_ERROR("HID: Error writing ID query, %d", status);
hidStatus = HID_FAILURE;
}
}
if (globalState == GLOBAL_STATE_DOCKED && hidStatus == HID_SUCCESS) {
DBG_TRACE("Reading ID query response");
status = read(hidFd, readbuff, HID_ID_MSG_LENGTH);
if (status != HID_ID_MSG_LENGTH) {
DBG_ERROR("HID: Error reading ID query response, errno = %s", strerror(errno));
hidStatus = HID_FAILURE;
}
else {
DBG_TRACE("Contents of receive buffer");
DBG_TRACE("first 3-2 bytes: %02X%02X%02X", readbuff[0], readbuff[1], readbuff[2]);
DBG_TRACE("Status: %02X%02X", readbuff[3], readbuff[4]);
DBG_TRACE("Ref Num: %02X%02X", readbuff[5], readbuff[6]);
DBG_TRACE("SEMU ID: %02X%02X%02X", readbuff[7], readbuff[8], readbuff[9]);
DBG_TRACE("Manufacturer ID: %02X", readbuff[10]);
DBG_TRACE("ROM Revision: %02X%02X%02X%02X", readbuff[11], readbuff[12], readbuff[13], readbuff[14]);
DBG_TRACE("Fuse SN: %02X%02X%02X%02X", readbuff[15], readbuff[16], readbuff[17], readbuff[18]);
DBG_TRACE("ROM SN: %02X%02X", readbuff[19], readbuff[20]);
statusFuse[1] = readbuff[6];
statusFuse[2] = readbuff[7];
statusFuse[3] = readbuff[8];
FSNo[1] = readbuff[14];
FSNo[2] = readbuff[15];
FSNo[3] = readbuff[16];
FSNo[4] = readbuff[17];
RomSN[3] = readbuff[18];
RomSN[4] = readbuff[19];
globalState = GLOBAL_STATE_DOCKED_IDSUCC;
}
}
}
if (globalState == GLOBAL_STATE_DOCKED_IDSUCC) {
dockType = NO_DOCK;
if (statusFuse[1] == 0x0A && statusFuse[2] == 0xC0) {
dockType = LE_DOCK;
DBG_TRACE("It's a Low End Dock");
}
else if (statusFuse[1] == 0x12 && statusFuse[2] == 0xC0 && statusFuse[3] == 0x00) {
dockType = CAR_DOCK;
DBG_TRACE("It's a Car Dock");
}
else if (statusFuse[1] == 0x1A && statusFuse[2] == 0x80 && statusFuse[3] == 0x00) {
dockType = HE_DOCK;
DBG_TRACE("It's a High End Dock");
}
/* Format the output */
createOutput(&FSNo[1],&devInfo[0], 4);
createOutput(&RomSN[3],&devInfo[8], 2);
devInfo[12] = 0;
createOutput(&statusFuse[1], &devProp[0], 3);
devProp[6] = 0;
DBG_TRACE("ID SUCCESS %s", devInfo);
if(dockType == NO_DOCK)
DBG_TRACE("UNKNOWN STATUS FUSES <%d><%d><%d>\n", statusFuse[1], statusFuse[2], statusFuse[3]);
tryComm = 0;
dockDetails = ID_SUCCESS;
dockDetails |= (dockType << DOCK_TYPE_OFFSET);
doIoctl(CPCAP_IOCTL_ACCY_WHISPER, dockDetails, devInfo, devProp);
globalState = GLOBAL_STATE_DOCKED_IDSUCC;
memset(devInfo,0x00,sizeof(devInfo));
memset(devProp,0x00,sizeof(devProp));
}
/* if the global state is still docked, then increment the retry counter */
if (globalState == GLOBAL_STATE_DOCKED) {
if (tryComm == MAX_TRY_COMM) {
DBG_ERROR("GIVING UP AFTER %d tries", tryComm);
tryComm = 0;
dockDetails = 0; // set bit 0, for AUTH to be failure
doIoctl(CPCAP_IOCTL_ACCY_WHISPER, dockDetails, NULL, NULL);
globalState = GLOBAL_STATE_DOCKED_IDFAIL;
}
else {
ts.tv_sec = 0;
ts.tv_nsec = 100000000; // 100 ms
nanosleep(&ts, NULL);
tryComm++;
DBG_TRACE("Trying COMM %d time", tryComm);
}
}
}
if (wakeLock) {
release_wake_lock(wakeLockString);
wakeLock = 0;
}
}
}
static void createOutput(uint8_t *inp, char *out, int bytes) {
int i, j;
for (i = 0; i < bytes; i++) {
j = (inp[i] & 0x0F);
out[i*2+1] = xintToChar[j];
j = ((inp[i] >> 4) & 0xFF);
out[i*2] = xintToChar[j];
}
}
static void accySigHandler(signed int signal) {
switch(signal) {
case SIGINT:
case SIGKILL:
case SIGTERM:
/* cose fds */
if (cpcapFd > 0)
close(cpcapFd);
if (ttyFd > 0)
close(ttyFd);
if (hidFd > 0)
close(hidFd);
exit(0);
break;
default:
break;
}
}
static int accyInit(void) {
struct sockaddr_nl addr;
int sz = 64*1024;
int s;
struct sigaction shutdownAction;
struct stat statBuf;
#if defined(LOG_ACCY_FS)
if(stat(LOG_FILE_PATH, &statBuf) == 0) {
logFp = fopen(LOG_FILE_NAME, "w");
if (logFp == NULL) {
ALOGE("whisperd: Unable to open the Logfile %s", LOG_FILE_NAME);
}
}
#endif
cpcapFd = open("/dev/cpcap", O_RDWR);
if (cpcapFd == -1) {
DBG_ERROR("/dev/cpcap could not be opened. err = %s", strerror(errno));
}
else {
DBG_TRACE("/dev/cpcap opened: %d", cpcapFd);
}
/* Setup the shutdown action. */
shutdownAction.sa_handler = accySigHandler;
sigemptyset(&shutdownAction.sa_mask);
shutdownAction.sa_flags = 0;
/* Setup the signal handler. */
sigaction(SIGINT, &shutdownAction, NULL);
sigaction(SIGKILL, &shutdownAction, NULL);
sigaction(SIGTERM, &shutdownAction, NULL);
globalState = GLOBAL_STATE_UNDOCKED;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = 0xffffffff;
s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (s < 0) {
DBG_ERROR("Socket failed. err = %s", strerror(errno));
return 0;
}
setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
DBG_ERROR("Bind failed. errno = %d", errno);
close(s);
return 0;
}
ueventFd = s;
return (ueventFd > 0);
}
void doIoctl(int cmd, unsigned int data, char *dev_id, char *dev_prop) {
int i, status = -1;
struct timespec ts;
struct cpcap_whisper_request req;
memset(req.dock_id, 0, CPCAP_WHISPER_ID_SIZE);
req.cmd = data;
if(dev_id != NULL)
strcpy(req.dock_id, dev_id);
if(dev_prop != NULL)
strcpy(req.dock_prop, dev_prop);
for (i = 0; i < MAX_TRY_IOCTL; i++) {
DBG_TRACE("ioctl cmd %d: %d,", cmd, data);
if(dev_id == NULL) {
DBG_TRACE("ioctl id = NULL");
}
else {
DBG_TRACE("ioctl id = <%s>\n", req.dock_id);
}
status = ioctl(cpcapFd, cmd, &req);
if (status != IOCTL_SUCCESS) {
DBG_ERROR("ioctl returned %d with error: %d", status, errno);
ts.tv_sec = 0;
ts.tv_nsec = 50000000; // 50 ms
DBG_TRACE("Wait 50ms.");
nanosleep(&ts, NULL);
globalState = GLOBAL_STATE_UNDOCKED;
}
else {
DBG_TRACE("ioctl success");
globalState = GLOBAL_STATE_DOCKED;
break;
}
}
if (hidFd > 0) {
close(hidFd);
hidFd = -1;
}
return;
}
static void copyResults(int8_t cmdSize, uint8_t *out) {
int i;
char charOut[128];
mainparms = SHAC_GetData();
createOutput(mainparms->txBuffer, charOut, cmdSize);
charOut[cmdSize*2] = '\0';
DBG_TRACE("Send Value: %s", charOut);
for(i = 0; i < mainparms->rxSize; i++) {
out[i] = mainparms->rxBuffer[i];
}
createOutput(mainparms->rxBuffer, charOut, cmdSize);
charOut[cmdSize*2] = '\0';
DBG_TRACE("Receive Value: = %s", charOut);
}
int main(int argc, char *argv[]) {
int retVal;
retVal = accyInit();
if (retVal <= 0) {
DBG_ERROR("accyInit failed");
}
if (sem_init(&SigAccyProtStart, 0, 0) != 0) {
DBG_ERROR("Sem_init failed. errno = %d", errno);
}
//TODO: First time failure to set parameters
SHAP_OpenChannel();
SHAP_CloseFile();
retVal = accySpawnThread();
switchUser();
waitForUevents();
return 1;
}
int switchUser( void ) {
int status;
status = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
if (status < 0) {
return status;
}
status = setuid(AID_RADIO);
if (status < 0) {
return status;
}
struct __user_cap_header_struct header;
struct __user_cap_data_struct cap;
header.version = _LINUX_CAPABILITY_VERSION;
header.pid = 0;
cap.effective = cap.permitted = 1 << CAP_NET_ADMIN;
cap.inheritable = 0;
status = capset(&header, &cap);
return status;
}