/* This code is released for the public domain. Use it for whatever purpose you
 * might find usefull.
 */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/user.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <syscall.h>
#include <string.h>

#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb_ch9.h>

pid_t pid;
int fd;

void dump_urb(void *addr, int eax)
{
	char tmp[sizeof(struct usbdevfs_urb)+sizeof(struct usbdevfs_iso_packet_desc)];	
	struct usbdevfs_urb *txf=(struct usbdevfs_urb *)&tmp;
	int *_txf=(int *)&tmp;
	int i;
	for (i=0;i<(sizeof(tmp)/sizeof(int));i++)
		_txf[i]=ptrace(PTRACE_PEEKDATA, pid, ((int *)addr)+i, NULL);
		fprintf(stderr, " type:              ");
	switch (txf->type)
	{
		case USBDEVFS_URB_TYPE_ISO:       fprintf(stderr, "USBDEVFS_URB_TYPE_ISO\n"); break;
		case USBDEVFS_URB_TYPE_INTERRUPT: fprintf(stderr, "USBDEVFS_URB_TYPE_INTERRUPT\n"); break;
		case USBDEVFS_URB_TYPE_CONTROL:   fprintf(stderr, "USBDEVFS_URB_TYPE_CONTROL\n"); break;
		case USBDEVFS_URB_TYPE_BULK:      fprintf(stderr, "USBDEVFS_URB_TYPE_BULK\n"); break;
		default: fprintf(stderr, "%d\n", txf->type); break;
	}
	fprintf(stderr, " endpoint:          %d %s\n", txf->endpoint&USB_ENDPOINT_NUMBER_MASK, ((txf->endpoint&USB_ENDPOINT_DIR_MASK)==USB_DIR_IN?"USB_DIR_IN":"USB_DIR_OUT"));
	fprintf(stderr, " status:            %d\n", txf->status);
	fprintf(stderr, " flags:             0x%08x (%s %s)\n", txf->flags, (txf->flags&USBDEVFS_URB_SHORT_NOT_OK?"USBDEVFS_URB_SHORT_NOT_OK":""), (txf->flags&USBDEVFS_URB_ISO_ASAP?"USBDEVFS_URB_ISO_ASAP":""));
	fprintf(stderr, " buffer:            %p\n", txf->buffer);
	fprintf(stderr, " buffer_length:     %d\n", txf->buffer_length);
	fprintf(stderr, " actual_length:     %d\n", txf->actual_length);
	fprintf(stderr, " start_frame:       %d\n", txf->start_frame);
	fprintf(stderr, " number_of_packets: %d\n", txf->number_of_packets);
	fprintf(stderr, " error_count:       %d\n", txf->error_count);
	fprintf(stderr, " signr:             %d\n", txf->signr);
	fprintf(stderr, " usercontext:       %p\n", txf->usercontext);
	if (txf->type==USBDEVFS_URB_TYPE_ISO)
	{
		fprintf(stderr, " iso_frame_desc[0].length:        %d\n", txf->iso_frame_desc[0].length);
		fprintf(stderr, " iso_frame_desc[0].actual_length: %d\n", txf->iso_frame_desc[0].actual_length);
		fprintf(stderr, " iso_frame_desc[0].status:        %d\n", txf->iso_frame_desc[0].status);
	}
	/*if (eax==0)*/
	{
		if ((txf->endpoint&USB_ENDPOINT_DIR_MASK)==USB_DIR_OUT)
		{
			char data1[1024*1024];
			int *_data1=(int *)data1;

			for (i=0;i<txf->buffer_length;i+=sizeof(int))
				_data1[i/sizeof(int)]=ptrace(PTRACE_PEEKDATA, pid, ((char *)txf->buffer)+i, NULL);
			fprintf(stderr, " \"");
			for (i=0;i<txf->buffer_length;i++)
				fprintf(stderr, "\\x%02x", (unsigned char)data1[i]);
			fprintf(stderr, "\"");
		} else if (((txf->endpoint&USB_ENDPOINT_DIR_MASK)==USB_DIR_IN))
		{
			char data1[1024*1024];
			int *_data1=(int *)data1;

			if (txf->type==USBDEVFS_URB_TYPE_CONTROL)
				if (txf->actual_length<8)
					txf->actual_length=8;
			for (i=0;i<txf->actual_length;i+=sizeof(int))
				_data1[i/sizeof(int)]=ptrace(PTRACE_PEEKDATA, pid, ((char *)txf->buffer)+i, NULL);
			fprintf(stderr, " \"");
			for (i=0;i<txf->actual_length;i++)
				fprintf(stderr, "\\x%02x", (unsigned char)data1[i]);
			fprintf(stderr, "\"");
		}
	}
}

int main(int argc, char *argv[])
{
	int retval;
	if (argc!=3)
	{
		fprintf(stderr, "Usage: %s pid usb-device\n");
		return -1;
	}
	pid=atoi(argv[1]);
	if (ptrace(PTRACE_ATTACH, pid, (char *)1, NULL))
	{
		perror("ptrace(ATTACH)");
		return -1;
	}
	waitpid(pid, &retval, 0);
	while (1)
	{		
		struct user user;
		if (ptrace(PTRACE_SYSCALL, pid, (char *)1, NULL))
		{
			perror("ptrace(PTRACE_SYSCALL)");
			goto done;
		}
		waitpid(pid, &retval, 0);
		if (ptrace(PTRACE_GETREGS, pid, NULL, &user))
		{
			perror("ptrace(PTRACE_GETREGS)");
			goto done;
		}
		/*fprintf(stderr, "orig_eax=0x%08x eax=0x%08x\n", user.regs.orig_eax, user.regs.eax);*/
		if (user.regs.eax!=-ENOSYS)
		if (user.regs.orig_eax==__NR_open)
		{
			char data1[128];
			int *_data1=(int *)data1;
			int i;
			for (i=0;i<(128/sizeof(int));i++)
				_data1[i]=ptrace(PTRACE_PEEKDATA, pid, ((int *)user.regs.ebx)+i, NULL);
			data1[sizeof(data1)-1]=0;
			if ((fd=user.regs.eax)>=0)
			if (!strcmp(data1, argv[2]))
			{
				fprintf(stderr, "open(0x%08lx %s)=%ld\n", user.regs.ebx, data1, user.regs.eax);
				break;
			}
		}
	}
	while (1)
	{
		struct user user;
		if (ptrace(PTRACE_SYSCALL, pid, (char *)1, NULL))
		{
			perror("ptrace(PTRACE_SYSCALL)");
			goto done;
		}
		waitpid(pid, &retval, 0);
		if (ptrace(PTRACE_GETREGS, pid, NULL, &user))
		{
			perror("ptrace(PTRACE_GETREGS)");
			goto done;
		}
		/*fprintf(stderr, "orig_eax=0x%08x eax=0x%08x\n", user.regs.orig_eax, user.regs.eax);*/
		if (user.regs.eax!=-ENOSYS)
		if (user.regs.orig_eax==__NR_ioctl)
		if (user.regs.ebx==fd)
		switch (user.regs.ecx)
		{
			case USBDEVFS_CONNECT:
				fprintf(stderr, "USBDEVFS_CONNECT: %ld\n", user.regs.eax);
				break;
			case USBDEVFS_DISCONNECT:
				fprintf(stderr, "USBDEVFS_DISCONNECT: %ld\n", user.regs.eax);
				break;
			case USBDEVFS_RESET:
				fprintf(stderr, "USBDEVFS_RESET: %ld\n", user.regs.eax);
				break;
			case USBDEVFS_CLAIMINTERFACE:
			{
				int interface = ptrace(PTRACE_PEEKDATA, pid, ((int *)user.regs.edx), NULL);
				fprintf(stderr, "USBDEVFS_CLAIMINTERFACE(%p->%p): %ld\n", (void *)user.regs.edx, (void *)interface, user.regs.eax);
				break;
			}
			case USBDEVFS_CONTROL:
			{
				struct usbdevfs_ctrltransfer txf;
				int *_txf=(int *)&txf;
				int i;
				for (i=0;i<(sizeof(txf)/sizeof(int));i++)
					_txf[i]=ptrace(PTRACE_PEEKDATA, pid, ((int *)user.regs.edx)+i, NULL);
				fprintf(stderr, "USBDEVFS_CONTROL(\n");
				fprintf(stderr, " bRequestType: %s|", ((txf.bRequestType&USB_ENDPOINT_DIR_MASK)==USB_DIR_IN?"USB_DIR_IN":"USB_DIR_OUT"));
				switch (txf.bRequestType&USB_TYPE_MASK)
				{
					case USB_TYPE_STANDARD: fprintf(stderr, "USB_TYPE_STANDARD|"); break;
					case USB_TYPE_CLASS:    fprintf(stderr, "USB_TYPE_CLASS|"); break;
					case USB_TYPE_VENDOR:   fprintf(stderr, "USB_TYPE_VENDOR|"); break;
					case USB_TYPE_RESERVED: fprintf(stderr, "USB_TYPE_RESERVED|"); break;
				}
				switch (txf.bRequestType&USB_RECIP_MASK)
				{
					case USB_RECIP_DEVICE:    fprintf(stderr, "USB_RECIP_DEVICE\n"); break;
					case USB_RECIP_INTERFACE: fprintf(stderr, "USB_RECIP_INTERFACE\n"); break;
					case USB_RECIP_ENDPOINT:  fprintf(stderr, "USB_RECIP_ENDPOINT\n"); break;
					case USB_RECIP_OTHER:     fprintf(stderr, "USB_RECIP_OTHER\n"); break;
				}
				fprintf(stderr, " bRequest:     ");
				switch (txf.bRequest)
				{
					case USB_REQ_GET_STATUS:        fprintf(stderr, "USB_REQ_GET_STATUS\n"); break;
					case USB_REQ_CLEAR_FEATURE:     fprintf(stderr, "USB_REQ_CLEAR_FEATURE\n"); break;
					case USB_REQ_SET_FEATURE:       fprintf(stderr, "USB_REQ_SET_FEATURE\n"); break;
					case USB_REQ_SET_ADDRESS:       fprintf(stderr, "USB_REQ_SET_ADDRESS\n"); break;
					case USB_REQ_GET_DESCRIPTOR:    fprintf(stderr, "USB_REQ_GET_DESCRIPTOR\n"); break;
					case USB_REQ_SET_DESCRIPTOR:    fprintf(stderr, "USB_REQ_SET_DESCRIPTOR\n"); break;
					case USB_REQ_GET_CONFIGURATION: fprintf(stderr, "USB_REQ_GET_CONFIGURATION\n"); break;
					case USB_REQ_SET_CONFIGURATION: fprintf(stderr, "USB_REQ_SET_CONFIGURATION\n"); break;
					case USB_REQ_GET_INTERFACE:     fprintf(stderr, "USB_REQ_GET_INTERFACE\n"); break;
					case USB_REQ_SET_INTERFACE:     fprintf(stderr, "USB_REQ_SET_INTERFACE\n"); break;
					case USB_REQ_SYNCH_FRAME:       fprintf(stderr, "USB_REQ_SYNCH_FRAME\n"); break;
					default: fprintf(stderr, "%d\n", txf.bRequest); break;
				}
				fprintf(stderr, " wValue:       0x%04x\n", txf.wValue);
				fprintf(stderr, " wIndex:       0x%04x\n", txf.wIndex);
				fprintf(stderr, " wLength:      %d\n", txf.wLength);
				fprintf(stderr, " timeout:      %d\n", txf.timeout);
				fprintf(stderr, " data:         %p", txf.data);
				if (user.regs.eax>0)
				{
					char data1[65536];
					int *_data1=(int *)data1;

					for (i=0;i<user.regs.eax;i+=sizeof(int))
						_data1[i/sizeof(int)]=ptrace(PTRACE_PEEKDATA, pid, ((char *)txf.data)+i, NULL);
					fprintf(stderr, " \"");
					for (i=0;i<user.regs.eax;i++)
						if (data1[i]=='\\')
							fprintf(stderr, "\\\\");
						else if (((unsigned char)data1[i])<32)
							fprintf(stderr, "\\%d%d%d", data1[i]>>6, data1[i]>>3&7, data1[i]>>3&7);
						else if (((unsigned char )data1[i])<=127)
							fprintf(stderr, "%c", data1[i]);
						else
						fprintf(stderr, "\\%d%d%d", data1[i]>>6, data1[i]>>3&7, data1[i]>>3&7);
					fprintf(stderr, "\"");
				}
				fprintf(stderr, "): %ld\n", user.regs.eax);
				break;
			}
			case USBDEVFS_BULK:
				fprintf(stderr, "USBDEVFS_BULK(TODO): %ld\n", user.regs.eax);	
				break;
			case USBDEVFS_RESETEP:
				fprintf(stderr, "USBDEVFS_RESETEP(%ld): %ld\n", user.regs.edx, user.regs.eax);
				break;
			case USBDEVFS_SETINTERFACE:
				fprintf(stderr, "USBDEVFS_SETINTERFACE(TODO): %ld\n", user.regs.eax);
				break;
			case USBDEVFS_SETCONFIGURATION:
				fprintf(stderr, "USBDEVFS_SETCONFIGURATION(%ld): %ld\n", user.regs.edx, user.regs.eax);
				break;
			case USBDEVFS_GETDRIVER:
				fprintf(stderr, "USBDEVFS_GETDRIVER(TODO): %ld\n", user.regs.eax);
				break;
			case USBDEVFS_SUBMITURB:
			{
				fprintf(stderr, "USBDEVFS_SUBMITURB(%p\n", (void *)user.regs.edx);
				dump_urb((void *)user.regs.edx, user.regs.eax);
				fprintf(stderr, "): %ld\n", user.regs.eax);
				break;
			}
			case USBDEVFS_DISCARDURB:
				fprintf(stderr, "USBDEVFS_DISCARDURB: %ld\n", user.regs.eax);
				break;
			case USBDEVFS_REAPURB:
			{
				void *arg;
				if (!user.regs.eax)
				{
					arg=(void *)ptrace(PTRACE_PEEKDATA, pid, user.regs.edx, NULL);
					fprintf(stderr, "USBDEVFS_REAPURB(%p->%p\n", (void *)user.regs.edx, arg);
					dump_urb(arg, user.regs.eax);
					fprintf(stderr, "): %ld\n", user.regs.eax);
				} else
					fprintf(stderr, "USBDEVFS_REAPURB(%p): %ld\n", (void *)user.regs.edx, user.regs.eax);
				break;
			}
			case USBDEVFS_REAPURBNDELAY:
			{
				void *arg;
				if (!user.regs.eax)
				{
					arg=(void *)ptrace(PTRACE_PEEKDATA, pid, user.regs.edx, NULL);
					fprintf(stderr, "USBDEVFS_REAPURBNDELAY(%p->%p\n", (void *)user.regs.edx, arg);
					dump_urb(arg, user.regs.eax);
					fprintf(stderr, "): %ld\n", user.regs.eax);
				} else
					fprintf(stderr, "USBDEVFS_REAPURBNDELAY(%p): %ld\n", (void *)user.regs.edx, user.regs.eax);
				break;
			}
			default:
				{
					int src=ptrace(PTRACE_PEEKDATA, pid, (int *)user.regs.edi, NULL);
					fprintf(stderr, "ioctl(%ld, %p %p %p(%d) %p)=%ld\n", user.regs.ebx, (void *)user.regs.ecx, (void *)user.regs.edx, (void *)user.regs.esi, src, (void *)user.regs.edi, user.regs.eax);
				}
		}

	}
done:
	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
		perror("ptrace(PTRACE_DETACH)");
	return 0;
}
