/**************************************************************************** * Copyright (c) 2006 DST Technologies Inc. All Rights Reserved. * * Module: EVENTS * * Description: This is a decent implementation of events under Linux. * * Notes: This implementation poses some limitations, some of which can * be changed by adjusting some defines: * - Max number of events, shared and local, in the system is * defined by MAX_NOF_EVENTS * - Max number of processes that can share the same event at * any given time is defined by MAX_NOF_PROCS * - When a event is deleted, all tasks within the calling process * that may be pending on the event will be unlocked with an * error code. * ***************************************************************************/ /*============================ * Includes *===========================*/ #include "lld_os.h" #include "dsthallocal.h" #include "lld_local.h" #include "os.h" /*============================ * Defines *===========================*/ #define UNUSED_EVENT 0xffffffff #define SIGNALED 1 #define NOT_SIGNALED 0 #define INVALID_PID 0xffffffff /* Invalid process ID */ #define Event(i) (EventsPtr->_Events[i]) #define EventPtr(i) (&(EventsPtr->_Events[i])) /*============================ * Structures *===========================*/ typedef struct { DS_U32 Status; /* = UNUSED_EVENT if not used */ DS_U32 Name; /* 0 for local events */ DS_U32 OwnerPid[MAX_NOF_PROCS]; /* Owner process IDs. Contains */ /* INVALID_PID for non used entries */ WAIT_Q WaitQ; /* Queue of processes waiting on */ /* the event */ }EVNT_STR; typedef struct { EVNT_STR _Events[MAX_NOF_EVENTS]; }EVENTS; /*============================ * Shared variables *===========================*/ static EVENTS *EventsPtr = NULL; /* Pointer to events area */ /*============================ * Prototypes *===========================*/ static DS_U32 EventCreate (DS_U32 EventName, DS_BOOL Signaled, DS_U32 *EvntId); static DS_U32 EventWait (DS_U32 EvntId, DS_U32 Timeout, DS_U32 *TimeLeft, DS_U32 AutoReset); static DS_U32 EventSet (DS_U32 EvntId); static DS_U32 EventReset (DS_U32 EvntId); static DS_U32 EventDelete (DS_U32 EvntId); static DS_U32 EventInfo (DS_U32 EvntId, DS_U32 *Size, void *Dest); static EVNT_STR *GetEvPtr (DS_U32 EvntId, DS_U32 *ProcIndex); /*^^*************************************************************************** * void EventInit (void) * * Description: Initialize kernel events for first use * * Entry : None * * Return: None * * Notes : * **************************************************************************^^*/ void EventInit (void) { DS_U32 i; EVNT_STR *EvPtr; /*======================================= * Allocate shared mem for events *======================================*/ if (EventsPtr == NULL) { EventsPtr = lld_os_malloc (sizeof(EVENTS)); /*======================================= * Initialize events array *======================================*/ for (i=0; iStatus = UNUSED_EVENT; } } } /*^^*************************************************************************** * void EventCleanup (void) * * Description: Cleanup for module shutdown * * Entry : None * * Return: None * * Notes : * **************************************************************************^^*/ void EventCleanup (void) { if (EventsPtr != NULL) { lld_os_free (EventsPtr); } } /*^^*************************************************************************** * void EventProcessExit (void) * * Description: Cleanup for all events when a process exits normally * or upnormally. * * Entry : None * * Return: None * * Notes : * **************************************************************************^^*/ void EventProcessExit (void) { DS_U32 i, j, k, ProcId; EVNT_STR *EvPtr; ProcId = mCurrentPid; /*================================ * For all events in the system *===============================*/ for (i=0; iStatus != UNUSED_EVENT) { k = 0; /* Ev reference count */ /*=============================== * Find ev reference count *==============================*/ for (j=0; jOwnerPid[j] == ProcId) { EvPtr->OwnerPid[j] = INVALID_PID; } if (EvPtr->OwnerPid[j] != INVALID_PID) { k++; /* Other process owns the event */ } } /*============================================ * If no other processes reference the ev *===========================================*/ if (k == 0) { EvPtr->Status = UNUSED_EVENT; } } } } /*^^*************************************************************************** * DS_U32 EventOperation (OBJ_OPER *EvPrm) * * Description: Perform event operation defined in EvPrm * * Entry : EvPrm = Pointer to OBJ_OPER struc holding the requested operation. * * Return: 0 or -ERESTARTSYS * EvPrm.RetCode hold return values (varies by the opcode) * EvPrm.Prm2 will also be set as follows: * Create: EvPrm.Prm2 = Event ID. = 0 if any error. * Wait: EvPrm.Prm2 = Adjusted timeout if ret due to signal * Info: EvPrm.Prm2 = Info size * * Notes : * **************************************************************************^^*/ DS_U32 EventOperation (OBJ_OPER *EvPrm) { switch (EvPrm->Opcode) { case OBJ_OPCODE_CREATE: EvPrm->RetCode = EventCreate (EvPrm->Prm1, EvPrm->Prm2, &(EvPrm->Prm2)); break; case OBJ_OPCODE_DELETE: EvPrm->RetCode = EventDelete (EvPrm->Prm1); break; case OBJ_OPCODE_WAIT: EvPrm->RetCode = EventWait (EvPrm->Prm1, EvPrm->Prm2, &(EvPrm->Prm2), EvPrm->RetCode); if (EvPrm->RetCode == OBJ_SIGNAL_RCV) { return (ERR_RESTART_SYS); } break; case OBJ_OPCODE_SET: EvPrm->RetCode = EventSet (EvPrm->Prm1); break; case OBJ_OPCODE_RESET: EvPrm->RetCode = EventReset (EvPrm->Prm1); break; case OBJ_OPCODE_INFO: EvPrm->RetCode = EventInfo (EvPrm->Prm1, &(EvPrm->Prm2), (void *)(EvPrm->Prm2)); break; default: EvPrm->RetCode = OBJ_INVALID_OP; break; } return (0); } /*^^*************************************************************************** * static DS_U32 EventCreate (DS_U32 EventName, DS_BOOL Signaled, DS_U32 *EvntId) * * Description: Creates an event object with the specified status * * Entry : EventName = Unique number identifying the event. * Set to NULL to create unnamed event (can not be * used for inter-process synchronization) * Status = Initial status (Set=_TRUE_, Reset=_FALSE_) * EvntId = Pointer to receive event ID * * Return: Entry EvntId holds event ID. =0 if any error. Return codes are: * OBJ_OK Operation completed * OBJ_INVALID_ID Invalid ev id or process does not own ev * OBJ_MAX_PROCS Max number of procs reached * OBJ_MAX_OBJS Max number of events reached * * Notes : If the same process creates a shared event multiple times, * it's pid will appear in the event's OwnerPid list only once. * In this case, only one delete is required by the calling process * to delete the event. * **************************************************************************^^*/ static DS_U32 EventCreate (DS_U32 EventName, DS_BOOL Signaled, DS_U32 *EvntId) { DS_U32 i, j; EVNT_STR *EvPtr = NULL; *EvntId = 0; if (EventsPtr == NULL) { return (OBJ_INVALID_ID); } /*================================ * If shared event *===============================*/ if (EventName != 0) { /*=========================================== * Find if shared ev exist (same name) *==========================================*/ for (i=0; iStatus != UNUSED_EVENT) && (EvPtr->Name == EventName)) { /*=============================================== * Find if current process already owns the ev *==============================================*/ for (j=0; jOwnerPid[j] == mCurrentPid) { *EvntId = (i+1); return (OBJ_OK); } } /*=============================================== * No, add process to owners list *==============================================*/ for (j=0; jOwnerPid[j] == INVALID_PID) { EvPtr->OwnerPid[j] = mCurrentPid; *EvntId = (i+1); return (OBJ_OK); } } return (OBJ_MAX_PROCS); } } } /*====================================================== * If ev does not exist or local ev, create new one *=====================================================*/ for (i=0; iStatus == UNUSED_EVENT) { if (Signaled) { EvPtr->Status = SIGNALED; } else { EvPtr->Status = NOT_SIGNALED; } EvPtr->Name = EventName; for (j=0; jOwnerPid[j] = INVALID_PID; } EvPtr->OwnerPid[0] = mCurrentPid; lld_init_wait_q (&(EvPtr->WaitQ)); *EvntId = (i+1); return (OBJ_OK); } } return (OBJ_MAX_OBJS); } /*^^*************************************************************************** * static DS_U32 EventWait (DS_U32 EvntId, DS_U32 Timeout, DS_U32 *TimeLeft, * DS_U32 AutoReset) * * Description: Locks a event (decrement count) with a timeout * * Entry : EvntId = Returned by EventCreate * Timeout = In 1/100 of a second increments. * = 0 to return immidiately if the resource not available * = 0xffffffff to wait forever on the resource * TimeLeft = Pointer to DS_U32 to receive remaining timeout if the * wait operation interrupted by a signal (if return value * is OBJ_SIGNAL_RCV) * AutoReset = _TRUE_ to reset the event after receiving it * * Return: OBJ_OK Operation completed * OBJ_INVALID_ID Invalid ev id or process does not own ev * OBJ_TIMEOUT Timedout * OBJ_SIGNAL_RCV Signal received * * Notes : - If ev status = SIGNALED and AutoReset = _FALSE_, the function will * return immediately with OBJ_OK * - If ev status = SIGNALED and AutoReset = _TRUE_, Status will be set * to NOT_SIGNALED and the function will return with OBJ_OK * - If ev status = NOT_SIGNALED and timeout = 0, will return TIMEOUT * - If ev status = NOT_SIGNALED and timeout > 0, other processes will * be scheduled until either timeout is elapsed (return TIMEOUT), * the calling process detects that ev state > 0 (return OK) or the * process receives a signal (return SIGNAL_RCV) * * **** VERY IMPORTANT *** * This locking mechanism does not support priority inversion since * linux does not support (_TRUE_) task priorities. * **************************************************************************^^*/ static DS_U32 EventWait (DS_U32 EvntId, DS_U32 Timeout, DS_U32 *TimeLeft, DS_U32 AutoReset) { DS_U32 i, Idx; EVNT_STR *EvPtr; while (_TRUE_) { /*=========================================== * Make sure ev still exist (not deleted) *==========================================*/ EvPtr = GetEvPtr(EvntId, &Idx); if (EvPtr == NULL) { return (OBJ_INVALID_ID); } /*========================== * If event is signaled *=========================*/ if (EvPtr->Status == SIGNALED) { if (AutoReset) { EvPtr->Status = NOT_SIGNALED; } return (OBJ_OK); } /*================================== * If not signaled and timeout = 0 *=================================*/ if (Timeout == 0) { return (OBJ_TIMEOUT); } /*============================================================== * If not signaled and timeout, wait signaled or timeout *=============================================================*/ if (Timeout == OS_WAIT_FOREVER) { lld_wait_on_q (&(EvPtr->WaitQ)); i = Timeout; /* Any non zero value */ } else { i = lld_timeout_wait_on_q (&(EvPtr->WaitQ), Timeout); } if (i == 0) { return (OBJ_TIMEOUT); } if (lld_os_signal_detected()) { *TimeLeft = i; return (OBJ_SIGNAL_RCV); } if (Timeout != OS_WAIT_FOREVER) { Timeout = i; } } } /*^^*************************************************************************** * static DS_U32 EventSet (DS_U32 EvntId) * * Description: Set event state to signaled * * Entry : EvntId = Returned by EventCreate * * Return: OBJ_OK Operation completed * OBJ_INVALID_ID Invalid ev id or process does not own ev * * Notes: * **************************************************************************^^*/ static DS_U32 EventSet (DS_U32 EvntId) { EVNT_STR *EvPtr; EvPtr = GetEvPtr(EvntId, NULL); if (EvPtr == NULL) { return (OBJ_INVALID_ID); } if (EvPtr->Status == NOT_SIGNALED) { EvPtr->Status = SIGNALED; lld_wakeup_wait_q (&(EvPtr->WaitQ)); } return (OBJ_OK); } /*^^*************************************************************************** * static DS_U32 EventReset (DS_U32 EvntId) * * Description: Set event to not signaled state * * Entry : EvntId = Returned by EventCreate * * Return: OBJ_OK Operation completed * OBJ_INVALID_ID Invalid ev id or process does not own ev * * Notes: * **************************************************************************^^*/ static DS_U32 EventReset (DS_U32 EvntId) { EVNT_STR *EvPtr; EvPtr = GetEvPtr(EvntId, NULL); if (EvPtr == NULL) { return (OBJ_INVALID_ID); } EvPtr->Status = NOT_SIGNALED; return (OBJ_OK); } /*^^*************************************************************************** * static DS_U32 EventDelete (DS_U32 EvntId) * * Description: Deletes an event. * * Entry : EvntId = Returned by EventCreate * * Return: OBJ_OK Operation completed * OBJ_INVALID_ID Invalid ev id or process does not own ev * * Notes: The event will be deleted only if its reference count is 0. * All tasks waiting on the event will be unblocked with error * return code. * The only caveat here is: * If a taskA deletes a event while other task(s) are pending or * already locked it, and TaskA then creates another event before * the other tasks gets notified of the deleted event, it is * possible that the returned EvntId will be the same as the just * deleted event and hence other pending task(s) will continue to * run normally. * **************************************************************************^^*/ static DS_U32 EventDelete (DS_U32 EvntId) { DS_U32 i, j; EVNT_STR *EvPtr; EvPtr = GetEvPtr(EvntId, &j); if (EvPtr == NULL) { return (OBJ_INVALID_ID); } /*====================================== * Remove process from owners list *=====================================*/ EvPtr->OwnerPid[j] = INVALID_PID; /*================================================== * Check if other process still use the event *=================================================*/ j = 0; for (i=0; iOwnerPid[i] != INVALID_PID) { j = 1; /* Other process is using it */ break; } } /*================================================ * If no other process is using the event *===============================================*/ if (j == 0) { EvPtr->Status = UNUSED_EVENT; } /*====================================== * Awake all tasks waiting on the ev *=====================================*/ lld_wakeup_wait_q (&(EvPtr->WaitQ)); return (OBJ_OK); } /*^^*************************************************************************** * static DS_U32 EventInfo (DS_U32 EvntId, DS_U32 *Size, void *Dest) * * Description: Return a copy of kernel structure for a event. * * Entry : EvntId = User mode ev ID * Size = Pointer to receive size of ev structure * Dest = Area to copy ev info to * * Return: * * Notes: This fucntion is provided for diagnostic purpose only. * **************************************************************************^^*/ static DS_U32 EventInfo (DS_U32 EvntId, DS_U32 *Size, void *Dest) { EVNT_STR *EvPtr; EvntId--; if ((EvntId >= MAX_NOF_EVENTS) || (EventsPtr == NULL)) { return (OBJ_INVALID_ID); } EvPtr = EventPtr(EvntId); *Size = sizeof (EVNT_STR); lld_os_copy_to_user(Dest, (DS_S8 *)EvPtr, sizeof(EVNT_STR)); return (OBJ_OK); } /*^^*************************************************************************** * static EVNT_STR *GetEvPtr (DS_U32 EvntId, DS_U32 *ProcIndex) * * Description: Find event corresponding to a ev ID and process ID * * Entry : EvntId = User mode ev ID * ProcIndex = Ptr to receive process index in OwnerPid array or * NULL if not needed * * Return: Pointer to event struc or NULL * * Notes: This call will fail if the event does not exist or if * the caller process did not create the event. * **************************************************************************^^*/ static EVNT_STR *GetEvPtr (DS_U32 EvntId, DS_U32 *ProcIndex) { EVNT_STR *EvPtr; DS_U32 i, ProcId; EvntId--; if ((EvntId >= MAX_NOF_EVENTS) || (EventsPtr == NULL)) { return (NULL); } ProcId = mCurrentPid; /* Current process ID */ EvPtr = EventPtr(EvntId); if (EvPtr->Status == UNUSED_EVENT) { return (NULL); } for (i=0; iOwnerPid[i] == ProcId) { if (ProcIndex != NULL) { *ProcIndex = i; } return (EvPtr); } } return (NULL); }