blob: 3a541fe590b3f586870251d63f10da9c8cac55e4 [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2011 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
/*--------------------------------------------------------------------------------------------------------------------
* -------------------------------------------------------------------------------------------------------------------
*
* Filename: command_api.c
*
* Description: The APIs to remotely control DIAG internal behaviors.
*
* History:
* Aug, 13 2012 - Haili Wang(hlw@marvell.com) Creation of file
*
* Notes:
*
******************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#ifdef ANDROID
#include <cutils/sockets.h>
#endif
#include "utlEventHandler.h"
#include "media_manager.h"
#include "diag_state_machine.h"
#include "sd_api.h"
#include "pxa_dbg.h"
#include "diag_al.h"
#include "diag_api.h"
static const char response_error[] = "ERROR";
static const char response_invalid[] = "INVALID";
static const char response_success[] = "SUCCESS";
static int cmdfd = -1;
static utlEventHandlerId_T cmdHandler;
struct command_client{
int fd;
utlEventHandlerId_T handle_id;
};
#define MAX_CLIENT_NUMBER 10
static struct command_client clients[MAX_CLIENT_NUMBER];
#ifdef ANDROID
#define DIAG_CMD_SOCKET "diagcmd"
#else
#define DIAG_CMD_SOCKET "/tmp/diagcmd"
#endif
static void init_clients(void)
{
int i;
for(i = 0; i < MAX_CLIENT_NUMBER; i ++)
{
clients[i].fd = -1;
clients[i].handle_id = 0;
}
}
static int client_id_is_valid(int id)
{
if((id >= 0) && (id < MAX_CLIENT_NUMBER))
return 1;
else
return 0;
}
static void close_client(int i)
{
if(client_id_is_valid(i))
{
if(clients[i].handle_id > 0)
utlDeleteEventHandler(clients[i].handle_id);
if(clients[i].fd > 0)
close(clients[i].fd);
clients[i].handle_id = 0;
clients[i].fd = -1;
}
}
static void close_clients(void)
{
int i;
for(i = 0; i < MAX_CLIENT_NUMBER; i ++)
{
close_client(i);
}
}
static int get_free_client_id(void)
{
int i;
for(i = 0; i < MAX_CLIENT_NUMBER; i ++)
{
if(clients[i].fd == -1)
return i;
}
return -1;
}
static int get_client_id_by_fd(int fd)
{
int i;
if(fd < 0)
return -1;
for(i = 0; i < MAX_CLIENT_NUMBER; i ++)
{
if(clients[i].fd == fd)
{
return i;
}
}
return -1;
}
static int sendReply(int fd, const char* cmd, const char* arg)
{
char buf[128];
int len;
strncpy(buf, cmd, sizeof(buf) - 1);
if(arg)
{
len = strlen(cmd) + 1;
buf[len - 1] = ' ';
strncpy(buf + len, arg , sizeof(buf) - len - 1);
}
return send(fd, buf, strlen(buf) + 1, MSG_NOSIGNAL);
}
static void parseCommand(char* cmd, int client_id)
{
char* command, *arg;
const char *reply = response_invalid, *reply_arg = NULL;
char media_list[32];
int ret;
arg = strchr(cmd, ' ');
if(arg != NULL)
*arg++ = '\0';
else
arg = "";
command = cmd;
DBGMSG("command:%s, arg:%s\n", command, arg);
if(strcasecmp(command,"start") == 0)
{
ret = getMediaByName(arg);
if(ret >= 0)
{
if(startMediaMachine(ret) == 0)
reply = response_success;
else
reply = response_error;
}
}
else if(strcasecmp(command,"folder") == 0)
{
#if CONFIG_DIAG_ALL
if(switchSDFolder(arg) == 0)
reply = response_success;
else
reply = response_error;
#endif
}
else if(strcasecmp(command,"switch") == 0)
{
ret = getMediaByName(arg);
if(ret >= 0)
{
if(switchMediaMachine(ret) == 0)
reply = response_success;
else
reply = response_error;
}
}
else if (strcasecmp(command,"stop") == 0)
{
ret = getMediaByName(arg);
if(ret >= 0)
{
if(stopMediaMachine(ret) == 0)
reply = response_success;
else
reply = response_error;
}
}
else if(strcasecmp(command,"dump") == 0)
{
if(dumpCPBuffer(arg) == 0)
reply = response_success;
else
reply = response_error;
}
else if(strcasecmp(command, "print") == 0)
{
if(strcasecmp(arg, "active") == 0)
{
if(getActiveMedia(media_list))
reply_arg = media_list;
reply = response_success;
}
else if(strcasecmp(arg, "status") == 0)
{
dumpMediaMachine();
reply = response_success;
}
}
else if(strcasecmp(command,"exit") == 0)
{
stopAllMediaMachine();
(void)sendReply(clients[client_id].fd, response_success, NULL);
sleep(3); // wait for CP to disconnect
exit(0);
}
(void)sendReply(clients[client_id].fd, reply, reply_arg);
}
static utlReturnCode_T ReceiveDataFromUtility(const utlEventHandlerType_T handler_type UNUSED,
const utlEventHandlerType_T event_type UNUSED,
const int fd,
const utlRelativeTime_P2c period_p UNUSED,
void *arg_p UNUSED)
{
int received, offset, client_id;
char* current;
char cmdBuffer[512];
client_id = get_client_id_by_fd(fd);
if(client_id < 0)
return utlFAILED;
received = recv (fd, &cmdBuffer, sizeof(cmdBuffer), 0);
if(received == 0)
{
DBGMSG("***** DIAG Command Client %d Connection closed by peer *****\r\n", client_id);
close_client(client_id);
return utlSUCCESS;
}
else if(received < 0)
{
DBGMSG("***** DIAG Command Client %d Read failed:%s. *****\r\n", client_id, strerror(errno));
close_client(client_id);
return utlSUCCESS;
}
DBGMSG("Receive Diag Command from Client(%d) %d bytes!",client_id,received);
current = cmdBuffer;
for(offset = 0; received != 0; --received)
{
/* Each cmd message is terminated by '\0' */
if( current[offset] == '\0')
{//coverity[path_manipulation_sink:SUPPRESS]
parseCommand(current, client_id);
current += offset + 1;
offset = 0;
}
else ++offset;
}
if(offset != 0) //number of bytes left
{
ERRMSG("Fragmented packet received from DIAG command socket");
}
return utlSUCCESS;
}
static int acceptCmdPort(const utlEventHandlerType_T handler_type UNUSED,
const utlEventHandlerType_T event_type UNUSED,
const int fd UNUSED,
const utlRelativeTime_P2c period_p UNUSED,
void *arg_p UNUSED)
{
int ret;
int flags;
int client_id;
ret = accept(cmdfd, NULL, NULL);
if( ret < 0)
{
ERRMSG("***** DIAG Command Port: accept control socket Error: %s *****\r\n",strerror(errno));
return utlFAILED;
}
else
{
flags = fcntl(ret,F_GETFL, 0);
(void)fcntl(ret,F_SETFL, flags | O_NONBLOCK);
client_id = get_free_client_id();
DBGMSG("Accept Diag Command Client:%d!",client_id);
if(client_id_is_valid(client_id))
{
clients[client_id].fd = ret;
clients[client_id].handle_id = utlSetFdEventHandler(utlEVENT_HANDLER_TYPE_READ, utlEVENT_HANDLER_PRIORITY_LOW, ret, ReceiveDataFromUtility, NULL);
}
else
{
DBGMSG("Diag Command Clients reach max number! Connection will be closed");
close(ret);
}
}
return utlSUCCESS;
}
int openCmdPort(void)
{
int len;
struct sockaddr_un socket_name;
init_clients();
socket_name.sun_family = AF_UNIX;
#ifdef ANDROID
cmdfd = android_get_control_socket(DIAG_CMD_SOCKET);
if (cmdfd < 0 )
{
ERRMSG("***** DIAG Command Port: control socket Error: %s *****\r\n",strerror(errno));
return -1;
}
#else
if (strlen(DIAG_CMD_SOCKET) < sizeof(socket_name.sun_path))
strcpy (socket_name.sun_path, DIAG_CMD_SOCKET);
else
{
/* socket path is TOO big for the socket name */
ERRMSG("socket path is TOO big for the socket name\n");
return -1;
}
cmdfd = socket(PF_UNIX, SOCK_STREAM, 0);
if ( cmdfd < 0 )
{
ERRMSG("***** DIAG Command Port: control socket Error: %s *****\r\n",strerror(errno));
return -1;
}
/* unlink to make sure the bind operation won't fail */
unlink(DIAG_CMD_SOCKET);
/* calculate length */
len = sizeof(socket_name.sun_family) + strlen(socket_name.sun_path);
if ( bind(cmdfd, (const struct sockaddr*) &socket_name, len) < 0 )
{
ERRMSG("***** DIAG Command Port: bind control socket Error: %s *****\r\n",strerror(errno));
close(cmdfd);
return -1;
}
(void)chmod(socket_name.sun_path, 0660);
#endif
if( listen(cmdfd, MAX_CLIENT_NUMBER) < 0)
{
ERRMSG("***** DIAG Command Port: listen control socket Error: %s *****\r\n",strerror(errno));
close(cmdfd);
return -1;
}
cmdHandler = utlSetFdEventHandler(utlEVENT_HANDLER_TYPE_READ, utlEVENT_HANDLER_PRIORITY_LOW, cmdfd, acceptCmdPort, NULL);
return 0;
}
void closeCmdPort(void)
{
utlDeleteEventHandler(cmdHandler);
close(cmdfd);
close_clients();
cmdfd = -1;
}