/***************************************************************************
 *   Copyright (C) 2006 by Valentine Sinitsyn                              *
 *   e_val@inbox.ru                                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/

#include "cpufreqdconnection.h"

#include <tqdir.h>

#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

/* Excerpt from cpufreqd_remote.h (cpufreqd-2.0.0)
 *
 * Copyright (C) 2005  Mattia Dongili <malattia@linux.it>
 *                     Hrvoje Zeba <hrvoje@boo.mi2.hr>
 *
 * Format:
 *	it is an uint32_t used as bitmask
 *	
 *	31-16     15-0 
 *	<command> <arguments>
 * 
 * The response may be longer than a single line and is 
 * terminated by the RESPONSE_END (see defines).
 */

#define CMD_SHIFT		16
#define ARG_MASK		0x0000ffff

#define REMOTE_CMD(c)		(c >> CMD_SHIFT)
#define REMOTE_ARG(c)		(c & ARG_MASK)
#define MAKE_COMMAND(cmd, arg)	((cmd << CMD_SHIFT) | arg)
#define INVALID_CMD		0xffffffff

/**
 * This class encapsulates cpufreqd connection performed via Unix sockets
 *@author: Valentine Sinitsyn (Valentine.Sinitsyn@usu.ru)
 */

CPUFreqdConnection::CPUFreqdConnection(): m_fd(-1) {
	lookup();
}


CPUFreqdConnection::~CPUFreqdConnection() {
}

bool CPUFreqdConnection::open() {
	struct sockaddr_un sck;

	// socket name is too long - it can't be copied to to sun_path
	if (m_socket.isEmpty() || m_socket.length() >= 108)
		return false;

	if (m_fd > 0)
		close();

	if ((m_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
		return false;

	sck.sun_family = AF_UNIX;
	//It is guaranted that socket.ascii() is shorter than 108 bytes, but we want to be sure
	strncpy(sck.sun_path, m_socket.ascii(), 108);

	if (::connect(m_fd, (struct sockaddr *)&sck, sizeof(sck)) == -1)
		return false;

	return true;
}

ssize_t CPUFreqdConnection::read(void *buf, size_t size) {
	return ::read(m_fd, buf, size);
}

bool CPUFreqdConnection::write(uint32_t cmd, uint32_t arg) {
	uint32_t command;

	command = MAKE_COMMAND(cmd, arg);
	return (::write(m_fd, &command, sizeof(command)) == (int)sizeof(command));
}

void CPUFreqdConnection::close() {
	::close(m_fd);
	m_fd = -1;
}

bool CPUFreqdConnection::available() const {
	return !m_socket.isEmpty();
}

bool CPUFreqdConnection::lookup() {
	TQString candidate;
	TQDir tmp("/tmp", "cpufreqd-*", TQDir::Time, TQDir::Dirs);

	if (tmp.count())
		candidate = "/tmp/" + tmp[0] + "/cpufreqd";

	if (candidate != m_socket) {
		m_socket = candidate;
		return true;
	}
	
	return false;
}
