/*
 * $Id: syspkt.c,v 1.41 2006/10/12 14:21:23 desrod Exp $
 *
 * syspkt.c:  Pilot SysPkt manager
 *
 * (c) 1996, Kenneth Albanowski.
 * Derived from padp.c.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This library 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 Library
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include "pi-source.h"
#include "pi-syspkt.h"
#include "pi-slp.h"
#include "pi-serial.h"
#include "pi-error.h"

/* Declare prototypes */
static int sys_PackRegisters(void *data, struct Pilot_registers *r);


/***********************************************************************
 *
 * Function:    sys_UnpackState
 *
 * Summary:     Get the state command
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_UnpackState(void *buffer, struct Pilot_state *s)
{
	int 	idx;
	unsigned char *data = buffer;

	s->reset 		= get_short(data);
	s->exception 		= get_short(data + 2);
	memcpy(s->func_name, data + 152, 32);
	memcpy(s->instructions, data + 78, 30);
	s->func_name[32 - 1] 	= 0;
	s->func_start 		= get_long(data + 144);
	s->func_end 		= get_long(data + 148);
	sys_UnpackRegisters(data + 4, &s->regs);

	for (idx = 0; idx < 6; idx++) {
		s->breakpoint[idx].address = get_long(data + 108 + idx * 6);
		s->breakpoint[idx].enabled = get_byte(data + 112 + idx * 6);
	}

	s->trap_rev = get_short(data + 184);

	return 0;
}


/***********************************************************************
 *
 * Function:    sys_UnpackRegisters
 *
 * Summary:     Read the register commands
 *
 * Parameters:  None
 *
 * Returns:     0
 *
 ***********************************************************************/
int
sys_UnpackRegisters(void *data, struct Pilot_registers *r)
{
	unsigned char *buffer = data;

	r->D[0] = get_long(buffer + 0);
	r->D[1] = get_long(buffer + 4);
	r->D[2] = get_long(buffer + 8);
	r->D[3] = get_long(buffer + 12);
	r->D[4] = get_long(buffer + 16);
	r->D[5] = get_long(buffer + 20);
	r->D[6] = get_long(buffer + 24);
	r->D[7] = get_long(buffer + 28);
	r->A[0] = get_long(buffer + 32);
	r->A[1] = get_long(buffer + 36);
	r->A[2] = get_long(buffer + 40);
	r->A[3] = get_long(buffer + 44);
	r->A[4] = get_long(buffer + 48);
	r->A[5] = get_long(buffer + 52);
	r->A[6] = get_long(buffer + 56);
	r->USP  = get_long(buffer + 60);
	r->SSP  = get_long(buffer + 64);
	r->PC   = get_long(buffer + 68);

	r->SR   = get_short(buffer + 72);

	return 0;
}


/***********************************************************************
 *
 * Function:    sys_PackRegisters
 *
 * Summary:     Pack the register commands
 *
 * Parameters:  None
 *
 * Returns:     0
 *
 ***********************************************************************/
static int
sys_PackRegisters(void *data, struct Pilot_registers *r)
{
	int 	idx;
	unsigned char *buffer = data;

	for (idx = 0; idx < 8; idx++)
		set_long(buffer + idx * 4, r->D[idx]);
	for (idx = 0; idx < 7; idx++)
		set_long(buffer + 32 + idx * 4, r->A[idx]);
	set_long(buffer + 60, r->USP);
	set_long(buffer + 64, r->SSP);
	set_long(buffer + 68, r->PC);

	set_short(buffer + 72, r->SR);

	return 0;
}

/***********************************************************************
 *
 * Function:    sys_Continue
 *
 * Summary:     Define the Continue command
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_Continue(int sd, struct Pilot_registers *r, struct Pilot_watch *w)
{
	char 	buf[94];

	buf[0] 	= 0;
	buf[1] 	= 0;
	buf[2] 	= 0;
	buf[3] 	= 0;
	buf[4] 	= 0x07;
	buf[5] 	= 0;		/* gapfill */

	if (!r)
		return pi_write(sd, buf, 6);

	sys_PackRegisters(buf + 6, r);
	set_byte(buf + 80, (w != 0) ? 1 : 0);
	set_byte(buf + 81, 0);
	set_long(buf + 82, w ? w->address : 0);
	set_long(buf + 86, w ? w->length : 0);
	set_long(buf + 90, w ? w->checksum : 0);

	return pi_write(sd, buf, 94);
}

/***********************************************************************
 *
 * Function:    sys_Step
 *
 * Summary:     Single-step command
 *
 * Parameters:  None
 *
 * Returns:     Socket, command, 6 bytes
 *
 ***********************************************************************/
int
sys_Step(int sd)
{
	char 	buf[94];

	buf[0] = 0;
	buf[1] = 0;
	buf[2] = 0;
	buf[3] = 0;
	buf[4] = 0x03;
	buf[5] = 0;		/* gapfill */

	return pi_write(sd, buf, 6);
}

/***********************************************************************
 *
 * Function:    sys_SetBreakpoints
 *
 * Summary:     Set the breakpoints (0x0C, 0x8C)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_SetBreakpoints(int sd, struct Pilot_breakpoint *b)
{
	int 	idx;
	pi_buffer_t *buf;

	buf = pi_buffer_new (94);
	if (buf == NULL) {
		errno = ENOMEM;
		return pi_set_error(sd, PI_ERR_GENERIC_MEMORY);
	}

	buf->data[0] = 0;
	buf->data[1] = 0;
	buf->data[2] = 0;
	buf->data[3] = 0;
	buf->data[4] = 0x0c;
	buf->data[5] = 0;		/* gapfill */

	for (idx = 0; idx < 6; idx++) {
		set_long(buf->data + 6 + idx * 6, b[idx].address);
		set_byte(buf->data + 10 + idx * 6, b[idx].enabled);
		set_byte(buf->data + 11 + idx * 6, 0);
	}

	pi_write(sd, buf->data, 42);

	idx = pi_read(sd, buf, 6);

	if (idx <= 0 || buf->data[4] != (unsigned char) 0x8c) {
		pi_buffer_free (buf);
		return 0;
	}

	pi_buffer_free (buf);
	return 1;
}

/***********************************************************************
 *
 * Function:    sys_SetTrapBreaks
 *
 * Summary:     Set the Trap Breaks (0x11, 0x91)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_SetTrapBreaks(int sd, int *traps)
{
	int 	idx;
	pi_buffer_t *buf;

	buf = pi_buffer_new (32);
	if (buf == NULL) {
		errno = ENOMEM;
		return pi_set_error(sd, PI_ERR_GENERIC_MEMORY);
	}

	buf->data[0] = 0;
	buf->data[1] = 0;
	buf->data[2] = 0;
	buf->data[3] = 0;
	buf->data[4] = 0x11;
	buf->data[5] = 0;		/* gapfill */

	for (idx = 0; idx < 5; idx++) {
		set_short(buf->data + 6 + idx * 2, traps[idx]);
	}

	pi_write(sd, buf->data, 16);

	idx = pi_read(sd, buf, 6);

	if ((idx <= 0) || (buf->data[4] != (unsigned char) 0x91)) {
		pi_buffer_free (buf);
		return 0;
	}

	pi_buffer_free (buf);
	return 1;
}

/***********************************************************************
 *
 * Function:    sys_GetTrapBreaks
 *
 * Summary:     Get the Trap Breaks (0x10, 0x90)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_GetTrapBreaks(int sd, int *traps)
{
	int 	idx;
	pi_buffer_t *buf;

	buf = pi_buffer_new (32);
	if (buf == NULL) {
		errno = ENOMEM;
		return pi_set_error(sd, PI_ERR_GENERIC_MEMORY);
	}

	buf->data[0] = 0;
	buf->data[1] = 0;
	buf->data[2] = 0;
	buf->data[3] = 0;
	buf->data[4] = 0x10;
	buf->data[5] = 0;		/* gapfill */

	pi_write(sd, buf->data, 6);

	idx = pi_read(sd, buf, 16);

	if ((idx < 16) || (buf->data[4] != (unsigned char) 0x90)) {
		pi_buffer_free (buf);
		return 0;
	}

	for (idx = 0; idx < 5; idx++) {
		traps[idx] = get_short(buf->data + 6 + idx * 2);
	}

	pi_buffer_free (buf);
	return 1;
}


/***********************************************************************
 *
 * Function:    sys_ToggleDbgBreaks
 *
 * Summary:     Enable the DbgBreaks command (0x0D, 0x8D)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_ToggleDbgBreaks(int sd)
{
	int 	idx;
	pi_buffer_t *buf;
	unsigned char byte;

	buf = pi_buffer_new (32);
	if (buf == NULL) {
		errno = ENOMEM;
		return pi_set_error(sd, PI_ERR_GENERIC_MEMORY);
	}

	buf->data[0] = 0;
	buf->data[1] = 0;
	buf->data[2] = 0;
	buf->data[3] = 0;
	buf->data[4] = 0x0d;
	buf->data[5] = 0;		/* gapfill */

	pi_write(sd, buf->data, 6);

	idx = pi_read(sd, buf, 7);

	if ((idx < 7) || (buf->data[4] != (unsigned char) 0x8d)) {
		pi_buffer_free (buf);
		return 0;
	}

	byte = get_byte(buf->data + 6);

	pi_buffer_free (buf);

	return byte;
}


/***********************************************************************
 *
 * Function:    sys_QueryState
 *
 * Summary:     Query the state (uh)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_QueryState(int sd)
{
	char 	buf[6];

	buf[0] = 0;
	buf[1] = 0;
	buf[2] = 0;
	buf[3] = 0;
	buf[4] = 0;
	buf[5] = 0;		/* gapfill */

	return pi_write(sd, buf, 2);
}

/***********************************************************************
 *
 * Function:    sys_ReadMemory
 *
 * Summary:     Read memory (0x01, 0x81)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_ReadMemory(int sd, unsigned long addr, unsigned long len, void *dest)
{
	int 	result;
	unsigned long todo, done;
	pi_buffer_t *buf;

	buf = pi_buffer_new (0xFFFF);
	if (buf == NULL) {
		errno = ENOMEM;
		return pi_set_error(sd, PI_ERR_GENERIC_MEMORY);
	}

	done = 0;
	do {
		todo 	= len;
		if (todo > 256)
			todo = 256;

		buf->data[0] = 0;
		buf->data[1] = 0;
		buf->data[2] = 0;
		buf->data[3] = 0;
		buf->data[4] = 0x01;
		buf->data[5] = 0;	/* gapfill */

		set_long(buf->data + 6, addr + done);
		set_short(buf->data + 10, todo);

		pi_write(sd, buf->data, 12);

		result = pi_read(sd, buf, todo + 6);

		if (result < 0) {
			pi_buffer_free (buf);
			return done;
		}

		if ((buf->data[4] == 0x81)
		    && ((unsigned int) result == todo + 6)) {
			memcpy(((char *) dest) + done, buf->data + 6, todo);
			done += todo;
		} else {
			pi_buffer_free (buf);
			return done;
		}
	} while (done < len);

	pi_buffer_free (buf);
	return done;
}


/***********************************************************************
 *
 * Function:    sys_WriteMemory
 *
 * Summary:     Write memory (0x02, 0x82)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_WriteMemory(int sd, unsigned long addr, unsigned long len, void *src)
{
	int 	result;
	unsigned long todo, done;
	pi_buffer_t *buf;

	buf = pi_buffer_new (0xFFFF);
	if (buf == NULL) {
		errno = ENOMEM;
		return pi_set_error(sd, PI_ERR_GENERIC_MEMORY);
	}

	done = 0;
	do {
		todo = len;
		if (todo > 256)
			todo = 256;

		buf->data[0] = 0;
		buf->data[1] = 0;
		buf->data[2] = 0;
		buf->data[3] = 0;
		buf->data[4] = 0x02;
		buf->data[5] = 0;	/* gapfill */

		set_long(buf->data + 6, addr);
		set_short(buf->data + 10, len);
		memcpy(buf->data + 12, (char *)src + done, todo);

		pi_write(sd, buf->data, len + 12);

		result = pi_read(sd, buf, 6);

		if (result < 0) {
			pi_buffer_free (buf);
			return done;
		}

		if ((buf->data[4] == (unsigned char)0x82)
		    && ((unsigned long) result == todo + 6)) {
			;
		} else {
			pi_buffer_free (buf);
			return done;
		}
	} while (done < len);

	pi_buffer_free (buf);
	return done;
}


/***********************************************************************
 *
 * Function:    sys_Find
 *
 * Summary:     Searches a range of addresses for data (0x13, 0x80)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_Find(int sd, unsigned long startaddr, unsigned long stopaddr, size_t len,
	 int caseinsensitive, void *data, unsigned long *found)
{
	int 	result;
	unsigned char byte;
	pi_buffer_t *buf;

	buf = pi_buffer_new (len + 17);
	if (buf == NULL) {
		errno = ENOMEM;
		return pi_set_error(sd, PI_ERR_GENERIC_MEMORY);
	}

	buf->data[0] = 0;
	buf->data[1] = 0;
	buf->data[2] = 0;
	buf->data[3] = 0;
	buf->data[4] = 0x11;
	buf->data[5] = 0;		/* gapfill */

	set_long(buf->data + 6, startaddr);
	set_long(buf->data + 10, stopaddr);
	set_short(buf->data + 14, len);
	set_byte(buf->data + 16, caseinsensitive);
	memcpy(buf->data + 17, data, len);

	pi_write(sd, buf->data, len + 17);

	result = pi_read(sd, buf, 12);

	if (result < 0) {
		pi_buffer_free (buf);
		return result;
	}

	if (found)
		*found = get_long(buf->data + 6);

	byte = get_byte(buf->data + 10);

	pi_buffer_free (buf);

	return byte;
}


/***********************************************************************
 *
 * Function:    sys_RemoteEvent
 *
 * Summary:     Parameters sent from host to target to feed pen and
 *		keyboard events. They do not require a response.
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_RemoteEvent(int sd, int penDown, int x, int y, int keypressed,
		int keymod, int keyasc, int keycode)
{
	char 	buf[16];

	/* Offset 1, 3 and 9 are padding */
	set_byte(&buf[0], 0x0D); /* RemoteEvtCommand	*/
	set_byte(&buf[1], 0);
	set_byte(&buf[2], penDown);
	set_byte(&buf[3], 0);
	set_short(&buf[4], x);
	set_short(&buf[6], y);
	set_byte(&buf[8], keypressed);
	set_byte(&buf[9], 0);
	set_short(&buf[10], keymod);
	set_short(&buf[12], keyasc);
	set_short(&buf[14], keycode);

	return pi_write(sd, buf, 16);
}


/***********************************************************************
 *
 * Function:    sys_RPC
 *
 * Summary:     Remote Procedure calls (0x0A, 0x8A)
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
sys_RPC(int sd, int sockaddr, int trap, long *D0, long *A0, int params,
	struct RPC_param *param, int reply)
{
	int 	idx;
	unsigned char *c;
	pi_buffer_t *buf;

	buf = pi_buffer_new (4096);
	if (buf == NULL) {
		errno = ENOMEM;
		return pi_set_error(sd, PI_ERR_GENERIC_MEMORY);
	}

	buf->data[0] = sockaddr;	/* 0 for debug, 1 for console */
	buf->data[1] = sockaddr;
	buf->data[2] = 0;
	buf->data[4] = 0x0a;
	buf->data[5] = 0;

	set_short(buf->data + 6, trap);
	set_long(buf->data + 8, *D0);
	set_long(buf->data + 12, *A0);
	set_short(buf->data + 16, params);

	c = buf->data + 18;
	for (idx = params - 1; idx >= 0; idx--) {
		set_byte(c, param[idx].byRef);
		c++;
		set_byte(c, param[idx].size);
		c++;
		if (param[idx].data)
			memcpy(c, param[idx].data, param[idx].size);
		c += param[idx].size;
		if (param[idx].size & 1)
			*c++ = 0;
	}

	if (sockaddr == 3)
		set_short(buf->data + 4, c - buf->data - 6);

	pi_write(sd, buf->data + 4,(size_t)(c - buf->data - 4));

	if (reply) {
		int l = pi_read(sd, buf, 4096);

		if (l < 0) {
			pi_buffer_free (buf);
			return l;
		}

		if (buf->data[0] != (unsigned char)0x8a) {
			pi_buffer_free (buf);
			return pi_set_error(sd, -2);
		}

		*D0 = get_long(buf->data + 4);
		*A0 = get_long(buf->data + 8);
		c = buf->data + 14;
		for (idx = params - 1; idx >= 0; idx--) {
			if (param[idx].byRef && param[idx].data)
				memcpy(param[idx].data, c + 2,
				       param[idx].size);
			c += 2 + ((get_byte(c + 1) + 1) & ~1);
		}
	}

	pi_buffer_free (buf);
	return 0;
}


/***********************************************************************
 *
 * Function:    RPC
 *
 * Summary:     Deprecated
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
RPC(int sd, int sockaddr, int trap, int reply, ...)
{
	int 	idx 	= 0,
		j,
		RPC_arg[20];
	va_list ap;
	struct 	RPC_param p[20];
	long 	D0 	= 0,
		A0 	= 0;

	va_start(ap, reply);
	for (;;) {
		int type = va_arg(ap, int);

		if (type == 0)
			break;
		if (type < 0) {
			p[idx].byRef 	= 0;
			p[idx].size 	= -type;
			RPC_arg[idx] 	= va_arg(ap, int);

			p[idx].data 	= &RPC_arg[idx];
			p[idx].invert 	= 0;
		} else {
			void *c = va_arg(ap, void *);

			p[idx].byRef 	= 1;
			p[idx].size 	= type;
			p[idx].data 	= c;
			p[idx].invert 	= va_arg(ap, int);

			if (p[idx].invert) {
				if (p[idx].size == 2) {
					int *s = c;

					*s = htons(*s);
				} else {
					int *l = c;

					*l = htonl(*l);
				}
			}
		}
		idx++;
	}
	va_end(ap);

	if (sys_RPC(sd, sockaddr, trap, &D0, &A0, idx, p, reply != 2) < 0)
	    return pi_error(sd);

	for (j = 0; j < idx; j++) {
		if (p[j].invert) {
			void *c = p[j].data;

			if (p[j].size == 2) {
				int *s = c;

				*s = htons(*s);
			} else {
				int *l = c;

				*l = htonl(*l);
			}
		}
	}

	if (reply)
		return A0;
	return D0;
}


/***********************************************************************
 *
 * Function:    PackRPC
 *
 * Summary:     Pack the RPC structure for transmission
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
PackRPC(struct RPC_params *p, int trap, int reply, ...)
{
	int 	idx = 0;
	va_list ap;

	p->trap = trap;
	p->reply = reply;

	va_start(ap, reply);
	for (;;) {
		int type = (int) va_arg(ap, int);

		if (type == 0)
			break;
		if (type < 0) {
			p->param[idx].byRef 	= 0;
			p->param[idx].size 	= -type;
			p->param[idx].arg 	= (int) va_arg(ap, int);

			p->param[idx].data 	= &p->param[idx].arg;
			p->param[idx].invert 	= 0;
		} else {
			void *c = (void *) va_arg(ap, void *);

			p->param[idx].byRef 	= 1;
			p->param[idx].size 	= type;
			p->param[idx].data 	= c;
			p->param[idx].invert 	= (int) va_arg(ap, int);
		}
		idx++;
	}
	p->args = idx;
	va_end(ap);

	return 0;
}


/***********************************************************************
 *
 * Function:    UninvertRPC
 *
 * Summary:
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
void
UninvertRPC(struct RPC_params *p)
{
	int 	j;

	for (j = 0; j < p->args; j++) {
		if (p->param[j].invert) {
			void *c = p->param[j].data;

			if ((p->param[j].invert == 2)
			    && (p->param[j].size == 2)) {
				int *s = c;

				*s = htons(*s) >> 8;
			} else if (p->param[j].size == 2) {
				int *s = c;

				*s = htons(*s);
			} else {
				long *l = c;

				*l = htonl(*l);
			}
		}
	}
}


/***********************************************************************
 *
 * Function:    InvertRPC
 *
 * Summary:
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
void
InvertRPC(struct RPC_params *p)
{
	int 	j;

	for (j = 0; j < p->args; j++) {
		if (p->param[j].invert) {
			void *c = p->param[j].data;

			if ((p->param[j].invert == 2)
			    && (p->param[j].size == 2)) {
				int *s = c;

				*s = ntohs(*s) >> 8;
			} else if (p->param[j].size == 2) {
				int *s = c;

				*s = ntohs(*s);
			} else {
				long *l = c;

				*l = ntohl((unsigned) *l);
			}
		}
	}
}


/***********************************************************************
 *
 * Function:    DoRPC
 *
 * Summary:     Actually execute the RPC query/response
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
unsigned long
DoRPC(int sd, int sockaddr, struct RPC_params *p, int *error)
{
	int 	err;
	long 	D0 = 0,
		A0 = 0;

	InvertRPC(p);

	err =
	    sys_RPC(sd, sockaddr, p->trap, &D0, &A0, p->args, &p->param[0],
		    p->reply);

	UninvertRPC(p);

	if (error)
		*error = err;

	if (p->reply == RPC_PtrReply)
		return A0;
	else if (p->reply == RPC_IntReply)
		return D0;
	else
		return err;
}


/***********************************************************************
 *
 * Function:    RPC_Int_Void
 *
 * Summary:
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
RPC_Int_Void(int sd, int trap)
{
	return RPC(sd, 1, trap, 0, RPC_End);
}


/***********************************************************************
 *
 * Function:    RPC_Ptr_Void
 *
 * Summary:
 *
 * Parameters:  None
 *
 * Returns:     Nothing
 *
 ***********************************************************************/
int
RPC_Ptr_Void(int sd, int trap)
{
	return RPC(sd, 1, trap, 1, RPC_End);
}


/* vi: set ts=8 sw=4 sts=4 noexpandtab: cin */
/* ex: set tabstop=4 expandtab: */
/* Local Variables: */
/* indent-tabs-mode: t */
/* c-basic-offset: 8 */
/* End: */

