/*
 *
 * MODIFIED GADGETFS EXAMPLE TO WORK AS A SIMULATED USB MOUSE FOR THE 
 * PXA255 PROCESSOR
 *
 */

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <memory.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <asm/byteorder.h>

#include <linux/types.h>
#include <linux/usb/gadgetfs.h>
#include <linux/usb/ch9.h>
//#include <usb.h>
#include <linux/hid.h>

#include "usbstring.h"

#include <stdint.h>

/* HID descriptor */
struct hid_descriptor {
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint16_t bcdHID;
    uint8_t  bCountryCode;
    uint8_t  bNumDescriptors;
    uint8_t  bReportDescriptorType;
    uint16_t wDescriptorLength;
} __attribute__ ((packed));

static int verbose;


#define DRIVER_VENDOR_NUM	0x046D		/* Logitech */

#define DRIVER_PRODUCT_NUM	0xC00C 


#define	STRINGID_MFGR		1
#define	STRINGID_PRODUCT	2
#define	STRINGID_SERIAL		0
#define	STRINGID_CONFIG		0
#define	STRINGID_INTERFACE	0

static struct usb_device_descriptor
device_desc = {
	.bLength =		sizeof device_desc,
	.bDescriptorType =	USB_DT_DEVICE,
	.bcdUSB =		__constant_cpu_to_le16 (0x0110),
	.bDeviceClass =		0,
	.bDeviceSubClass =	0,
	.bDeviceProtocol =	0,
	// .bMaxPacketSize0 ... set by gadgetfs
	.idVendor =		__constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
	.idProduct =		__constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
	.bcdDevice = 		__constant_cpu_to_le16 (0x2110),
	.iManufacturer =	STRINGID_MFGR,
	.iProduct =		STRINGID_PRODUCT,
	.iSerialNumber =	STRINGID_SERIAL,
	.bNumConfigurations =	1,
};

#define	MAX_USB_POWER		1

#define	CONFIG_VALUE		1

static const struct usb_config_descriptor
config = {
	.bLength =		sizeof config,
	.bDescriptorType =	USB_DT_CONFIG,

	/* must compute wTotalLength ... */
	.bNumInterfaces =	1,
	.bConfigurationValue =	CONFIG_VALUE,
	.iConfiguration = 	STRINGID_CONFIG,
	.bmAttributes =		USB_CONFIG_ATT_ONE
					| USB_CONFIG_ATT_SELFPOWER,
	.bMaxPower =		0x32,//(MAX_USB_POWER + 1) / 2,
};

static struct usb_interface_descriptor
source_sink_intf = {
	.bLength =		sizeof source_sink_intf,
	.bDescriptorType =	USB_DT_INTERFACE,

	.bInterfaceClass =	USB_CLASS_HID,
	.bInterfaceSubClass =	1,
	.bInterfaceProtocol =	2,
	.iInterface =		STRINGID_INTERFACE,
};


static struct hid_descriptor
hid_desc = {
	.bLength = 9,
	.bDescriptorType = USB_DT_CS_DEVICE,
	.bcdHID = 0x0110,
	.bCountryCode = 0,
	.bNumDescriptors = 1,
};




const char ReportDescriptor1[] = {
	0x05, 0x01, //Usage Page (Generic Desktop)
	0x09, 0x02, //Usage (Mouse)
	0xA1, 0x01, //Collection (Application)
	0x09, 0x01, //Usage (Pointer)
	0xA1, 0x00, //Collection (Physical)
	0x05, 0x09, //Usage Page (Buttons)
	0x19, 0x01, //Usage Minimum (1)
	0x29, 0x03, //Usage Maximum (3)
	0x15, 0x00, //Logical Minimum (0)
	0x25, 0x01, //Logical Maximum (1)
	0x95, 0x03, //Report Count (3)
	0x75, 0x01, //Report Size (1)
	0x81, 0x02, //Input (Data, Variable, Absolute)
	0x95, 0x01, //Report Count (1)
	0x75, 0x05, //Report Size (5)
	0x81, 0x01, //Input (03=Logitech, 01=Hid spec:Constant)
	0x05, 0x01, //Usage Page (Generic Desktop)
	0x09, 0x30, //Usage (X)
	0x09, 0x31, //Usage (Y)
	0x09, 0x38, //Usage (Logitech:_)
	0x15, 0x81, //Logical Minimum (-127)
	0x25, 0x7F, //Logical Maximum (127)
	0x75, 0x08, //Report Size (8)
	0x95, 0x02, //Report Count (Logitech=3, Hid spec =2)
	0x81, 0x06, //Input (Data, Variable, Relative)
	0xC0, 	    //End Collection
	0xC0	    // End Collection
};

const unsigned int ReportDescSize = sizeof(ReportDescriptor1);
	
const char MouseMoveArrayX[] = {
	0x8F, 0x0F,
	0x00, 0x00,
	0x8F, 0x00,
	0x8F, 0x00,
	0x00
};

const unsigned int MouseMoveArraySize = sizeof(MouseMoveArrayX);

const char MouseMoveArrayY[] = {
	0x00, 0x00,
	0x3F, 0x9F,
	0x3F, 0x00,
	0x00, 0x00,
	0x00
};


const char MouseMoveArrayButtons[] = {
	0x00, 0x00,
	0x00, 0x02,
	0x02, 0x00,
	0x00, 0x01,
	0x00
};





/* some devices can handle other status packet sizes */
#define STATUS_MAXPACKET	8
#define	LOG2_STATUS_POLL_MSEC	3

static struct usb_endpoint_descriptor
fs_status_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bmAttributes =		USB_ENDPOINT_XFER_INT,
	.wMaxPacketSize =	__constant_cpu_to_le16 (4),
	.bInterval =		0x0A,  
};

static const struct usb_endpoint_descriptor *fs_eps [] = {
	&fs_status_desc,
};


/* High speed configurations are used only in addition to a full-speed
 * ones ... since all high speed devices support full speed configs.
 * Of course, not all hardware supports high speed configurations.
 */

static struct usb_endpoint_descriptor
hs_source_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	__constant_cpu_to_le16 (512),
};

static struct usb_endpoint_descriptor
hs_sink_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	__constant_cpu_to_le16 (512),
	.bInterval =		1,
};

static struct usb_endpoint_descriptor
hs_status_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bmAttributes =		USB_ENDPOINT_XFER_INT,
	.wMaxPacketSize =	__constant_cpu_to_le16 (STATUS_MAXPACKET),
	.bInterval =		LOG2_STATUS_POLL_MSEC + 3,
};

static const struct usb_endpoint_descriptor *hs_eps [] = {
	&hs_source_desc,
	&hs_sink_desc,
	&hs_status_desc,
};

/*-------------------------------------------------------------------------*/

static char serial [32];

static struct usb_string stringtab [] = {
	{ STRINGID_MFGR,	"Licensed to Code, LLC", },
	{ STRINGID_PRODUCT,	"My USB Mouse", },
	{ STRINGID_SERIAL,	serial, },
	{ STRINGID_CONFIG,	"The Configuration", },
	{ STRINGID_INTERFACE,	"Source/Sink", },
};

static struct usb_gadget_strings strings = {
	.language =	0x0409,		/* "en-us" */
	.strings =	stringtab,
};

/*-------------------------------------------------------------------------*/

/* kernel drivers could autoconfigure like this too ... if
 * they were willing to waste the relevant code/data space.
 */

static int	HIGHSPEED;
static char	*DEVNAME;
static char	*EP_IN_NAME, *EP_OUT_NAME, *EP_STATUS_NAME;

/* gadgetfs currently has no chunking (or O_DIRECT/zerocopy) support
 * to turn big requests into lots of smaller ones; so this is "small".
 */
#define	USB_BUFSIZE	(7 * 1024)

static enum usb_device_speed	current_speed;

static inline int min(unsigned a, unsigned b)
{
	return (a < b) ? a : b;
}

static int autoconfig ()
{
	struct stat	statb;

	/* Intel PXA 2xx processor, full speed only */
	if (stat (DEVNAME = "pxa2xx_udc", &statb) == 0 ||
			stat (DEVNAME = "dummy_udc", &statb) == 0) {
		HIGHSPEED = 0;
		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0110),

		/* using bulk for this since the pxa interrupt endpoints
		 * always use the no-toggle scheme (discouraged).
		 */
		source_sink_intf.bNumEndpoints = 1;
		fs_status_desc.bEndpointAddress = 0x82; //Logitech=82, HId spec=81
	}
	else {
		DEVNAME = 0;
		return -ENODEV;
	}
	return 0;
}


/*-------------------------------------------------------------------------*/

/* full duplex data, with at least three threads: ep0, sink, and source */
/*  Only using the ep0 thread for the mouse simulation */

static pthread_t	ep0;

static pthread_t	source;
static int		source_fd = -1;

static pthread_t	sink;
static int		sink_fd = -1;

// FIXME no status i/o yet

static void close_fd (void *fd_ptr)
{
	int	status, fd;

	fd = *(int *)fd_ptr;
	*(int *)fd_ptr = -1;

	/* test the FIFO ioctls (non-ep0 code paths) */
	if (pthread_self () != ep0) {
		status = ioctl (fd, GADGETFS_FIFO_STATUS);
		if (status < 0) {
			/* ENODEV reported after disconnect */
			if (errno != ENODEV && errno != -EOPNOTSUPP)
				perror ("get fifo status");
		} else {
			fprintf (stderr, "fd %d, unclaimed = %d\n",
				fd, status);
			if (status) {
				status = ioctl (fd, GADGETFS_FIFO_FLUSH);
				if (status < 0)
					perror ("fifo flush");
			}
		}
	}

	if (close (fd) < 0)
		perror ("close");
}



/*-------------------------------------------------------------------------*/
static void start_io ()
{
	sigset_t	allsig, oldsig;

	sigfillset (&allsig);
	errno = pthread_sigmask (SIG_SETMASK, &allsig, &oldsig);
	if (errno < 0) {
		perror ("set thread signal mask");
		return;
	}

	/* give the other threads a chance to run before we report
	 * success to the host.
	 * FIXME better yet, use pthread_cond_timedwait() and
	 * synchronize on ep config success.
	 */
	sched_yield ();
	//printf("start_io end of sched_yield()\n");
	
cleanup:
	//printf("start of start_io cleanup\n");
	errno = pthread_sigmask (SIG_SETMASK, &oldsig, 0);
	if (errno != 0) {
		perror ("restore sigmask");
		exit (-1);
	}
	//printf("exiting start_io cleanup\n");
}

static void stop_io ()
{
	if (!pthread_equal (source, ep0)) {
		pthread_cancel (source);
		if (pthread_join (source, 0) != 0)
			perror ("can't join source thread");
		source = ep0;
	}
	if (!pthread_equal (sink, ep0)) {
		pthread_cancel (sink);
		if (pthread_join (sink, 0) != 0)
			perror ("can't join sink thread");
		sink = ep0;
	}
}

/*-------------------------------------------------------------------------*/

static char *
build_config (char *cp, const struct usb_endpoint_descriptor **ep)
{
	struct usb_config_descriptor *c;
	int i;

	c = (struct usb_config_descriptor *) cp;

	memcpy (cp, &config, config.bLength);
	cp += config.bLength;
	memcpy (cp, &source_sink_intf, source_sink_intf.bLength);
	cp += source_sink_intf.bLength;
	memcpy (cp, &hid_desc, hid_desc.bLength);
	cp += hid_desc.bLength;

	for (i = 0; i < source_sink_intf.bNumEndpoints; i++) {
		memcpy (cp, ep [i], USB_DT_ENDPOINT_SIZE);
		cp += USB_DT_ENDPOINT_SIZE;
	}
	c->wTotalLength = __cpu_to_le16 (cp - (char *) c);
	return cp;
}

static int init_device (void)
{
	char		buf [4096], *cp = &buf [0];
	int		fd;
	int		status;

		status = autoconfig ();
	//if (status < 0) {
	//	fprintf (stderr, "?? don't recognize /dev/gadget %s device\n",
	//		iso ? "iso" : "bulk");
	//	return status;
	//}

	fd = open (DEVNAME, O_RDWR);
	if (fd < 0) {
		perror (DEVNAME);
		return -errno;
	}

	*(__u32 *)cp = 0;	/* tag for this format */
	cp += 4;

	/* write full then high speed configs */
	cp = build_config (cp, fs_eps);
	if (HIGHSPEED)
		cp = build_config (cp, hs_eps);

	/* and device descriptor at the end */
	memcpy (cp, &device_desc, sizeof device_desc);
	cp += sizeof device_desc;

	status = write (fd, &buf [0], cp - &buf [0]);
	printf("status for init_device = %x\n", status);
	if (status < 0) {
		perror ("write dev descriptors");
		close (fd);
		return status;
	} else if (status != (cp - buf)) {
		fprintf (stderr, "dev init, wrote %d expected %d\n",
				status, cp - buf);
		close (fd);
		return -EIO;
	}
	return fd;
}


//Originally this tried to write the simulated 'mouse bytes'
//Now it just tries to write a small buffer [3] for functionality
static void mouse_control (int fd)
{
/*	int		status, tmp, iCount;
	__u8		buf [3];
	//__u8		buf [4];
	int		i;
	i = 0;
	iCount = 0;

	//if (setup->bRequestType != USB_DIR_OUT)
	//	goto stall;

	printf("in mouse control function....");
	printf("fd in mouse_control = %x\n", fd);
	for(i=0; i<MouseMoveArraySize; i++)
	{
		buf[0] = MouseMoveArrayButtons[i];
		buf[1] = MouseMoveArrayX[i];
		buf[2] = MouseMoveArrayY[i];
		//buf[3] = 0;
		
		if(buf<0)
		{
			printf("mouse buffer < 0.\n");
			//goto stall;
		}

		tmp = sizeof(buf);
		printf("tmp = sizeof(buf) = %x\n", tmp);
		//printf("buf[0]=%x, buf[1]=%x, buf[2]=%x, buf[3]=%x\n", buf[0], buf[1], buf[2], buf[3]);
		printf("buf[0]=%x, buf[1]=%x, buf[2]=%x\n", buf[0], buf[1], buf[2]);

		while(tmp>0)
		{
			status = write(fd, buf, tmp);
			usleep (100000);
			if ((status<0) || (iCount > 20) || (status > tmp))
			{
				printf("errno = %04x....", errno);
				printf("ERROR - either status <0 (%x) or iCount>20 (%x)\n", status, iCount);
				break;
			}
			else if (status<tmp)
			{
				printf("Status (%x) < tmp (%x); need to write remaining data.\n", status, tmp);
			}
			else
			{
				printf("wrote buffer to fd in mouse_control function, status=%x, i=%x\n", status, i);
				break;
			}
			
			tmp-=status;
			iCount++;
		}
		
	}*/
	int status;
	
	__u8 buf[3] = {1, 2, 3};
	status = write(fd, buf, 3);
	printf("mouse_control status = %x\n", status);

}
	

static void handle_control (int fd, struct usb_ctrlrequest *setup, int s_iCounter)
{
	int		status, tmp;
	__u8		buf [256];
	__u8 buf2[3] = {1, 2, 3};
	int		i;
	i = 0;

	if (verbose)
		fprintf (stderr, "SETUP %02x.%02x "
				"v%04x i%04x %d\n",
			setup->bRequestType, setup->bRequest,
			setup->wValue, setup->wIndex,
			setup->wLength);

	printf ("s_iCounter = %x\n SETUP bRequestType = %02x\n bRequest = %02x\n "
				"wValue = %04x\n wIndex = %04x\n wLength = %02x\n",
			s_iCounter, setup->bRequestType, setup->bRequest,
			setup->wValue, setup->wIndex,
			setup->wLength);

	if ((setup->bRequestType == 0x00) && (setup->bRequest == 0x09) && (s_iCounter > 5))
		goto special;
	//If the initialization is complete (s_iCounter>5) and we see the host polling the device (bRequestType = 0 and bRequest = 9), 
	//Attempt to write the buffer
	 
	printf("fd in handle_control = %x\n", fd);
	//fd is the same each time this function is called

	if (s_iCounter > 0x0a)
		return;
	// Exits out of the loop after 0x0a times though....completes initialization and buffer write attempt


	switch (setup->bRequest) {	/* usb 2.0 spec ch9 requests */
	case USB_REQ_GET_DESCRIPTOR:
		printf("In handle_control Get Descriptor case\n");
		
		switch (setup->wValue >> 8) {
		case USB_DT_STRING:
			printf("get descriptor string type\n");
			if (setup->bRequestType != USB_DIR_IN)
		   		goto stall;
		  	tmp = setup->wValue & 0x0ff;
		  	if (verbose > 1)
				fprintf (stderr,
				"... get string %d lang %04x\n",
				tmp, setup->wIndex);
		  	if (tmp != 0 && setup->wIndex != strings.language)
				goto stall;
		  	status = usb_gadget_get_string (&strings, tmp, buf);
		  	if (status < 0)
				goto stall;
		  	tmp = status;
		 	if (setup->wLength < tmp)
				tmp = setup->wLength;
		 	 status = write (fd, buf, tmp);
		  	if (status < 0) {
				if (errno == EIDRM)
					fprintf (stderr, "string timeout\n");
				else
					perror ("write string data");
		  	} else if (status != tmp) 
				fprintf (stderr, "short string write, %d\n",
				status);
		  	break;
		case 0x22:
		  	printf("Got an report descriptor request\n");
		  	tmp = setup->wValue & 0x00ff;
		  	if (verbose > 1)
				//printf("... get rpt desc %d, index %04x", tmp, setup->wIndex);
		  	if (tmp != 0 && setup->wIndex != 0) 
				goto stall;
		  	for(i=0; i<=ReportDescSize; i++)
		  	{
				buf[i] = ReportDescriptor1[i];
		  	}
		  	if (buf<0) 
		  	{
				//printf("buffer <0, going to stall\n");
				goto stall;
		  	}
		  	status=write(fd, buf, ReportDescSize);
		  	printf("status for write report descriptor = %x\n", status);
			if (status < 0)
		  	{
		     		if (errno == EIDRM)
		   			printf ("GET_REPORT timeout\n");
		     		else
					perror ("write GET_REPORT data");
		  	}
/*			//Have also tried to write the buffer here after the report descriptor has been written
			if (s_iCounter > 7)
			{
				usleep(100000);
				status = write(fd, buf2, 34);
				printf("mouse_control status = %x\n", status); //Status = -1 every time!
			}
*/		  	break;
		default:
		  	goto stall;
		}
		return;
			
			
	case USB_REQ_SET_CONFIGURATION:
		printf("Set Configuration case\n");
		if (setup->bRequestType != USB_DIR_OUT)
			goto stall;
		if (verbose)
			fprintf (stderr, "CONFIG #%d\n", setup->wValue);

		/* Kernel is normally waiting for us to finish reconfiguring
		 * the device.
		 *
		 * Some hardware can't, notably older PXA2xx hardware.  (With
		 * racey and restrictive config change automagic.)  To handle
		 * such hardware, don't write code this way ... instead, keep
		 * the endpoints always active and don't rely on seeing any
		 * config change events, either this or SET_INTERFACE.
		 */
		switch (setup->wValue) {
		case CONFIG_VALUE:
			//printf("about to call start_io function\n");
			start_io ();
			break;
		case 0:
			//printf("about to call stop_io function\n");
			stop_io ();
			//printf("just finished stop_io function\n");
			break;
		default:
			/* kernel bug -- "can't happen" */
			fprintf (stderr, "? illegal config\n");
			goto stall;
		}

		/* ... ack (a write would stall) */
		status = read (fd, &status, 0);
		printf("status of read = %x\n", status);
		if (status)
			perror ("ack SET_CONFIGURATION");
		//printf("About to return from set configuration\n");
		return;
	case USB_REQ_GET_INTERFACE:
		printf("Get Interface case\n");
		if (setup->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
				|| setup->wIndex != 0
				|| setup->wLength > 1)
			goto stall;
		
		/* only one altsetting in this driver */
		buf [0] = 0;
		status = write (fd, buf, setup->wLength);
		if (status < 0) {
			if (errno == EIDRM)
				fprintf (stderr, "GET_INTERFACE timeout\n");
			else
				perror ("write GET_INTERFACE data");
		} else if (status != setup->wLength) {
			fprintf (stderr, "short GET_INTERFACE write, %d\n",
				status);
		}
		return;
	case USB_REQ_SET_INTERFACE:
		printf("Set Interface case\n");
		if (setup->bRequestType != USB_RECIP_INTERFACE
				|| setup->wIndex != 0
				|| setup->wValue != 0)
			goto stall;

		/* just reset toggle/halt for the interface's endpoints */
		status = 0;
		if (ioctl (source_fd, GADGETFS_CLEAR_HALT) < 0) {
			status = errno;
			perror ("reset source fd");
		}
		if (ioctl (sink_fd, GADGETFS_CLEAR_HALT) < 0) {
			status = errno;
			perror ("reset sink fd");
		}
		/* FIXME eventually reset the status endpoint too */
		if (status)
			goto stall;

		/* ... and ack (a write would stall) */
		status = read (fd, &status, 0);
		if (status)
			perror ("ack SET_INTERFACE");
		return;
	default:
		goto stall;
	}

special:
	printf("Entered special case bRequestType=%x and bRequest=%x on s_iCounter=%x\n", setup->bRequestType, setup->bRequest, s_iCounter);
	usleep(100000);
	status = write(fd, buf2, 34);  //Status = -1 every time
	printf("mouse_control status in special = %x\n", status);
		
stall:
	if (verbose)
		printf ("... protocol stall %02x.%02x\n",
			setup->bRequestType, setup->bRequest);

	/* non-iso endpoints are stalled by issuing an i/o request
	 * in the "wrong" direction.  ep0 is special only because
	 * the direction isn't fixed.
	 */
	if (setup->bRequestType & USB_DIR_IN)
		status = read (fd, &status, 0);
	else
		status = write (fd, &status, 0);
	if (status != -1)
		printf ("can't stall ep0 for %02x.%02x\n",
			setup->bRequestType, setup->bRequest);
	else if (errno != EL2HLT)
		perror ("ep0 stall");
	
}
     

static void signothing (int sig, siginfo_t *info, void *ptr)
{
	/* NOP */
	if (verbose > 2)
		fprintf (stderr, "%s %d\n", __FUNCTION__, sig);
}

static const char *speed (enum usb_device_speed s)
{
	switch (s) {
	case USB_SPEED_LOW:	return "low speed";
	case USB_SPEED_FULL:	return "full speed";
	case USB_SPEED_HIGH:	return "high speed";
	default: 		return "UNKNOWN speed";
	}
}

/*-------------------------------------------------------------------------*/

/* control thread, handles main event loop  */

#define	NEVENT	5

static void *ep0_thread (void *param)
{
	int			fd = *(int*) param;
	struct sigaction	action, old;
	sigset_t		sigs;
	static int		s_iCounter = 0;

	source = sink = ep0 = pthread_self ();
	pthread_cleanup_push (close_fd, param);

	/* SIGIO tells file owner that a device event is available
	 * use F_SETSIG to use a different signal.
	 * (not SIGUSR1 or SIGUSR2; linux pthreads may use them.)
	 * right now, only ep0 supports O_ASYNC signaling.
	 */
	if (fcntl (fd, F_SETOWN, getpid ()) < 0) {
		perror ("set ep0 owner");
		return 0;
	}
	if (fcntl (fd, F_SETFL, O_ASYNC) < 0) {
		perror ("set ep0 async mode");
		return 0;
	}
	action.sa_sigaction = signothing;
	sigfillset (&action.sa_mask);
	action.sa_flags = SA_SIGINFO;
	if (sigaction (SIGIO, &action, &old) < 0) {
		perror ("sigaction");
		return 0;
	}

	/* event loop */
	sigfillset (&sigs);
	for (;;) {
		int				tmp;
		struct usb_gadgetfs_event	event [NEVENT];
		int				connected = 0;
		int				i, nevent;
		//Have also tried the simple write as a part of this function, also fails each time
		//int				status;
		//__u8 				buf2[3] = {1, 2, 3};

		sigwait (&sigs, &tmp);

		/* eventually any endpoint could send signals */

		switch (tmp) {
		case SIGIO:		/* used only by ep0 */
			printf("EP0 thread SIGIO case\n");
			tmp = read (fd, &event, sizeof event);
			if (tmp < 0) {
				printf("tmp < 0\n");
				if (errno == EAGAIN) {
					sleep (1);
					continue;
				}
				perror ("ep0 read after sigio");
				goto done;
			}
			nevent = tmp / sizeof event [0];
			if (nevent != 1 && verbose)
				fprintf (stderr, "read %d ep0 events\n",
					nevent);

			for (i = 0; i < nevent; i++) {
				switch (event [i].type) {
				case GADGETFS_NOP:
					printf("NOP\n");
					if (verbose)
						fprintf (stderr, "NOP\n");
					break;
				case GADGETFS_CONNECT:
					connected = 1;
					printf("CONNECTED\n");
				        current_speed = event [i].u.speed;
					if (verbose)
						fprintf (stderr,
							"CONNECT %s\n",
						    speed (event [i].u.speed));
					break;
				case GADGETFS_SETUP:
					s_iCounter++;
					connected = 1;
					
					/*  Had also attempted to perform the writing of the mouse 'movement' bytes in this function
					    (eliminating the 'special' case in handle control).  Implementing here passes the same file descriptor
						to both handle_control and mouse_control, yet the mouse_control write fails each time with status = -1
					*/

					//if (s_iCounter <= 0x0a)  
					//{
					  handle_control (fd, &event [i].u.setup, s_iCounter);
					  //printf("handle_control event %x for s_iCounter=%x\n", &event [i].u.setup, s_iCounter);
					//}
					
					//else if (s_iCounter > 0x0a)
					//{  
					  //mouse_control (fd);
					  //printf("Exited Mouse Movement after s_iCounter=%x, about to goto done\n", s_iCounter);
					  //goto done;
					//}
					//else
					//{
					 // printf("counter is not <=> 0x2b (s_iCounter=%x), going to done\n", s_iCounter);
					 // goto done;
					//}
					
					break;
				case GADGETFS_DISCONNECT:
					connected = 0;
					printf("DISCONNECTED\n");
				        current_speed = USB_SPEED_UNKNOWN;
					if (verbose)
						fprintf(stderr, "DISCONNECT\n");
					stop_io ();
					break;
				case GADGETFS_SUSPEND:
					// connected = 1;
					printf("SUSPEND\n");
					if (verbose)
						fprintf (stderr, "SUSPEND\n");
					break;
				default:
					printf("default case\n");
					fprintf (stderr,
						"* unhandled event %d\n",
						event [i].type);
				}
			}
			continue;

		case SIGINT:
			break;
		case SIGTERM:
			fprintf (stderr, "\nquit, sig %d\n", tmp);
			break;
		case SIGUSR1:
			break;
		case SIGUSR2:
			fprintf (stderr, "\npthreads sig %d\n", tmp);
			break;
		default:
			fprintf (stderr, "\nunrecognized signal %d\n", tmp);
			break;
		}
done:
		fflush (stdout);
		if (connected)
		{
			stop_io ();
		}
		break;
	}

	if (verbose)
		fprintf (stderr, "done\n");
	fflush (stdout);

	pthread_cleanup_pop (1);
	return 0;
}

/*-------------------------------------------------------------------------*/
int main (int argc, char **argv)
{
	int		fd, c, i;

	hid_desc.bDescriptorType = 0x22;	
	hid_desc.wDescriptorLength = 0x34;


	/* random initial serial number */
	srand ((int) time (0));
	for (i = 0; i < sizeof serial - 1; ) {
		c = rand () % 127;
		if ((('a' <= c && c <= 'z') || ('0' <= c && c <= '9')))
			serial [i++] = c;
	}

	while ((c = getopt (argc, argv, "I:a:i:o:r:s:v")) != EOF) {
		switch (c) {
		case 'r':		/* nonrandom serial */
			strncpy (serial, optarg, sizeof serial - 1);
			continue;
		case 'v':		/* verbose */
			verbose++;
			continue;
		}
		fprintf (stderr, "usage:  %s "
				"[-r serial] [-v]\n",
				argv [0]);
		return 1;
	}
	if (chdir ("/dev/gadget") < 0) {
		perror ("can't chdir /dev/gadget");
		return 1;
	}

	fd = init_device ();
	if (fd < 0)
		return 1;
	fprintf (stderr, "/dev/gadget/%s ep0 configured\nserial=\"%s\"\n",
		DEVNAME, serial);
	fflush (stderr);
	(void) ep0_thread (&fd);
	return 0;
}

