/* Copyright 1999, 2000 Red Hat, Inc.
 *
 * This software may be freely redistributed under the terms of the GNU
 * public license.
 *
 * 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 <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>

#include "psaux.h"

#ifdef DEBUG_PS2_PROBE
#define dprintf(str...) fprintf(stderr,str)
#else
#define dprintf(str...)
#endif

static void psauxFreeDevice(struct psauxDevice *dev)
{
	freeDevice((struct device *) dev);
}

static void psauxWriteDevice(FILE *file, struct psauxDevice *dev)
{
	writeDevice(file, (struct device *)dev);
}

static int psauxCompareDevice(struct psauxDevice *dev1, struct psauxDevice *dev2)
{
	return compareDevice( (struct device *)dev1, (struct device *)dev2);
}

struct psauxDevice *psauxNewDevice(struct psauxDevice *old)
{
	struct psauxDevice *ret;

	ret = malloc(sizeof(struct psauxDevice));
	memset(ret, '\0', sizeof(struct psauxDevice));
	ret = (struct psauxDevice *) newDevice((struct device *) old, (struct device *) ret);
	ret->bus = BUS_PSAUX;
	ret->newDevice = psauxNewDevice;
	ret->freeDevice = psauxFreeDevice;
	ret->writeDevice = psauxWriteDevice;
	ret->compareDevice = psauxCompareDevice;
	return ret;
}

static int mouse_read(int fd)
{
	struct timeval tv;
	unsigned char ch;
	fd_set fds;
	int ret;

	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	tv.tv_sec = 0;
	tv.tv_usec = 600000;
	ret = select(fd+1, &fds, NULL, NULL, &tv);
	if (-1 == ret) {
		return -1;
	}

	ret = read(fd, &ch, 1);
	if (1 == ret) {
		dprintf("mouse_read: %d\n",ch);
		return ch;
	}

	return -1;
}

static int mouse_cmd(int fd, unsigned char cmd)
{
	int ret;

	dprintf("mouse_cmd: %d\n",cmd);
	ret = write(fd, &cmd, 1);
	if (ret != 1) {
		return -1;
	}

	ret = mouse_read(fd);
	if (ret == 0xfa)
		return 0;
	return -1;
}

struct device *psauxProbe(enum deviceClass probeClass, int probeFlags,
			struct device *devlist)
{
	int portfd, ret;
	struct psauxDevice *ps2dev;
	
	if (probeFlags & PROBE_SAFE) return devlist;
	
	if (
	    (probeClass == CLASS_UNSPEC) ||
	    (probeClass == CLASS_MOUSE) 
	    ) {
		portfd=open("/dev/psaux", O_RDWR|O_NONBLOCK);
		if (portfd < 0)
		  return devlist;
		
		/* reset mouse */
		write(portfd, "\xff", 1);

		/* get any kernel garbage */
		while ((ret = mouse_read(portfd)) >= 0) {
		  dprintf("got %02x\n", ret);
		}

		if (mouse_cmd(portfd, 0xf4)) {
			dprintf("mouse enable failed: no mouse?\n");
		}

		if (mouse_cmd(portfd, 0xf2)) {
			dprintf("mouse type command failed: no mouse\n");
			goto out;
		}

		if (0x00 != mouse_read(portfd)) {
			dprintf("initial mouse type check strange: no mouse\n");
			goto out;
		}

		if (mouse_cmd(portfd, 0xf3)) {
			dprintf("ook1\n");
		}
		if (mouse_cmd(portfd, 0xc8)) {
			dprintf("ook2\n");
		}
		if (mouse_cmd(portfd, 0xf3)) {
			dprintf("ook3\n");
		}
		if (mouse_cmd(portfd, 0x64)) {
			dprintf("ook4\n");
		}
		if (mouse_cmd(portfd, 0xf3)) {
			dprintf("ook5\n");
		}
		if (mouse_cmd(portfd, 0x50)) {
			dprintf("ook6\n");
		}

		/* now issue get device id command */
		dprintf("device id\n");
		if (mouse_cmd(portfd, 0xf2)) {
			dprintf("ook device id\n");
		}

		ret = mouse_read(portfd);
		ps2dev=psauxNewDevice(NULL);
		ps2dev->device=strdup("psaux");
		ps2dev->class=CLASS_MOUSE;
		if (devlist)
		  ps2dev->next = devlist;
		devlist = (struct device *) ps2dev;
		switch (ret) {
		 case 0x03:
		 case 0x04:
		 case 0x05:
			/* Not Intellimouse for now. It screws up the
			 * installer. */
			ps2dev->driver=strdup("generic3ps/2");
			ps2dev->desc=strdup("Generic 3 Button Mouse (PS/2)");
			break;
		 case 0x02:
			/* a ballpoint something or other */
		 case 0x06:
			/* A4 Tech 4D Mouse ? */
		 case 0x08:
			/* A4 Tech 4D+ Mouse ? */
		 case 0x00:
		 default:
			ps2dev->driver=strdup("genericps/2");
			ps2dev->desc=strdup("Generic Mouse (PS/2)");
			break;
		}
out:		
		/* reset mouse */
		dprintf("resetting mouse\n");
		
		write(portfd, "\xff", 1);

		/* get any kernel garbage */
		while ((ret = mouse_read(portfd)) >= 0) {
			dprintf("got %02x\n", ret);
		}

		if (mouse_cmd(portfd, 0xf4)) {
			dprintf("mouse enable failed: no mouse?\n");
		}

		if (mouse_cmd(portfd, 0xf2)) {
			dprintf("mouse type command failed: no mouse\n");
		}

		if (0x00 != mouse_read(portfd)) {
			dprintf("initial mouse type check strange: no mouse\n");
		}
		
		close(portfd);
	}
	return devlist;
}
