blob: b00c2d03cd23734b87a1287a982dbab6dc334d3f [file] [log] [blame]
/*
* Copyright (c) 2017, 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.
*/
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include "cmdiface.h"
#include "parser.h"
#include "debug.h"
#include "WlctPciAcss.h"
#include "pmc_file.h"
#include "udp_server.h"
struct open_interface_s *CmdIface::open_interfaces = NULL;
pthread_mutex_t CmdIface::open_interfaces_mutex = PTHREAD_MUTEX_INITIALIZER;
int CmdIface::interface_id = 0;
/*
*/
void *CmdIface::get_interface(const char *name)
{
struct open_interface_s *s = open_interfaces;
LOG_DEBUG << "Looking for interface: " << name << std::endl;
while(s != NULL)
{
LOG_VERBOSE << "Checking interface for match: " << s->interface << std::endl;
if(0 == strncasecmp(name, s->interface, MAX_INTERFACE_NAME))
{
LOG_VERBOSE << "Match found" << std::endl;
return s->handler;
}
s = s->next;
}
return NULL;
}
/*
*/
void *CmdIface::add_interface(const char *name, void *handler)
{
LOG_DEBUG << "Adding interface: " << name << std::endl;
void *h = get_interface(name);
if(h != NULL)
{
LOG_DEBUG << "The interface is already open" << std::endl;
return h; // Interface already opened
}
// Add interface to the list
struct open_interface_s *s = new struct open_interface_s;
if(s == NULL)
{
return NULL;
}
snprintf(s->interface, MAX_INTERFACE_NAME, "%s", name);
s->handler = handler;
pthread_mutex_lock(&open_interfaces_mutex);
s->next = open_interfaces;
open_interfaces = s;
pthread_mutex_unlock(&open_interfaces_mutex);
return handler;
}
/*
*/
void CmdIface::del_interface(void *handler)
{
LOG_DEBUG << "Deleting interfaces by handler" << std::endl;
struct open_interface_s *s = open_interfaces;
struct open_interface_s *prev = NULL;
while(s != NULL) {
if(handler == s->handler) {
// Remove the interface from the list
pthread_mutex_lock(&open_interfaces_mutex);
if(prev != NULL)
prev->next = s->next;
else
open_interfaces = s->next;
pthread_mutex_unlock(&open_interfaces_mutex);
delete s;
}
prev = s;
s = s->next;
}
}
/*
*/
int CmdIface::cmd_get_interfaces()
{
INTERFACE_LIST interfaces;
int num_interfaces;
int rc;
LOG_DEBUG << "Getting active WIGIG card interfaces" << std::endl;
rc = GetInterfaces(&interfaces, &num_interfaces);
LOG_DEBUG << "Found " << num_interfaces << " interfaces" << std::endl;
std::ostringstream replyBuilder;
if(rc == 0)
{
for(int i=0; i < num_interfaces; i++)
{
replyBuilder << interfaces.list[i].ifName << ' ';
}
}
replyBuilder << "\r\n";
m_Reply = replyBuilder.str();
return 0;
}
/*
*/
int CmdIface::cmd_open_interface(char *interface)
{
void *handler = NULL;
int rc=0;
LOG_DEBUG << "Opening an interface: " << interface << std::endl;
if( strstr(interface, "SPARROW")|| strstr(interface, "sparrow")){
rc = CreateDeviceAccessHandler(interface, MST_SPARROW, &handler);
}
else if(strstr(interface, "MARLON") || strstr(interface, "marlon")){
rc = CreateDeviceAccessHandler(interface, MST_MARLON, &handler);
}
else{
m_Reply = "0xDEAD\r\n";
}
if(rc != 0)
m_Reply = "0xDEADDEAD\r\n";
else {
std::ostringstream replyBuilder;
replyBuilder << interface << '_' << interface_id;
add_interface(replyBuilder.str().c_str(), handler);
replyBuilder << "\r\n";
m_Reply = replyBuilder.str();
interface_id++; // TODO: Should be protected by a mutex? I didn't see it's being used in the application
}
return 0;
}
/*
*/
int CmdIface::cmd_close_interface(char *interface)
{
LOG_DEBUG << "Closing interface: " << interface << std::endl;
void *handler = get_interface(interface);
if(handler != NULL)
{
del_interface(handler);
interface_id--;
}
else
{
LOG_WARNING << "Interface " << interface << " wasn't opened" << std::endl;
}
m_Reply = "0\r\n";
return 0;
}
/*
*/
int CmdIface::cmd_r(char *interface, unsigned int address)
{
unsigned int val = 0xDEADDEAD;
void *handler = get_interface(interface);
std::ostringstream replyBuilder;
if(handler == NULL)
{
m_Reply = "0xDEADDEAD";
}
else
{
int rc = WlctAccssRead(handler, address, val);
(void)rc;
replyBuilder << val;
}
replyBuilder << "\r\n";
m_Reply = replyBuilder.str();
return 0;
}
/*
*/
int CmdIface::cmd_w(char *interface, unsigned int address, unsigned int data)
{
int rc;
void *handler = get_interface(interface);
std::ostringstream replyBuilder;
if(handler == NULL)
{
replyBuilder << "0xDEADDEAD";
}
else
{
rc = WlctAccssWrite(handler, address, data);
if(rc == 0)
replyBuilder << "0";
else
replyBuilder << "ERROR";
}
replyBuilder << "\r\n";
m_Reply = replyBuilder.str();
return 0;
}
/*
*/
int CmdIface::cmd_alloc_pmc(char *interface, unsigned int desc_size, unsigned int desc_num)
{
int rc;
LOG_DEBUG << "Allocating PMC descriptors:"
<< " interface = " << interface
<< " size = " << desc_size
<< " number = " << desc_num
<< std::endl;
std::ostringstream replyBuilder;
void *handler = get_interface(interface);
if(handler == NULL)
{
LOG_DEBUG << "Cannot get handler for interface " << interface << std::endl;
replyBuilder << "0xDEADDEAD";
}
else
{
rc = WlctAccssAllocPmc(handler, desc_size, desc_num);
if(rc == 0)
{
LOG_DEBUG << "Successfully allocated PMC descriptors" << std::endl;
replyBuilder << "0";
}
else
{
LOG_ERROR << "Error allocating PMC descriptors" << std::endl;
replyBuilder << "ERROR";
}
}
replyBuilder << "\r\n";
m_Reply = replyBuilder.str();
return 0;
}
/*
*/
int CmdIface::cmd_read_pmc(char *interface, unsigned int ref_number)
{
LOG_DEBUG << "Reading PMC data:"
<< " interface = " << interface
<< " reference number = " << ref_number
<< std::endl;
void *handler = get_interface(interface);
if(handler == NULL)
{
LOG_ERROR << "No interface found: " << interface << std::endl;
m_Reply = "0xDEADDEAD\r\n";
return 0;
}
PmcFile pmcFile(ref_number);
PmcFileWriter pmcFileWriter(pmcFile);
bool status = pmcFileWriter.WriteFile();
if (false == status)
{
LOG_ERROR << "Error creating PMC data file" << std::endl;
m_Reply = "0xDEADDEAD\r\n";
return 0;
}
// Reply with file size
size_t pmcFileSize = pmcFileWriter.GetFileSize();
std::ostringstream replyBuilder;
replyBuilder << pmcFileSize << "\r\n";
m_Reply = replyBuilder.str();
return 0;
}
/*
*/
int CmdIface::cmd_read_pmc_file(unsigned int ref_number)
{
LOG_DEBUG << "Reading PMC File #" << ref_number << std::endl;
PmcFile pmcFile(ref_number);
if (NULL == pmcFile.GetFileName())
{
LOG_ERROR << "Error getting PMC data file #" << ref_number << std::endl;
m_Reply = "0xDEADDEAD\r\n";
return 0;
}
// Note: No \r\n is needed here, the file name won't be sent to a clientls
m_Reply = pmcFile.GetFileName();
replyType = REPLY_TYPE_FILE;
return 0;
}
/*
*/
int CmdIface::cmd_rb(char *interface, unsigned int address, unsigned int num_regs)
{
void *handler = get_interface(interface);
if((handler == NULL) || (num_regs > MAX_REGS_LEN))
{
m_Reply = "0xDEADDEAD\r\n";
return 0;
}
unsigned int *val = new unsigned int[num_regs];
if (!val)
{
m_Reply = "0xDEADDEAD\r\n";
return 0;
}
int rc = readBlock(handler, address, num_regs*sizeof(unsigned int), (char*)val);
if (rc == 0)
{
std::ostringstream replyBuilder;
replyBuilder << std::hex;
for(unsigned int i=0; i < num_regs; i++)
{
replyBuilder << "0x" << val[i];
if(i < num_regs -1 )
{
replyBuilder << ' ';
}
}
replyBuilder << "\r\n";
m_Reply = replyBuilder.str();
}
else
{
m_Reply = "0xDEADDEAD\r\n";
}
delete[] val;
return 0;
}
/*
*/
int CmdIface::cmd_wb(char *interface, unsigned int address, unsigned int len, const char *block)
{
void *handler = get_interface(interface);
if((handler == NULL) || (len > MAX_REGS_LEN))
{
m_Reply = "0xDEADDEAD\r\n";
return 0;
}
LOG_VERBOSE << "current WB is " << block << " length is " << len << std::endl;
int rc = writeBlock(handler, address, len, block);
if(rc == 0)
{
m_Reply = "0\r\n";
}
else
{
m_Reply = "ERROR\r\n";
}
return 0;
}
int CmdIface::cmd_interface_reset(char *interface)
{
void *handler = get_interface(interface);
if(handler == NULL)
{
m_Reply = "0xDEADDEAD\r\n";
}
else
{
int rc = InterfaceReset(handler);
if(rc == 0)
{
m_Reply = "OK\r\n";
}
else
{
m_Reply = "0xDEADDEAD\r\n";
}
}
return 0;
}
int CmdIface::cmd_sw_reset(char *interface)
{
void *handler = get_interface(interface);
if (handler == NULL)
{
m_Reply = "0xDEADDEAD\r\n";
}
else
{
int rc = SwReset(handler);
if (rc == 0)
{
m_Reply = "OK\r\n";
}
else
{
m_Reply = "0xDEADDEAD\r\n";
}
}
return 0;
}
int CmdIface::cmd_set_host_alias(char* alias)
{
std::ofstream fd(UdpServer::host_details_file_name.c_str());
if (!fd.is_open())
{
m_Reply = "FAIL : Coudn't set the new alias: failed to open the configuration file";
LOG_VERBOSE << m_Reply << std::endl;
return -1;
}
fd << alias;
if (fd.bad())
{
m_Reply = "FAIL : Coudn't set the new alias: failed to write the new alias to the configuration file";
LOG_VERBOSE << m_Reply << std::endl;
fd.close();
return -2;
}
fd.close();
UdpServer::SetHostAlias(alias);
UdpServer::SendAll(UdpServer::GetHostDetails());
return 0;
}
/*
Execute command received from a remote client
The command of length 'len' is in 'buf'. 'outlen' has
the max output buffer size.
On return, 'outbuf' may have a reply to be sent to the client,
'outlen' should have the reply length or 0 if no reply required.
Returns KEEPALIVE_OK if ok, KEEPALIVE_CLOSE to close the connection, KEEPALIVE_SHUTDOWN to shutdown the server (debug mode)
*/
int CmdIface::do_command(const char *buf, int len)
{
servercmd_t s;
int result = KEEPALIVE_OK;
replyType = REPLY_TYPE_BUFFER;
((char*)buf)[len] = '\0'; // Make a zero-terminated string
// Parse the command
parse_line(buf, &s);
dump_parser(&s);
// Check for the parser error. Note, not all the commands in the original protocol report about errors. so we check the error
// for selected commands only TODO: verify the commands list reporting the error
if(s.cmd == CMD_OPEN_INTERFACE || s.cmd == CMD_R || s.cmd == CMD_RB || s.cmd == CMD_W || s.cmd == CMD_WB)
{
if(s.error != 0)
{
LOG_ERROR << "Command line parsing error" << std::endl;
m_Reply = "0xDEADDEAD\r\n"; // TODO: or should it be "dmtools_error"??
return result;
}
}
switch(s.cmd)
{
case CMD_GET_INTERFACES:
cmd_get_interfaces();
break;
case CMD_OPEN_INTERFACE:
cmd_open_interface(s.interface);
break;
case CMD_CLOSE_INTERFACE:
cmd_close_interface(s.interface);
break;
case CMD_R:
cmd_r(s.interface, s.address);
break;
case CMD_RB:
cmd_rb(s.interface, s.address, s.value);
break;
case CMD_W:
cmd_w(s.interface, s.address, s.value);
break;
case CMD_WB:
// hexdata_len is in dwords, cmd_wb works in bytes. (hence * 4)
cmd_wb(s.interface, s.address, s.hexdata_len * 4, (const char*)s.hexdata);
break;
case CMD_INTERFACE_RESET:
cmd_interface_reset(s.interface);
break;
case CMD_SW_RESET:
cmd_sw_reset(s.interface);
break;
case CMD_EXIT:
result = KEEPALIVE_CLOSE; // Terminate the connection
break;
case CMD_ALLOC_PMC:
cmd_alloc_pmc(s.interface, s.address, s.value);
break;
case CMD_READ_PMC:
cmd_read_pmc(s.interface, s.address);
break;
case CMD_READ_PMC_FILE:
cmd_read_pmc_file(s.address);
break;
case CMD_SET_HOST_ALIAS:
cmd_set_host_alias(s.interface);
break;
case CMD_COMMAND_UNKNOWN:
default:
m_Reply = "dmtools_error\r\n";
}
return result;
}
/*
Dump the parser structure, for debugging only
*/
void CmdIface::dump_parser(servercmd_t *s)
{
if(s->error)
{
LOG_ERROR << "Parser error in comand parsing."
<< " Error: " << s->error
<< " Message: " << parser_error_to_string(s->error)
<< std::endl;
return;
}
LOG_VERBOSE << "Parsed Command: " << command_to_string(s->cmd)
<< " Interface: " << s->interface << std::endl;
if(s->address != (unsigned int)-1)
{
LOG_VERBOSE << "Address: " << s->address << std::endl;
}
if(s->value != (unsigned int)-1)
{
LOG_VERBOSE << "Value: " << s->value << std::endl;
}
if(s->hexdata_len)
{
for(int i=0; i < s->hexdata_len && i < MAX_REGS_LEN; i++)
LOG_VERBOSE << std::hex << "0x" << s->hexdata[i] << std::dec << std::endl;
}
}
/*
*/
int CmdIface::get_reply_len()
{
return m_Reply.size();
}
/*
*/
void CmdIface::to_lower(char* string)
{
for (int i = 0; string[i]; i++)
{
string[i] = tolower(string[i]);
}
return;
}
/*
*/
CmdIface::CmdIface()
: replyType(REPLY_TYPE_BUFFER)
{
}