/* 
 * Sony Programmable I/O Control Device (SPIC) driver for VAIO. 
 * Userspace Control Utility
 *
 * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
 *
 * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
 * 
 * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
 * 
 * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <getopt.h>
#include <errno.h>
#include <linux/types.h>

#include "spicctrl.h"

static const char *version = "version 1.9, Jun 28, 2005";

/* Function prototypes */
int main(int, char *[]);
static void usage(const char *, const char *);
static int sonypi_open(const char *, mode_t mode);
static int sonypi_ioctl(int, int, void *);
static int sonypi_close(int);

/*
 * Main function.
 */
int main(int argc, char *argv[]) {
	int c, choice = 0;
	__u32 value = 0;
	__u8 value8;
	__u16 value16;
	int fd = 0;
	int res = 0;
	mode_t mode = O_RDONLY;
	static struct option long_options[] = {
		{"getacstatus", 0, NULL, 'a'},
		{"setbrightness",  1, NULL, 'b'},
		{"getbrightness", 0, NULL, 'B'},
		{"getbat1capacity", 0, NULL, 'c'},
		{"getbat2capacity", 0, NULL, 'C'},
		{"setbluetoothpower", 1, NULL, 'l'},
		{"getbluetoothpower", 0, NULL, 'L'},
		{"powerstatus", 0, NULL, 'p'},
		{"getbat1remaining", 0, NULL, 'r'},
		{"getbat2remaining", 0, NULL, 'R'},
		{"getbat1status", 0, NULL, 's'},
		{"getbat2status", 0, NULL, 'S'},
		/* FAN Status */
		{"getfanstatus", 0, NULL, 'F'},
		{"setfanstatus", 1, NULL, 'f'},
		/* TEMP Status */
		{"gettempstatus", 0, NULL, 'T'},
		{NULL, 0, NULL, 0}
	};

	while (1) {
		c = getopt_long(argc, argv, "ab:BcCl:LprRsSFf:T", 
				long_options, NULL);
		if (c == -1)
			break;

		switch (c) {
			case 'b':
			case 'l':
			case 'f':
				choice = c;
				fd = 1;
				value = atol(optarg);
				mode = O_RDWR;
				break;
			case 'a':
			case 'B':
			case 'c':
			case 'C':
			case 'L':
			case 'p':
			case 'r':
			case 'R':
			case 's':
			case 'S':
			case 'F':
			case 'T':
				choice = c;
				fd = 1;
				break;
			default:
				fprintf(stderr, "unknown option %c\n", c);
				break;
		}
	}

	if (fd) {
		if ((fd = sonypi_open("/dev/sonypi", mode)) == -1)
			return -1;
	}

	switch (choice) {
		case 'a':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBATFLAGS, &value8)) >= 0)
				printf("%d\n", (value8 & SONYPI_BFLAGS_AC) != 0);
			break;
		case 'b':
			if (value > 255)
				value = 255;
			value8 = (__u8) value;
			res = sonypi_ioctl(fd, SONYPI_IOCSBRT, &value8);
			break;
		case 'B':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBRT, &value8)) >= 0)
				printf("%d\n", value8);
			break;
		case 'c':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBAT1CAP, &value16)) >= 0)
				printf("%d\n", value16);
			break;
		case 'C':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBAT2CAP, &value16)) >= 0)
				printf("%d\n", value16);
			break;
		case 'l':
			if (value > 1)
				value = 1;
			value8 = (__u8) value;
			res = sonypi_ioctl(fd, SONYPI_IOCSBLUE, &value8);
			break;
		case 'L':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBLUE, &value8)) >= 0)
				printf("%d\n", value8);
			break;
		case 'p':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBATFLAGS, &value8)) < 0)
				break;
			if (value8 & SONYPI_BFLAGS_B1) {
				__u16 cap, rem;
				if ((res = sonypi_ioctl(fd, SONYPI_IOCGBAT1CAP, &cap)) >= 0
				 && (res = sonypi_ioctl(fd, SONYPI_IOCGBAT1REM, &rem)) >= 0)
					printf("BAT1: %d/%d %.2f%% ", rem, cap, 100*rem/(double)cap);
			}
			if (value8 & SONYPI_BFLAGS_B2) {
				__u16 cap, rem;
				if ((res = sonypi_ioctl(fd, SONYPI_IOCGBAT2CAP, &cap)) >= 0
				 && (res = sonypi_ioctl(fd, SONYPI_IOCGBAT2REM, &rem)) >= 0)
					printf("BAT2: %d/%d %.2f%% ", rem, cap, 100*rem/(double)cap);
			}
			if (value8 & SONYPI_BFLAGS_AC)
				printf("AC");
			printf("\n");
			break;	
		case 'r':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBAT1REM, &value16)) >= 0)
				printf("%d\n", value16);
			break;
		case 'R':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBAT2REM, &value16)) >= 0)
				printf("%d\n", value16);
			break;
		case 's':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBATFLAGS, &value8)) >= 0)
				printf("%d\n", (value8 & SONYPI_BFLAGS_B1) != 0);
			break;
		case 'S':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGBATFLAGS, &value8)) >= 0)
				printf("%d\n", (value8 & SONYPI_BFLAGS_B2) != 0);
			break;
		case 'f':
			if (value > 255)
				value = 255;
			value8 = (__u8) value;
			res = sonypi_ioctl(fd, SONYPI_IOCSFAN, &value8);
			break;
		case 'F':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGFAN, &value8)) >= 0)
				printf("%d\n", value8);
			break;
		case 'T':
			if ((res = sonypi_ioctl(fd, SONYPI_IOCGTEMP, &value8)) >= 0)
				printf("%d\n", value8);
			break;
		default:
			usage(argv[0], version);
			return 1;
	}

	sonypi_close(fd);

	return (res < 0 ? -1 : 0);
}

/*
 * Prints the program usage.
 *
 *	program: program filename
 */
static void usage(const char *program, const char *ver) {

	fprintf(stderr,
		"Sony Vaio SPIC control program %s\n\n"
		"Usage: %s [COMMAND] [OPTION]...\n"
		"\nCommands:\n"
		"\t-a, --getacstatus\t\tget AC adaptor status\n"
		"\t-b, --setbrightness=NUM\t\tset lcd screen brightness (0-255)\n"
		"\t-B, --getbrightness\t\tget lcd screen brightness\n"
		"\t-c, --getbat1capacity\t\tget first battery capacity\n"
		"\t-C, --getbat2capacity\t\tget second battery capacity\n"
		"\t-l, --setbluetoothpower=NUM\tset bluetooth device power state (0-1)\n"
		"\t-L, --getbluetoothpower\t\tget bluetooth device power state\n"
		"\t-p, --powerstatus\t\tprint out battery summary\n"
		"\t-r, --getbat1remaining\t\tget first battery remaining capacity\n"
		"\t-R, --getbat2remaining\t\tget second battery remaining capacity\n"
		"\t-s, --getbat1status\t\tget first battery status\n"
		"\t-S, --getbat2status\t\tget second battery status\n"
		"\t-f, --setfanspeed=NUM\t\tset fan speed (0-255)\n"
		"\t-F, --getfanspeed\t\tget fan speed\n"
		"\t-T, --gettemperature\t\tget temperature\n",
		ver, program);
}

/*
 * Opens the device
 *
 * Return: a file descriptor, -1 if an error occured.
 */
static int sonypi_open(const char *devname, mode_t mode) {
	int fd;

	if ((fd = open(devname, mode)) == -1) {
		perror(devname);
		return -1;
	}

	return fd;
}	

/*
 * Sends a ioctl command to the SPIC driver.
 *
 * Return: 0 if success, -1 if an error occured.
 */
static int sonypi_ioctl(int fd, int ioctlno, void *param) {
	if (ioctl(fd, ioctlno, param) < 0) {
		perror("ioctl");
		return -1;
	}

	return 0;
}

/*
 * Closes the device
 *
 * Return: 0 if success, -1 if an error occured.
 */
static int sonypi_close(int fd) {
	if (close(fd) == -1) {
		perror("close");
		return -1;
	}

	return 0;
}
