/*---------------------------------------------------------------------------* Project: CtrBrom - libraries - OS File: os_alarm.c Copyright 2008 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: $ $Rev$ $Author$ *---------------------------------------------------------------------------*/ #include //---------------------------------------------------------------------- #ifdef SDK_ARM9 //---- timer interrupt mask (must be same number with OSi_ALARM_TIMER) #define OSi_ALARM_IE_TIMER (OS_IE_TIMER0 << OSi_ALARM_TIMER) //---- timer control setting for alarm #define OSi_ALARM_TIMERCONTROL ( REG_OS_TM0CNT_H_E_MASK | REG_OS_TM0CNT_H_I_MASK | OS_TIMER_PRESCALER_64 ) #endif // SDK_ARM9 //---- flag for initialization alarm static u16 i_osUseAlarm = FALSE; //---- alarm queue static struct OSiAlarmQueue i_osAlarmQueue; u16 i_osIsTimerReserved(int timerNum); void i_osSetTimerReserved(int timerNum); void i_osUnsetTimerReserved(int timerNum); static void i_osSetTimer(OSAlarm *alarm); static void i_osInsertAlarm(OSAlarm *alarm, OSTick fire); static void i_osAlarmHandler(void *arg); void i_osArrangeTimer(void); /*---------------------------------------------------------------------------* Name: i_osSetTimer Description: set Timer Arguments: alarm pointer to alarm to set timer Returns: None. *---------------------------------------------------------------------------*/ static void i_osSetTimer(OSAlarm *alarm) { OSTimerCount timerCount; s64 delta; OSTick tick = osGetTick(); delta = (s64)(alarm->fire - tick); //---- let timer be disable #ifdef SDK_ARM11 osClearTimerEventFlag(OSi_ALARM_TIMER); #else // SDK_ARM9 osSetTimerControl(OSi_ALARM_TIMER, 0); #endif // SDK_ARM9 //---- set interrupt callback osSetInterruptHandler( OSi_ALARM_IE_TIMER_ID, i_osArrangeTimer ); // osEnterTimerCallback(OSi_ALARM_TIMER, i_osAlarmHandler, NULL); //---- set count and let timer be enable if (delta <= 0) { #ifdef SDK_ARM11 timerCount = 1; #else // SDK_ARM9 // ARM9は0xFFFF設定禁止 timerCount = (u16)~1; #endif // SDK_ARM9 } else if (delta < OS_TICK_LO_LIMIT) { #ifdef SDK_ARM11 timerCount = delta; #else // SDK_ARM9 timerCount = (u16)(~delta); #endif // SDK_ARM9 } else { #ifdef SDK_ARM11 timerCount = OS_TICK_LO_LIMIT; #else // SDK_ARM9 timerCount = 0; #endif // SDK_ARM9 } //osPrintf( "**i_osSetTimer alarm=%x, fire=%llx time=%llx delta=%lld timeCount=%x \n", alarm, alarm->fire, time, delta, (int)timerCount ); #ifdef SDK_ARM11 osStartTimer(OSi_ALARM_TIMER, timerCount, 0); #else // SDK_ARM9 osSetTimerCount((OSTimer)OSi_ALARM_TIMER, timerCount); osSetTimerControl(OSi_ALARM_TIMER, (u16)OSi_ALARM_TIMERCONTROL); #endif // SDK_ARM9 //---- TIMER IRQ Enable (void)osEnableInterruptID(OSi_ALARM_IE_TIMER_ID); } /*---------------------------------------------------------------------------* Name: osInitAlarm Description: Initialize alarm system Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void osInitAlarm(void) { if (!i_osUseAlarm) { i_osUseAlarm = TRUE; //---- check if tick system is available SDK_ASSERTMSG(osIsTickAvailable(), "osInitAlarm: alarm system needs of tick system."); //---- OS reserves OSi_ALARM_TIMER SDK_ASSERT(!i_osIsTimerReserved(OSi_ALARM_TIMER)); i_osSetTimerReserved(OSi_ALARM_TIMER); #ifdef SDK_ARM11 osDisableTimerReload(OSi_ALARM_TIMER); #endif // SDK_ARM11 //---- clear alarm list i_osAlarmQueue.head = NULL; i_osAlarmQueue.tail = NULL; //---- TIMER IRQ Disable (void)osDisableInterruptID(OSi_ALARM_IE_TIMER_ID); } } /*---------------------------------------------------------------------------* Name: osEndAlarm Description: end alarm system Arguments: None Returns: None *---------------------------------------------------------------------------*/ void osEndAlarm(void) { OSIntrMode enabled; SDK_ASSERT(i_osUseAlarm); enabled = osDisableInterrupts(); //---- check if any alarm exists if (i_osUseAlarm) { SDK_ASSERTMSG(!i_osAlarmQueue.head, "osEndAlarm: Cannot end alarm system while using alarm."); //---- unset timer reservation by OS SDK_ASSERT(i_osIsTimerReserved(OSi_ALARM_TIMER)); i_osUnsetTimerReserved(OSi_ALARM_TIMER); i_osUseAlarm = FALSE; } (void)osRestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: osIsAlarmAvailable Description: check alarm system is available Arguments: None Returns: if available, TRUE. *---------------------------------------------------------------------------*/ BOOL osIsAlarmAvailable(void) { return i_osUseAlarm; } /*---------------------------------------------------------------------------* Name: osCreateAlarm Description: Create alarm Arguments: alarm pointer to alarm to be initialized Returns: None. *---------------------------------------------------------------------------*/ void osCreateAlarm(OSAlarm *alarm) { SDK_ASSERT(i_osUseAlarm); SDK_ASSERT(alarm); alarm->handler = 0; alarm->tag = 0; } /*---------------------------------------------------------------------------* Name: i_osInsertAlarm Description: Insert alarm. Needs to be called interrupts disabled. Arguments: alarm pointer to alarm to be set fire tick to fire (only for one shot alarm) Returns: None. *---------------------------------------------------------------------------*/ static void i_osInsertAlarm(OSAlarm *alarm, OSTick fire) { OSAlarm *prev; OSAlarm *next; //---- caluculate next fire for periodic alarm if (alarm->period > 0) { OSTick tick = osGetTick(); fire = alarm->start; if (alarm->start < tick) { fire += alarm->period * ((tick - alarm->start) / alarm->period + 1); } } //---- set tick to fire alarm->fire = fire; //---- insert to list for (next = i_osAlarmQueue.head; next; next = next->next) { // if ( next->fire <= fire ) if ((s64)(fire - next->fire) >= 0) { continue; } //---- insert alarm before 'next' alarm->prev = next->prev; next->prev = alarm; alarm->next = next; prev = alarm->prev; if (prev) { prev->next = alarm; } else { i_osAlarmQueue.head = alarm; i_osSetTimer(alarm); } return; } //---- insert alarm after tail alarm->next = 0; prev = i_osAlarmQueue.tail; i_osAlarmQueue.tail = alarm; alarm->prev = prev; if (prev) { prev->next = alarm; } else { i_osAlarmQueue.head = i_osAlarmQueue.tail = alarm; i_osSetTimer(alarm); } } /*---------------------------------------------------------------------------* Name: osSetAlarm Description: Set alarm as a relative tick Arguments: alarm pointer to alarm to be set tick ticks to count before firing handler alarm handler to be called arg argument of handler Returns: None. *---------------------------------------------------------------------------*/ void osSetAlarm(OSAlarm *alarm, OSTick tick, OSAlarmHandler handler, void *arg) { OSIntrMode enabled; //osPrintf( "**osSetAlarm e=%x alarm=%x tick=%x handler%x\n", enabled, alarm, tick, handler ); SDK_ASSERT(i_osUseAlarm); SDK_ASSERTMSG(handler, "osSetAlarm: handler must not be NULL."); if (!alarm || alarm->handler) { #ifndef SDK_FINALROM osPanic("alarm could be already used."); #else osPanic(""); #endif } enabled = osDisableInterrupts(); //---- clear periodic info alarm->period = 0; //---- set handler alarm->handler = handler; alarm->arg = arg; //---- insert alarm i_osInsertAlarm(alarm, osGetTick() + tick); (void)osRestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: osSetPeriodicAlarm Description: set periodic alarm Arguments: alarm pointer to alarm to be set start origin of the period in absolute tick period ticks to count for each period handler alarm handler to be called arg argument of handler Returns: None. *---------------------------------------------------------------------------*/ void osSetPeriodicAlarm(OSAlarm *alarm, OSTick start, OSTick period, OSAlarmHandler handler, void *arg) { u32 enabled; //osPrintf( "**SetPeriodicAlarm s=%llx p=%llx\n", start, period ); SDK_ASSERT(i_osUseAlarm); SDK_ASSERTMSG(handler, "osSetPeriodicAlarm: handler must not be NULL\n"); SDK_ASSERTMSG(period > 0, "osSetPeriodicAlarm: bad period specified."); if (!alarm || alarm->handler) { #ifndef SDK_FINALROM osPanic("alarm could be already used."); #else osPanic(""); #endif } enabled = osDisableInterrupts(); //---- set periodic info alarm->period = period; alarm->start = start; //---- set handler alarm->handler = handler; alarm->arg = arg; //---- insert periodic alarm i_osInsertAlarm(alarm, 0); (void)osRestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: osCancelAlarm Description: Cancel alarm Arguments: alarm pointer to alarm to be canceled Returns: None. *---------------------------------------------------------------------------*/ void osCancelAlarm(OSAlarm *alarm) { OSAlarm *next; u32 enabled; SDK_ASSERT(i_osUseAlarm); SDK_ASSERT(alarm); enabled = osDisableInterrupts(); if (alarm->handler == NULL) { (void)osRestoreInterrupts(enabled); return; } //---- remove alarm next = alarm->next; if (next == NULL) { i_osAlarmQueue.tail = alarm->prev; } else { next->prev = alarm->prev; } if (alarm->prev) { alarm->prev->next = next; } else { i_osAlarmQueue.head = next; if (next) { i_osSetTimer(next); } } alarm->handler = NULL; alarm->period = 0; // not periodic alarm (void)osRestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: i_osAlarmHandler Description: handler timer interrupt Arguments: arg dummy Returns: None. *---------------------------------------------------------------------------*/ #include asm void i_osAlarmHandler( void* arg ) { INASM_EXTERN( i_osArrangeTimer ) stmfd sp!, {r0, lr} /* コールスタックを 8 バイト整合 */ bl i_osArrangeTimer ldmfd sp!, {r0, lr} /* コールスタックを 8 バイト整合 */ bx lr } #include /*---------------------------------------------------------------------------* Name: i_osArrangeTimer Description: handler timer interrupt. called from i_osAlarmHandler Arguments: None Returns: None. *---------------------------------------------------------------------------*/ void i_osArrangeTimer(void) { OSTick tick; OSAlarm *alarm; OSAlarm *next; OSAlarmHandler handler; //---- To be timer-irq Disable (void)osDisableInterruptID(OSi_ALARM_IE_TIMER_ID); //---- let timer be disable osSetTimerControl(OSi_ALARM_TIMER, 0); //---- set check flag timer interrupt osSetInterruptCheckID(OSi_ALARM_IE_TIMER_ID); tick = osGetTick(); alarm = i_osAlarmQueue.head; //osPrintf( "**Arrange alarm=%x time=%llx file=%llx\n", alarm, time, alarm->fire ); //---- no alarm if (alarm == NULL) { return; } //---- not reach to time of top alarm if (tick < alarm->fire) { i_osSetTimer(alarm); return; } //---- move next alarm to top next = alarm->next; i_osAlarmQueue.head = next; if (next == NULL) { i_osAlarmQueue.tail = NULL; } else { next->prev = NULL; } //---- call user alarm handler handler = alarm->handler; if (alarm->period == 0) { alarm->handler = NULL; } if (handler) { (handler) (alarm->arg); } //---- if alarm is periodic, re-inter to list if (alarm->period > 0) { alarm->handler = handler; i_osInsertAlarm(alarm, 0); } //---- set timer if (i_osAlarmQueue.head) { i_osSetTimer(i_osAlarmQueue.head); } } /*---------------------------------------------------------------------------* Name: osSetAlarmTag Description: set tag which is used osCancelAlarms Arguments: alarm alarm to be set tag tag tagNo Returns: None. *---------------------------------------------------------------------------*/ void osSetAlarmTag(OSAlarm *alarm, u32 tag) { SDK_ASSERT(i_osUseAlarm); SDK_ASSERT(alarm); SDK_ASSERTMSG(tag > 0, "osSetAlarmTag: Tag must be >0."); alarm->tag = tag; } /*---------------------------------------------------------------------------* Name: osCancelAlarms Description: cancel alarms which have specified tag Arguments: tag tagNo. to be cancelled. not 0 Returns: None. *---------------------------------------------------------------------------*/ void osCancelAlarms(u32 tag) { u32 enabled; OSAlarm *alarm; OSAlarm *next; SDK_ASSERT(i_osUseAlarm); SDK_ASSERTMSG(tag > 0, "OSCancelAlarms: Tag must be >0."); if (tag == 0) { return; } enabled = osDisableInterrupts(); for (alarm = i_osAlarmQueue.head, next = alarm ? alarm->next : NULL; alarm; alarm = next, next = alarm ? alarm->next : NULL) { if (alarm->tag == tag) { //---- cancel alarm osCancelAlarm(alarm); } } (void)osRestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: osCancelAllAlarms Description: cancel all alarms Arguments: None Returns: None. *---------------------------------------------------------------------------*/ void osCancelAllAlarms(void) { u32 enabled; OSAlarm *alarm; OSAlarm *next; SDK_ASSERT(i_osUseAlarm); enabled = osDisableInterrupts(); for (alarm = i_osAlarmQueue.head, next = alarm ? alarm->next : NULL; alarm; alarm = next, next = alarm ? alarm->next : NULL) { //---- cancel alarm osCancelAlarm(alarm); } (void)osRestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: i_osGetAlarmQueue Description: get alarm queue Arguments: None Returns: alarm queue. *---------------------------------------------------------------------------*/ struct OSiAlarmQueue *i_osGetAlarmQueue(void) { return &i_osAlarmQueue; } //================================================================================ // FOR DEBUG //================================================================================ /*---------------------------------------------------------------------------* Name: osGetNumberOfAlarm Description: get number of alarm Arguments: None Returns: number of alarm *---------------------------------------------------------------------------*/ int osGetNumberOfAlarm(void) { OSIntrMode enabled = osDisableInterrupts(); OSAlarm* p = i_osAlarmQueue.head; int num = 0; while(p) { num ++; p = p->next; } (void)osRestoreInterrupts(enabled); return num; } /*---------------------------------------------------------------------------* Name: osGetAlarmResource Description: store resources of alarm to specified pointer Arguments: resource pointer to store alarm resources Returns: TRUE ... success (always return this now) FALSE ... fail *---------------------------------------------------------------------------*/ BOOL osGetAlarmResource(OSAlarmResource *resource) { resource->num = osGetNumberOfAlarm(); return TRUE; }