| /****************************************************************************** |
| *(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; |
| } |
| |