/*
 * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl>
 * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl>
 * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com>
 * Copyright (c) 2007 Adrian Taylor <ade@hohum.me.uk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *	$Id: time.c,v 1.19 2007/01/13 11:17:38 ldv Exp $
 */

#include "defs.h"
#include <inttypes.h>
#include <linux/types.h>

#ifdef LINUX
#include <linux/version.h>
#include <linux/ioctl.h>
#include <linux/binder_module.h>
#endif /* LINUX */

typedef unsigned int u32;

void dump_bytes(data, size)
char* data;
unsigned long size;
{
	int i;
	tprintf("[");
	for (i=0;i<size;i++) {
		tprintf("0x%02x,", (u32)data[i]);
	}
	for (i=0;i<size;i++) {
		if (data[i] >= '!' && data[i] <= '~')
			tprintf("%c", data[i]);
		else
			tprintf(".");
	}
	tprintf("]");
}

void
dump_data(tcp, buffer, size)
struct tcb *tcp;
signed long buffer;
unsigned long size;
{
	char* data = malloc(size);
	if (data != NULL) {
		if (umoven(tcp,buffer,size,data) == 0) {
			char* location = data;
			tprintf("[");
			while (location < data+size) {
				u32 cmd;
				cmd = *((u32*)location);
				location+=sizeof(u32);
				switch (cmd) {
					case bcINCREFS:
						{
						u32 object;
						object = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcINCREFS(target=0x%08x)",object);
						break;
						}
					case bcINCREFS_DONE:
						{
						void* ptr;
						void* cookie;
						ptr = *((void**)location);
						location+=sizeof(void*);
						cookie = *((void**)location);
						location+=sizeof(void*);
						tprintf("bcINCREFS_DONE(ptr=0x%08x,cookie=0x%08x)",(u32)ptr,(u32)cookie);
						break;
						}
					case bcACQUIRE:
						{
						u32 object;
						object = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcACQUIRE(target=0x%08x)",object);
						break;
						}
					case bcACQUIRE_DONE:
						{
						void* ptr;
						void* cookie;
						ptr = *((void**)location);
						location+=sizeof(void*);
						cookie = *((void**)location);
						location+=sizeof(void*);
						tprintf("bcACQUIRE_DONE(ptr=0x%08x,cookie=0x%08x)",(u32)ptr,(u32)cookie);
						break;
						}
					case bcATTEMPT_ACQUIRE:
						{
						u32 priority;
						u32 target;
						priority = *((u32*)location);
						location+=sizeof(u32);
						target = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcATTEMPT_ACQUIRE(priority=0x%08x,target=0x%08x)",priority,target);
						break;
						}
					case bcACQUIRE_RESULT:
						{
						u32 result;
						result = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcACQUIRE_RESULT(result=0x%08x)",result);
						break;
						}
					case bcRELEASE:
						{
						u32 object;
						object = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcRELEASE(target=0x%08x)",object);
						break;
						}
					case bcDECREFS:
						{
						u32 object;
						object = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcDECREFS(target=0x%08x)",object);
						break;
						}
					case bcFREE_BUFFER:
						{
						void* ptr;
						ptr = *((void**)location);
						location+=sizeof(void*);
						tprintf("bcFREE_BUFFER(ptr=0x%08x)",(u32)ptr);
						break;
						}
					case bcRETRIEVE_ROOT_OBJECT:
						{
						u32 object;
						object = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcRETRIEVE_ROOT_OBJECT(pid=%d)",object);
						break;
						}
					case bcTRANSACTION:
					case bcREPLY:
						{
						binder_transaction_data_t* transaction;
						transaction = ((binder_transaction_data_t*)location);
						location += sizeof(binder_transaction_data_t);
						if (cmd == bcTRANSACTION)
							tprintf("bcTRANSACTION({");
						else
							tprintf("bcREPLY({");
						tprintf("cookie=0x%08x,code=0x%08x,flags=0x%08x,priority=%d,data_size=%d,offsets_size=%d,data=",(u32)transaction->cookie,transaction->code,transaction->flags,transaction->priority,transaction->data_size,transaction->offsets_size);
						if (transaction->flags & tfInline) {
							dump_bytes(transaction->data.buf,8);
						} else {
							char* transdata;
							tprintf("{buffer=0x%08x,offsets=0x%08x,*buffer=",(u32)transaction->data.ptr.buffer,(u32)transaction->data.ptr.offsets);
							transdata=malloc(transaction->data_size);
							if (transdata != NULL) {
								if (umoven(tcp,(u32)transaction->data.ptr.buffer,transaction->data_size,transdata) == 0) {
									dump_bytes(transdata,transaction->data_size);
								} else
									tprintf("[...]");
								free(transdata);
							}
							tprintf("}");
						}
						tprintf("})");
						break;
						}
					case bcREGISTER_LOOPER:
						{
						tprintf("bcREGISTER_LOOPER()");
						break;
						}
					case bcENTER_LOOPER:
						{
						tprintf("bcENTER_LOOPER()");
						break;
						}
					case bcEXIT_LOOPER:
						{
						tprintf("bcEXIT_LOOPER()");
						break;
						}
					/*case bcCATCH_ROOT_OBJECTS:
						{
						tprintf("bcCATCH_ROOT_OBJECTS()");
						break;
						}*/
					case bcSTOP_PROCESS:
						{
						u32 target;
						u32 now;
						target = *((u32*)location);
						location+=sizeof(u32);
						now = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcSTOP_PROCESS(target=0x%08x,now=0x%08x)",target,now);
						break;
						}
					case bcSTOP_SELF:
						{
						u32 now;
						now = *((u32*)location);
						location+=sizeof(u32);
						tprintf("bcSTOP_SELF(now=0x%08x)",now);
						break;
						}
					case bcREQUEST_DEATH_NOTIFICATION:
						{
						u32 target;
						void* cookie;
						target = *((u32*)location);
						location+=sizeof(u32);
						cookie = *((void**)location);
						location+=sizeof(void*);
						tprintf("bcREQUEST_DEATH_NOTIFICATION(target=0x%08x,cookie=0x%08x)",target,(u32)cookie);
						break;
						}
					case bcCLEAR_DEATH_NOTIFICATION:
						{
						u32 target;
						void* cookie;
						target = *((u32*)location);
						location+=sizeof(u32);
						cookie = *((void**)location);
						location+=sizeof(void*);
						tprintf("bcCLEAR_DEATH_NOTIFICATION(target=0x%08x,cookie=0x%08x)",target,(u32)cookie);
						break;
						}
					case bcDEAD_BINDER_DONE:
						{
						void* cookie;
						cookie = *((void**)location);
						location+=sizeof(void*);
						tprintf("bcDEAD_BINDER_DONE(cookie=0x%08x)",(u32)cookie);
						break;
						}
				}
			}
			
			tprintf("]");
		} else
			tprintf("{...}");
		
		free(data);
	} else
		tprintf("{...}");
}

int
openbinder_ioctl(tcp, code, arg)
struct tcb *tcp;
long code;
long arg;
{
	switch (code) {
	case BINDER_WRITE_READ:
		if (exiting(tcp)) {
			binder_write_read_t wk;
			if (syserror(tcp) || umove(tcp,arg,&wk) < 0)
				tprintf(", %#lx", arg);
			else {
				tprintf(", {write_size=%ld,write_consumed=%ld,write_buffer=0x%lx,read_size=%ld,read_consumed=%ld,read_buffer=0x%lx",wk.write_size,wk.write_consumed,wk.write_buffer,wk.read_size,wk.read_consumed,wk.read_buffer);
				tprintf(",write_data=");
				dump_data(tcp,wk.write_buffer,wk.write_size);
				tprintf(",read_data={...}");
			}
		}
		break;
	default:
		break;
	}
	return 1;
}

