/*
 * copro.c
 * Implements Copro's abstraction layer
 *
 * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
 *
 * License type: GPLv2
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>

#include "copro.h"

#define MAX_BUF 80

/* The file descriptor used to manage our TTY over RPMSG */
static int mFdRpmsg = -1;

int copro_isFwRunning(void)
{
	int fd;
	size_t byte_read;
	int result = 0;
	unsigned char bufRead[MAX_BUF];
	char *user  = getenv("USER");
	if (user && strncmp(user, "root", 4)) {
		system("XTERM=xterm su root -c 'cat /sys/class/remoteproc/remoteproc0/state' > /tmp/remoteproc0_state");
		fd = open("/tmp/remoteproc0_state", O_RDONLY);
	} else {
		fd = open("/sys/class/remoteproc/remoteproc0/state", O_RDONLY);
	}
	if (fd < 0) {
		printf("Error opening remoteproc0/state, err=-%d\n", errno);
		return (errno * -1);
	}
	byte_read = (size_t) read (fd, bufRead, MAX_BUF);
	if (byte_read >= strlen("running")) {
		char* pos = strstr((char*)bufRead, "running");
		if(pos) {
			result = 1;
		}
	}
	close(fd);
	return result;
}

int copro_stopFw(void)
{
	int fd;
	char *user  = getenv("USER");
	if (user && strncmp(user, "root",4)) {
		system("su root -c 'echo stop > /sys/class/remoteproc/remoteproc0/state'");
		return 0;
	}
	fd = open("/sys/class/remoteproc/remoteproc0/state", O_RDWR);
	if (fd < 0) {
		printf("Error opening remoteproc0/state, err=-%d\n", errno);
		return (errno * -1);
	}
	write(fd, "stop", strlen("stop"));
	close(fd);
	return 0;
}

int copro_startFw(void)
{
	int fd;
	char *user  = getenv("USER");
	if (user && strncmp(user, "root",4)) {
		system("su root -c 'echo start > /sys/class/remoteproc/remoteproc0/state'");
		return 0;
	}
	fd = open("/sys/class/remoteproc/remoteproc0/state", O_RDWR);
	if (fd < 0) {
		printf("Error opening remoteproc0/state, err=-%d\n", errno);
		return (errno * -1);
	}
	write(fd, "start", strlen("start"));
	close(fd);
	return 0;
}

int copro_getFwPath(char* pathStr)
{
	int fd;
	int byte_read;
	char *user  = getenv("USER");
	if (user && strncmp(user, "root",4)) {
		system("XTERM=xterm su root -c 'cat /sys/module/firmware_class/parameters/path' > /tmp/parameters_path");
		fd = open("/tmp/parameters_path", O_RDONLY);
	} else {
		fd = open("/sys/module/firmware_class/parameters/path", O_RDONLY);
	}
	if (fd < 0) {
		printf("Error opening firmware_class/parameters/path, err=-%d\n", errno);
		return (errno * -1);
	}
	byte_read = read (fd, pathStr, MAX_BUF);
	close(fd);
	return byte_read;
}

int copro_setFwPath(char* pathStr)
{
	int fd;
	int result = 0;
	char *user  = getenv("USER");
	if (user && strncmp(user, "root",4)) {
		char cmd[1024];
		snprintf(cmd, 1024, "su root -c 'echo %s > /sys/module/firmware_class/parameters/path'", pathStr);
		system(cmd);
		return strlen(pathStr);
	}
	fd = open("/sys/module/firmware_class/parameters/path", O_RDWR);
	if (fd < 0) {
		printf("Error opening firmware_class/parameters/path, err=-%d\n", errno);
		return (errno * -1);
	}
	result = write(fd, pathStr, strlen(pathStr));
	close(fd);
	return result;
}

int copro_getFwName(char* name)
{
	int fd;
	int byte_read;
	char *user  = getenv("USER");
	if (user && strncmp(user, "root",4)) {
		system("XTERM=xterm su root -c 'cat /sys/class/remoteproc/remoteproc0/firmware' > /tmp/remoteproc0_firmware");
		fd = open("/tmp/remoteproc0_firmware", O_RDONLY);
	} else {
		fd = open("/sys/class/remoteproc/remoteproc0/firmware", O_RDWR);
	}
	if (fd < 0) {
		printf("Error opening remoteproc0/firmware, err=-%d\n", errno);
		return (errno * -1);
	}
	byte_read = read (fd, name, MAX_BUF);
	close(fd);
	/*  remove \n from the read character */
	return byte_read - 1;
}

int copro_setFwName(char* nameStr)
{
	int fd;
	int result = 0;
	char *user  = getenv("USER");
	if (user && strncmp(user, "root",4)) {
		char cmd[1024];
		snprintf(cmd, 1024, "su root -c 'echo %s > /sys/class/remoteproc/remoteproc0/firmware'", nameStr);
		system(cmd);
		return strlen(nameStr);
	}
	fd = open("/sys/class/remoteproc/remoteproc0/firmware", O_RDWR);
	if (fd < 0) {
		printf("Error opening remoteproc0/firmware, err=-%d\n", errno);
		return (errno * -1);
	}
	result = write(fd, nameStr, strlen(nameStr));
	close(fd);
	return result;
}

int copro_openTtyRpmsg(int modeRaw)
{
	struct termios tiorpmsg;
	mFdRpmsg = open("/dev/ttyRPMSG0", O_RDWR |  O_NOCTTY | O_NONBLOCK);
	if (mFdRpmsg < 0) {
		printf("Error opening ttyRPMSG0, err=-%d\n", errno);
		return (errno * -1);
	}
	/* get current port settings */
	tcgetattr(mFdRpmsg,&tiorpmsg);
	if (modeRaw) {
		tiorpmsg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
				      | INLCR | IGNCR | ICRNL | IXON);
		tiorpmsg.c_oflag &= ~OPOST;
		tiorpmsg.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
		tiorpmsg.c_cflag &= ~(CSIZE | PARENB);
		tiorpmsg.c_cflag |= CS8;
	} else {
		/* ECHO off, other bits unchanged */
		tiorpmsg.c_lflag &= ~ECHO;
		/*do not convert LF to CR LF */
		tiorpmsg.c_oflag &= ~ONLCR;
	}
	tcsetattr(mFdRpmsg, TCSANOW, &tiorpmsg);
	return 0;
}

int copro_closeTtyRpmsg(void)
{
	close(mFdRpmsg);
	mFdRpmsg = -1;
	return 0;
}

int copro_writeTtyRpmsg(int len, char* pData)
{
	int result = 0;
	if (mFdRpmsg < 0) {
		printf("Error writing ttyRPMSG0, fileDescriptor is not set\n");
		return mFdRpmsg;
	}
	result = write(mFdRpmsg, pData, len);
	return result;
}

int copro_readTtyRpmsg(int len, char* pData)
{
	int byte_rd, byte_avail;
	int result = 0;
	if (mFdRpmsg < 0) {
		printf("Error reading ttyRPMSG0, fileDescriptor is not set\n");
		return mFdRpmsg;
	}
	ioctl(mFdRpmsg, FIONREAD, &byte_avail);
	if (byte_avail > 0) {
		if (byte_avail >= len) {
			byte_rd = read (mFdRpmsg, pData, len);
		} else {
			byte_rd = read (mFdRpmsg, pData, byte_avail);
		}
		/*printf("copro_readTtyRpmsg, read successfully %d bytes to %p, [0]=0x%x\n", byte_rd, pData, pData[0]);*/
		result = byte_rd;
	} else {
		result = 0;
	}
	return result;
}

char copro_computeCRC(char *bb, int size)
{
	short sum = 0;

	for (int i = 0; i < size - 1; i++)
		sum += (bb[i] & 0xFF);

	char res = (char) (sum & 0xFF);
	res += (char) (sum >> 8);
	return res;
}
