/*
 * libspe - A wrapper library to adapt the JSRE SPU usage model to SPUFS
 * Copyright (C) 2005 IBM Corp.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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 Lesser General Public
 *  License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software Foundation,
 *   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <elf.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <pthread.h>
#define _GNU_SOURCE
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>

#include <linux/unistd.h>

#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/syscall.h>

#include <sys/spe.h>

#include "libspe.h"
#include "spe.h"
#include "elf_loader.h"
#include "spe_exec.h"
#include "default_c99_handler.h"
#include "default_posix1_handler.h"

/*
 * Helpers
 *
 * */

static struct group_list grp_list = {NULL,PTHREAD_MUTEX_INITIALIZER,0};

extern int check_env;

/**
 * Send data to a SPU in mbox when space is available.
 *
 * Helper function for internal libspe use.
 *
 * @param thread The SPE thread to send the mailbox message to
 * @param data	 Data to send to the mailbox
 * @return zero on success, non-zero on failure
 */
static int
spe_send_in_mbox_synchronous(struct thread_store *thread, int data)
{
	while (!spe_stat_in_mbox(thread))
		;
	return spe_write_in_mbox(thread, data);
}

/**
 * Initiate transfer of an isolated SPE app by the loader kernel.
 *
 * Helper function for internal libspe use.
 *
 * @param thread The SPE thread to load the app to
 * @param handle The handle to the spe program
 * @return zero on success, non-zero on failure;
 */
static int
spe_start_isolated_app(struct thread_store *thread,
		spe_program_handle_t *handle)
{
	uint64_t addr;
	uint32_t size, addr_h, addr_l;
		 spe_spu_control_area_t *control_area;

	if (spe_parse_isolated_elf(handle, &addr, &size)) {
		 		 DEBUG_PRINTF("%s: invalid isolated image\n", __FUNCTION__);
		errno = ENOEXEC;
		return -errno;
	}

	if (addr & 0xf) {
		DEBUG_PRINTF("%s: isolated image is incorrectly aligned\n",
				__FUNCTION__);
		errno = EINVAL;
		return -errno;
	}

	control_area = spe_get_ps_area(thread, SPE_CONTROL_AREA);
	if (!control_area) {
		DEBUG_PRINTF("%s: could not access SPE control area: %s\n",
				__FUNCTION__, strerror(errno));
		return -errno;
	}

	DEBUG_PRINTF("%s: Waiting for spu loader to start\n", __FUNCTION__);
	while (control_area->SPU_Status & 0x200)
		sched_yield();

	addr_l = (uint32_t)(addr & 0xffffffff);
	addr_h = (uint32_t)(addr >> 32);

	DEBUG_PRINTF("%s: Sending isolated app params: 0x%08x 0x%08x 0x%08x\n",
			__FUNCTION__, addr_h, addr_l, size);

	return spe_send_in_mbox_synchronous(thread, addr_h)
		|| spe_send_in_mbox_synchronous(thread, addr_l)
		|| spe_send_in_mbox_synchronous(thread, size);
}

/* Group defaults
 *  */

int default_priority = 0;
int default_policy   = SCHED_OTHER;
int default_eventmask = 0;

//extern void * spe_thread (void *);

int 
add_group_to_groups(struct group_store *gid)
{
	struct grpsElem *listElem;
	listElem = malloc(sizeof *listElem);

	if (!listElem)
	{
		errno=ENOMEM;
		return -errno;
	}
	
	pthread_mutex_lock(&grp_list.mutex);

	listElem->group=gid;
	listElem->next=grp_list.list_start;
	
	grp_list.list_start=listElem;
	grp_list.numListSize++;
		
	pthread_mutex_unlock(&grp_list.mutex);
	
	return 0;
	
}
int 
add_thread_to_group(struct group_store *gid, struct thread_store *thread)
{
	struct grpListElem *listElem;
	listElem = malloc(sizeof *listElem);

	if (!listElem)
	{
		errno=ENOMEM;
		return -errno;
	}

	pthread_mutex_lock(&grp_list.mutex);
	
	listElem->thread=thread;
	listElem->next=gid->grp_members;

	gid->grp_members=listElem;
	gid->numListSize++;
		
	pthread_mutex_unlock(&grp_list.mutex);
	
	return 0;
}

int 
remove_group_from_groups(struct group_store *gid)
{
	pthread_mutex_lock(&grp_list.mutex);
	
	struct grpsElem *lgp = NULL;
	
	for (struct grpsElem *gp=grp_list.list_start ; gp !=NULL; gp=gp->next)
	{
		if (gp->group == gid)
		{
			if (gp->group->numListSize == 0)
			{
				// Delete the group
				
				if (lgp == NULL)
				{
					grp_list.list_start = gp->next;
				}
				else
				{
					lgp->next = gp->next; 
				}

				free(gp);
			}
			
			pthread_mutex_unlock(&grp_list.mutex);		
			return 0;
		}
		lgp = gp;
	}

	pthread_mutex_unlock(&grp_list.mutex);
	
	return 1;
}

int 
remove_thread_from_group(struct group_store *gid, struct thread_store *thread)
{
	pthread_mutex_lock(&grp_list.mutex);
	
	if (gid->numListSize == 1)
	{	
		struct grpListElem *p=gid->grp_members;
		gid->numListSize--;
		free(p);
		gid->grp_members = NULL;
		pthread_mutex_unlock(&grp_list.mutex);
		
		if (gid->deleteMe)
		{
			remove_group_from_groups(gid);
		}
		return 0;
	}

	for (struct grpListElem *p=gid->grp_members; p->next!=NULL; p=p->next)
	{
		struct grpListElem *np=p->next;
		if (np->thread==thread)
		{
			p->next=np->next;
			gid->numListSize--;
			free(np);
			
			pthread_mutex_unlock(&grp_list.mutex);

			return 0;
		}
	}

	pthread_mutex_unlock(&grp_list.mutex);

	errno = ESRCH;
	return -ESRCH;
}

struct thread_store *
srch_thread(struct thread_store *thread)
{
	pthread_mutex_lock(&grp_list.mutex);

	for (struct grpsElem *gp=grp_list.list_start ; gp !=NULL; gp=gp->next)
	{
		//printf("1\n");
		struct group_store *gid = gp->group;
			
		for (struct grpListElem *p=gid->grp_members; p !=NULL; p=p->next)
		{
			//printf("2\n");
			if (p->thread==thread)
			{
				pthread_mutex_unlock(&grp_list.mutex);		
				return p->thread;
			}
		}
	}
	
	pthread_mutex_unlock(&grp_list.mutex);
	errno = ESRCH;
	return NULL;
}

struct group_store *
srch_group(struct group_store *group)
{
	pthread_mutex_lock(&grp_list.mutex);

	for (struct grpsElem *gp=grp_list.list_start ; gp !=NULL; gp=gp->next)
	{
		if (gp->group == group)
		{
			pthread_mutex_unlock(&grp_list.mutex);		
			return gp->group;
		}
	}
	
	pthread_mutex_unlock(&grp_list.mutex);
	errno = ESRCH;
	return NULL;
}


void *
spe_gid_setup(int policy, int priority, int use_events)
{
	struct group_store *group_store;

        group_store = calloc (1, sizeof *group_store);
	if (!group_store)
	{
		DEBUG_PRINTF ("Could not allocate group store\n");
		errno = ENOMEM;
	        return NULL;
	}
	
	group_store->policy = policy;
	group_store->priority = priority;
	group_store->use_events = use_events;
	group_store->grp_members = NULL;
	group_store->numListSize = 0;
	group_store->deleteMe = 0;

	add_group_to_groups(group_store);
	
	return group_store;
}

spe_gid_t
spe_create_group (int policy, int priority, int use_events)
{
	struct group_store *group_store;
	
	/* Sanity: check for vaild policy/priority combination.
	 */
	if (!check_priority(policy, priority))
	{
		errno=EINVAL;
		return NULL;
	}

	DEBUG_PRINTF ("spu_create_group(0x%x, 0x%x, 0x%x)\n",
			                  policy, priority, use_events);
	
	group_store=spe_gid_setup(policy, priority, use_events);
	DEBUG_PRINTF("gid is %p\n", group_store);

	return group_store;
}

int
spe_destroy_group(spe_gid_t spe_gid)
{
	struct group_store *gid = spe_gid;

	if ( gid != NULL && !srch_group(gid)) 	{
		errno = ESRCH;
		return -1;
	}
	
	gid->deleteMe=1;
	remove_group_from_groups(gid);
	
	return 0;
}

int spe_group_max (spe_gid_t spe_gid)
{

	if ( spe_gid != NULL && !srch_group(spe_gid)) 	{
		errno = ESRCH;
		return -1;
	}
	
	DEBUG_PRINTF ("spu_group_max(0x%x)\n", spe_gid);

	return MAX_THREADS_PER_GROUP;	
}

int spe_count_physical_spes(void)
{
	char	buff[256] = "/sys/devices/system/spu";
	DIR	*dirp;
	int ret = -2;
	struct	dirent	*dptr;
	
	DEBUG_PRINTF ("spe_count_physical_spes()\n");

	 // Count number of SPUs in /sys/devices/system/spu 

	if((dirp=opendir(buff))==NULL)
	{
		fprintf(stderr,"Error opening %s ",buff);
		perror("dirlist");
		return -1;
	}
	while(dptr=readdir(dirp))
	{
		ret++;
	}
	closedir(dirp);
	
	return ret;

}

speid_t
spe_create_thread (spe_gid_t gid, spe_program_handle_t *handle, void *argp, void *envp, unsigned long mask, int flags)
{
	struct thread_store *thread_store;
	int rc;

	/* Sanity check
	 */
	if (!handle || (( flags & SPE_USER_REGS ) && !argp ) ||
		((flags & SPE_USER_REGS) && (flags & SPE_ISOLATE)))
	{
		errno=EINVAL;
		return NULL;
	}

	if ( gid != NULL && !srch_group(gid))
	{
		errno = ESRCH;
		return NULL;
	}

	if ( mask == 0 ||  (mask & ((1<<spe_group_max(NULL))-1)) == 0 )
	{
		errno=EINVAL;
		return NULL;
	}
	
	DEBUG_PRINTF ("spe_create_thread(%p, %p, %p, %p, 0x%x, 0x%x)\n",
		      gid, handle, argp, envp, mask, flags);

	if (check_env)
	  env_check();

	thread_store = spe_setup (gid, handle, argp, envp, flags);
	if (!thread_store)
	  {
		  DEBUG_PRINTF ("spe_setup failed\n");
		  errno=EINVAL;
		  return NULL;
	  }


	// remember the affinity mask
	//
	thread_store->mask = mask;

	DEBUG_PRINTF ("pthread_create()\n");
	rc = pthread_create (&thread_store->spe_thread, NULL,
			     spe_thread, thread_store);


	if (rc)
	  {
		  perror ("pthread_create");
		  return NULL;
	  }

	if (thread_store->flags & SPE_ISOLATE) {
		if (spe_start_isolated_app(thread_store, handle)) {
			DEBUG_PRINTF ("spe_start_isolated_app failed\n");
			return NULL;
		}
		DEBUG_PRINTF ("isolated app info sent to spe\n");
	}

	return thread_store;
}

int
spe_recycle(speid_t spe, spe_program_handle_t *handle)
{
	int ret;
	char buf = 0;
	struct thread_store *thread = (struct thread_store *)spe;

	if (!(thread->flags & SPE_ISOLATE)) {
		errno = EINVAL;
		return -errno;
	}

	thread->thread_status = 1;

	ret = pthread_create(&thread->spe_thread, NULL, spe_thread, thread);
	if (ret)
		return ret;

	return spe_start_isolated_app(spe, handle);
}

static inline int spe_wait_status(unsigned int spe_status_R)
{
	unsigned int rc, term = 0;

	rc = (spe_status_R & 0xffff0000) >> 16;
	if (spe_status_R & 0x4) {
		/* SPE halted. */
		term = SIGABRT;
	} else if (spe_status_R & 0x20) {
		/* SPE invalid instruction. */
		term = SIGILL;
	} else if (spe_status_R & 0x40) {
		/* SPE invalid ch. */
		term = SIGILL;
	} else if ((rc < 0x2000) || (rc > 0x20ff)) {
		/* Treat these as invalid stop codes. */
		term = SIGILL;
	} else if (spe_status_R & 0x1) {
		/* Thread killed for undetermined reason. */
		term = SIGKILL;
	}
	/* Return status that can be evaluated with WIFEXITED(), etc. */
	return ((rc & 0xff) << 8) | (term & 0x7f);
}

int 
spe_wait(speid_t speid, int *status, int options)
{
	struct thread_store *thread_store = speid;
	void *pthread_rc;
	int rc;

	if (!srch_thread(speid))
	{
		return -1;
	}	
	
	DEBUG_PRINTF("spu_wait(0x%x, %p, 0x%x)\n", speid, status, options);

	if (options & WNOHANG)
	{
		if (thread_store->thread_status != 99)
		{
			*status = 0;
			return 0;
		}
	}
	
	if (options & WUNTRACED)
	{
		if (thread_store->thread_status == 10 || thread_store->thread_status == 20 )
		{
			*status = 0x4;
		}
	}
	
	rc = pthread_join(thread_store->spe_thread, &pthread_rc);
	if (rc) {
		DEBUG_PRINTF("  pthread_join failed, errno=%d\n", errno);
		return -1;
	}
	if (status) {
		*status = spe_wait_status(thread_store->ret_status);
	}

		 if (!(options & WCONTINUED))
		 		 spe_cleanup(thread_store);

	DEBUG_PRINTF("Thread ended.\n");
	return rc;
}

int
spe_kill (speid_t speid, int sig)
{
	struct thread_store *thread_store = speid;

	if (!srch_thread(speid))
	{
		return -1;
	}	
	
	if (sig == SIGCONT)
	{
		int stopped = 0;
/*		
		if (thread_store->thread_status == 2) { // Running on SPU 
			pthread_kill (thread_store->spe_thread, SIGCONT);
			return 0;
		}
*/
		pthread_mutex_lock(&thread_store->event_lock);
		stopped = thread_store->event_pending;
		if ( thread_store->stop || stopped )
		{
			pthread_cond_signal(&thread_store->event_deliver);
		}
		pthread_mutex_unlock(&thread_store->event_lock);
		return 0;
	}
	if (sig == SIGKILL)
	{
		int stopped;
		int ret = 0;

		pthread_mutex_lock(&thread_store->event_lock);
		/* Tell the thread that it is to be killed.*/
		thread_store->killed = 1;

		/* See if it is halted and waiting */
		stopped = thread_store->event_pending;
		if (stopped) {
			pthread_cond_signal(&thread_store->event_deliver);
	        } else {
			ret = pthread_cancel (thread_store->spe_thread);
		}
		pthread_mutex_unlock(&thread_store->event_lock);
		return ret;
	}
	if (sig == SIGSTOP)
	{	
/*		
		if (thread_store->thread_status == 2) // Running on SPU 
			pthread_kill (thread_store->spe_thread, SIGSTOP);
		else
*/
		/* Tell the thread that it is to stop.*/
		thread_store->stop = 1;

		return 0;
	}

	errno = EINVAL;
	return -1;
}

int
validatefd(struct poll_helper *phelper, int pos, int fd)
{
	int i;
	
	for(i=0;i<pos;i++)
	{
		if (phelper[i].retfd == fd)
		{
			DEBUG_PRINTF("Removed double fd. (%i)\n ",fd);
			return 0;
		}
	}
	return 1;
}

int
spe_get_event(struct spe_event *pevents, int nevents, int timeout)
{
	int i;
	int ret_events = 0;
	int numSPEsToPoll = 0;
	int setupSPEs =0;
	int pollRet = 0;

	struct pollfd *SPEfds;

	struct poll_helper *phelper;
	
	nevents  = 1; // force the number of events returned to 1 for the moment
	pthread_mutex_lock(&grp_list.mutex);

	for (i=0 ; i < nevents ; i++)
	{
		// Clear output fields.
		pevents[i].speid = NULL;
		pevents[i].revents=0;
		pevents[i].data=0;
	}
	
	for (i=0 ; i < nevents ; i++)
	{
		int j;
		struct group_store *group = pevents[i].gid;
		struct grpListElem *elem = group->grp_members;

		DEBUG_PRINTF("spe_get_event():\n");
		DEBUG_PRINTF("  using group  : %p\n", group);
		DEBUG_PRINTF("  with members : %i\n", group->numListSize);

		for ( j=0 ; j < group->numListSize; j++)
		{
			struct thread_store *thread = elem->thread;
		
			DEBUG_PRINTF("  scan member  : %p\n", thread);
			
			// Scan for present events in the thread structures of SPEs
			if (thread->event != 0 && thread->event & pevents[i].events)
			{
				int pipeval,ret;
			
				DEBUG_PRINTF("  has event !  : 0x%04x\n", thread->event);
				
				//Found an event we're looking for.
				ret_events++;

				//Fill out return struct.
				pevents[i].revents = thread->event;
				pevents[i].speid   = thread;
				pevents[i].data    = thread->ev_data;

				//Empty the pipe
				ret=read(thread->ev_pipe[0], &pipeval, 4);
				thread->ev_data = 0;
				thread->event = 0;

				pthread_mutex_unlock(&grp_list.mutex);
				return ret_events;
			}
			
			//Decide on what fd's to poll on
			if (pevents[i].events & (SPE_EVENT_MAILBOX))
			{
				numSPEsToPoll++;
			}
			if (pevents[i].events & (SPE_EVENT_TAG_GROUP))
			{
				numSPEsToPoll++;
			}
			if (pevents[i].events & (SPE_EVENT_STOP | SPE_EVENT_DMA_ALIGNMENT |
						SPE_EVENT_SPE_ERROR | SPE_EVENT_SPE_DATA_SEGMENT | 
						SPE_EVENT_SPE_DATA_STORAGE | SPE_EVENT_SPE_TRAPPED | SPE_EVENT_THREAD_EXIT ))
			{
				numSPEsToPoll++;
			
			}
			elem=elem->next;
		}
	}
	
	if(numSPEsToPoll == 0)
	{
		pthread_mutex_unlock(&grp_list.mutex);
		errno=EINVAL;
		return -1;
	}

	if (ret_events > 0)
	{
		//printf("P1\n");
		
		//DEBUG_PRINTF("  returning !  : 0xi\n", ret_events);

		pthread_mutex_unlock(&grp_list.mutex);
		
		return ret_events;
	}
	
	DEBUG_PRINTF("  number of fd : %i\n", numSPEsToPoll);

	SPEfds=malloc (numSPEsToPoll * sizeof(struct pollfd));
	phelper=malloc (numSPEsToPoll * sizeof(struct poll_helper));

	// Set up all necessary fds to poll on and remeber what they are for.
	for (i=0 ; i < nevents ; i++)
	{
		int j;
		struct group_store *group = pevents[i].gid;
		struct grpListElem *elem = group->grp_members;

		for ( j=0 ; j < group->numListSize; j++)
		{
			struct thread_store *thread = elem->thread;
			
			if (pevents[i].events & (SPE_EVENT_MAILBOX))
			{
				SPEfds[setupSPEs].fd=thread->fd_ibox;
				SPEfds[setupSPEs].events=POLLIN;
			
				phelper[setupSPEs].event=i;
				phelper[setupSPEs].thread=thread;
				phelper[setupSPEs].retfd=-1;
				phelper[setupSPEs].type=1;
				//printf("1\n");
				setupSPEs++;
			}
			if (pevents[i].events & (SPE_EVENT_STOP | SPE_EVENT_DMA_ALIGNMENT |
						SPE_EVENT_SPE_ERROR | SPE_EVENT_SPE_DATA_SEGMENT | SPE_EVENT_INVALID_DMA_CMD |
						SPE_EVENT_SPE_DATA_STORAGE | SPE_EVENT_SPE_TRAPPED | SPE_EVENT_THREAD_EXIT ))
			{
				SPEfds[setupSPEs].fd=thread->ev_pipe[0];
				SPEfds[setupSPEs].events=POLLIN;
			
				phelper[setupSPEs].event=i;
				phelper[setupSPEs].thread=thread;
				phelper[setupSPEs].retfd=-1;
				phelper[setupSPEs].type=2;
				//printf("2\n");
				setupSPEs++;
			}
			if (pevents[i].events & (SPE_EVENT_TAG_GROUP))
			{
				SPEfds[setupSPEs].fd=thread->fd_mfc;
				SPEfds[setupSPEs].events=POLLIN;

				phelper[setupSPEs].event=i;
				phelper[setupSPEs].thread=thread;
				phelper[setupSPEs].retfd=-1;
				phelper[setupSPEs].type=3;
				if (SPEfds[setupSPEs].fd == -1)
					fprintf(stderr, "Warning: spe_get_events: attempting "
						"to wait for tag group without DMA support\n");
				//printf("3\n");
				setupSPEs++;
			}
			elem=elem->next;
		}
	}
	
	if (setupSPEs != numSPEsToPoll)
	{
		DEBUG_PRINTF("ERROR:Thread number mismatch.");
		
		free(SPEfds);
		free(phelper);
		
		pthread_mutex_unlock(&grp_list.mutex);
		errno = EFAULT;
		return -1;
	}

	pthread_mutex_unlock(&grp_list.mutex);
	
	DEBUG_PRINTF("Polling for %i fd events.\n",setupSPEs);

	pollRet = poll(SPEfds, setupSPEs, timeout);
	DEBUG_PRINTF("Poll returned %i events.\n",pollRet);
	
	ret_events = 0;
	
	// Timeout.
	if (pollRet == 0)
	{
		free(SPEfds);
		free(phelper);
		return 0;
	}
	
	
	for (i=0 ; i < setupSPEs ; i++ )
	{
		if (SPEfds[i].revents != 0 && pevents[phelper[i].event].speid == NULL )
		{
			int rc,data;
			
			if (validatefd(phelper,i,SPEfds[i].fd))
			{	
				switch (phelper[i].type) {
				case 1:
					// Read ibox data if present
	
					rc = read(SPEfds[i].fd, &data, 4);
					if (rc == 4)
					{
						phelper[i].retfd=SPEfds[i].fd;
						pevents[phelper[i].event].data = data;
						pevents[phelper[i].event].speid = phelper[i].thread;
						pevents[phelper[i].event].revents = SPE_EVENT_MAILBOX;
						ret_events++;
					}
					else
					{
						phelper[i].retfd=SPEfds[i].fd;
						pevents[phelper[i].event].data = errno;
						pevents[phelper[i].event].speid = phelper[i].thread;
						pevents[phelper[i].event].revents = SPE_EVENT_ERR;
						ret_events++;
					}
					break;
				case 2:
       	            // Read pipe data if present
	
       	            rc = read(SPEfds[i].fd, &data, 4);
       	            //pevents[phelper[i].event].revents = pevents[phelper[i].event].revents | data;
					phelper[i].retfd=SPEfds[i].fd;
       	                        	pevents[phelper[i].event].revents = data;
					pevents[phelper[i].event].speid = phelper[i].thread;
				       	pevents[phelper[i].event].data = phelper[i].thread->ev_data;
	
				       	phelper[i].thread->ev_data = 0;
				       	phelper[i].thread->event = 0;
					ret_events++;
					break;
				case 3:
					// Read tag group data if present
	
					rc = read(SPEfds[i].fd, &data, 4);
					if (rc == 4)
					{	
						phelper[i].retfd=SPEfds[i].fd;
						pevents[phelper[i].event].data = data;
						pevents[phelper[i].event].speid = phelper[i].thread;
						pevents[phelper[i].event].revents = SPE_EVENT_TAG_GROUP;
						ret_events++;
					}
					else
					{
						phelper[i].retfd=SPEfds[i].fd;
						pevents[phelper[i].event].data = errno;
						pevents[phelper[i].event].speid = phelper[i].thread;
						pevents[phelper[i].event].revents = SPE_EVENT_ERR;
						ret_events++;
					}
					break;
				}
			}
		}
	}
	
	free(SPEfds);
	free(phelper);
	//printf("P2\n");
	return ret_events;
}

int spe_get_threads(spe_gid_t gid, speid_t *spe_ids)
{
	struct group_store *group = gid;
	int i;

	if ( gid != NULL && !srch_group(gid)) 	{
		errno = ESRCH;
		return -1;
	}
	
	if (!spe_ids)
	{
		return group->numListSize;
	}
	else
	{
		struct grpListElem *elem = group->grp_members;
		
		for(i=0; i < group->numListSize ; i++)
		{
			spe_ids[i] = elem->thread;
			elem=elem->next;
			
		}
	}

	return i;
}

int spe_get_priority(spe_gid_t gid)
{
	struct group_store *group_store = gid;

	if ( gid != NULL && !srch_group(gid)) 	{
		errno = ESRCH;
		return -1;
	}
	
	return group_store->priority;
}

int spe_set_priority(spe_gid_t gid, int priority)
{
	struct group_store *group_store = gid;

	if ( gid != NULL && !srch_group(gid)) 	{
		errno = ESRCH;
		return -1;
	}

	/* Sanity: check for vaild policy/priority combination.
        */
	if (!check_priority(group_store->policy, priority))
        {
                errno=EINVAL;
                return -1;
        }
	
	group_store->priority=priority;

	/*int pthread_setschedparam(pthread_t target_thread,  int  policy,  const struct sched_param *param);*/
	return 0;
}

int
check_priority(int policy, int priority)
{
	
	switch (policy) {
	case SCHED_RR:
		if (1 > priority || priority > 99)
		{
			return 0;
		}
		break;
	case SCHED_FIFO:
		if (1 > priority || priority > 99)
		{
			return 0;
		}
		break;
	case SCHED_OTHER:
		if (0 > priority || priority > 39)
		{
			return 0;
		}
		break;
	}

	return 1;
}

int spe_get_policy(spe_gid_t gid)
{
	struct group_store *group_store = gid;

	if ( gid != NULL && !srch_group(gid)) 	{
		errno = ESRCH;
		return -1;
	}
	
	return group_store->policy;
}

spe_gid_t
spe_get_group (speid_t speid)
{
	struct thread_store *thread_store = speid;

	if (!srch_thread(speid))
	{
		return NULL;
	}	
	
	return thread_store->group_id;
}

int
spe_get_affinity( speid_t speid, unsigned long *mask)
{
	int result = 0;

	if (!srch_thread(speid))
	{
		return -1;
	}	

	printf("spe_get_affinity() not implemented in this release.\n");
	
	return result;
}

int
spe_set_affinity(speid_t speid, unsigned long mask)
{
	int result = 0;

	if (!srch_thread(speid))
	{
		return -1;
	}	
	
	printf("spe_set_affinity() not implemented in this release.\n");

	return result;
}

int 
spe_group_defaults(int policy, int priority, int spe_events)
{
        /* Sanity: check for vaild policy/priority combination.
	 */
	
	if (!check_priority(policy, priority))
	{
		errno=EINVAL;
		return -1;
	}

	default_policy = policy;
	default_priority = priority;
	default_eventmask= spe_events;

	return 0;
}

int spe_set_app_data( speid_t speid, void* data)
{
	struct thread_store *thread_store = speid;

	if (!srch_thread(speid))
		return -1;
	
	thread_store->app_data = data;
	
	return 0;
}

int spe_get_app_data( speid_t speid, void** p_data)
{
	struct thread_store *thread_store = speid;

	if (!srch_thread(speid))
		return -1;
	
	*p_data = thread_store->app_data;
	
	return 0;
}
