]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/compat/posix/v2_0/src/time.cxx
Initial revision
[karo-tx-redboot.git] / packages / compat / posix / v2_0 / src / time.cxx
1 //==========================================================================
2 //
3 //      time.cxx
4 //
5 //      POSIX time functions implementation
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):           nickg
44 // Contributors:        nickg
45 // Date:                2000-03-27
46 // Purpose:             POSIX time functions implementation
47 // Description:         This file contains the implementation of the POSIX time
48 //                      functions.
49 //              
50 //              
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include <pkgconf/posix.h>
57
58 #ifdef CYGPKG_POSIX_CLOCKS
59
60 #include <pkgconf/hal.h>
61 #include <pkgconf/kernel.h>
62
63 #include <cyg/kernel/ktypes.h>          // base kernel types
64 #include <cyg/infra/cyg_trac.h>         // tracing macros
65 #include <cyg/infra/cyg_ass.h>          // assertion macros
66
67 #include "pprivate.h"                   // POSIX private header
68
69 #include <time.h>                       // our header
70
71 #include <cyg/kernel/thread.hxx>
72 #include <cyg/kernel/clock.hxx>
73
74 #include <cyg/kernel/thread.inl>
75 #include <cyg/kernel/clock.inl>
76
77 // -------------------------------------------------------------------------
78 // Internal definitions
79
80 // Handle entry to a pthread package function. 
81 #define TIME_ENTRY() CYG_REPORT_FUNCTYPE( "returning %d" );
82
83 // Do a time package defined return. This requires the error code
84 // to be placed in errno, and if it is non-zero, -1 returned as the
85 // result of the function. This also gives us a place to put any
86 // generic tidyup handling needed for things like signal delivery and
87 // cancellation.
88 #define TIME_RETURN(err)                        \
89 CYG_MACRO_START                                 \
90     int __retval = 0;                           \
91     if( err != 0 ) __retval = -1, errno = err;  \
92     CYG_REPORT_RETVAL( __retval );              \
93     return __retval;                            \
94 CYG_MACRO_END
95
96 //==========================================================================
97 // Timer control structures
98
99 #ifdef CYGPKG_POSIX_TIMERS
100 typedef struct
101 {
102     timer_t             id;             // id value for checking
103     Cyg_Alarm           *alarm;         // eCos alarm object
104     cyg_bool            armed;          // is alarm enabled?
105     cyg_bool            pending;        // is expiry pending?
106     int                 overrun;        // Overrun count
107     struct sigevent     sigev;          // Sigevent to raise on expiry
108     
109     // Space for alarm object
110     cyg_uint8           alarm_obj[sizeof(Cyg_Alarm)];
111     
112 } posix_timer;
113
114 // Mutex for controlling access to shared data structures
115 static Cyg_Mutex timer_mutex CYGBLD_POSIX_INIT;
116
117 // Array of timer objects
118 static posix_timer timer_table[_POSIX_TIMER_MAX];
119
120 // Index of next timer to allocate from array
121 static int timer_next = 0;
122
123 // This is used to make timer_t values unique even when reusing
124 // a table slot. This allows _POSIX_TIMER_MAX to range
125 // up to 1024.
126 #define TIMER_ID_COOKIE_INC 0x00000400
127 #define TIMER_ID_COOKIE_MASK (TIMER_ID_COOKIE_INC-1)
128 static timer_t timer_id_cookie = TIMER_ID_COOKIE_INC;
129
130 #endif // ifdef CYGPKG_POSIX_TIMERS
131
132 //-----------------------------------------------------------------------------
133 // new operator to allow us to invoke the constructor on
134 // posix_timer.alarm_obj.
135
136 inline void *operator new(size_t size,  cyg_uint8 *ptr) { return (void *)ptr; };
137
138 //==========================================================================
139 // Time conversion variables
140 // These are used to interconvert between ticks and POSIX timespecs.
141
142 // Converters from sec and ns to ticks
143 static struct Cyg_Clock::converter ns_converter, sec_converter;
144
145 // Converters from ticks to sec and ns
146 static struct Cyg_Clock::converter ns_inverter, sec_inverter;
147
148 // tickns is the number of nanoseconds per tick.
149 static cyg_tick_count tickns;
150
151 static cyg_bool converters_initialized = false;
152
153 //==========================================================================
154 // Local functions
155
156 static void init_converters()
157 {
158     if( !converters_initialized )
159     {
160
161         // Create the converters we need.
162         Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1, &ns_converter );
163         Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000000000, &sec_converter );
164         Cyg_Clock::real_time_clock->get_clock_to_other_converter( 1, &ns_inverter );    
165         Cyg_Clock::real_time_clock->get_clock_to_other_converter( 1000000000, &sec_inverter );
166
167         tickns = Cyg_Clock::convert( 1, &ns_inverter );
168             
169         converters_initialized = true;
170     }
171 }
172
173 static cyg_bool valid_timespec( const struct timespec *tp )
174 {
175     // Fail a NULL pointer
176     if( tp == NULL ) return false;
177
178     // Fail illegal nanosecond values
179     if( tp->tv_nsec < 0 || tp->tv_nsec > 1000000000 )
180         return false;
181
182     return true;
183 }
184
185 externC cyg_tick_count cyg_timespec_to_ticks( const struct timespec *tp,
186                                               cyg_bool roundup)
187 {
188     init_converters();
189
190     // Short circuit zero timespecs
191     if( tp->tv_sec == 0 && tp->tv_nsec == 0 )
192     {
193         return 0;
194     }
195         
196     // Convert the seconds field to ticks.
197     cyg_tick_count ticks = Cyg_Clock::convert( tp->tv_sec, &sec_converter );
198
199     if( roundup )
200     {
201         // Convert the nanoseconds. We add (tickns-1) to round the value up
202         // to the next whole tick.
203
204         ticks += Cyg_Clock::convert( (cyg_tick_count)tp->tv_nsec+tickns-1, &ns_converter );    
205     }
206     else
207     {
208         // Convert the nanoseconds. This will round down to nearest whole tick.
209         ticks += Cyg_Clock::convert( (cyg_tick_count)tp->tv_nsec, &ns_converter );
210     }
211
212     return ticks;
213 }
214
215 externC void cyg_ticks_to_timespec( cyg_tick_count ticks, struct timespec *tp )
216 {
217     init_converters();
218
219     // short circuit zero ticks values
220     if( ticks == 0 )
221     {
222         tp->tv_sec = 0;
223         tp->tv_nsec = 0;
224         return;
225     }
226
227     // Convert everything to nanoseconds with a long long. For 64-bits,
228     // this is safe for 544 years. We'll think about it more closer to
229     // the time...
230
231     unsigned long long nsecs = Cyg_Clock::convert( ticks, &ns_inverter );
232
233     tp->tv_sec = (long)(nsecs / 1000000000ll);
234     tp->tv_nsec = (long)(nsecs % 1000000000ll);
235
236     CYG_POSTCONDITION(valid_timespec(tp), "Failed to make valid timespec!");
237 }
238
239 //==========================================================================
240 // Startup routine.
241
242 externC void cyg_posix_clock_start()
243 {
244     init_converters();
245 }
246
247 #ifdef CYGPKG_POSIX_TIMERS
248 //==========================================================================
249 // Alarm action routine
250 // This is called each time an alarm set up by a timer expires.
251
252 static void alarm_action( Cyg_Alarm *alarm, CYG_ADDRWORD data )
253 {
254     posix_timer *timer = (posix_timer *)data;
255
256     if( timer->pending )
257     {
258         // If the pending flag is already set, count an overrun and
259         // do not bother to try and deliver the expiry.
260         
261         timer->overrun++;
262     }
263     else
264     {
265         if( timer->sigev.sigev_notify == SIGEV_SIGNAL )
266         {
267             // Set the expiry pending and wake a thread to
268             // deliver the signal.
269         
270             timer->pending = true;
271     
272             sigset_t mask;
273             sigemptyset( &mask );
274             sigaddset( &mask, timer->sigev.sigev_signo );
275             cyg_posix_signal_sigwait();
276             cyg_posix_pthread_release_thread( &mask );
277         }
278         else if( timer->sigev.sigev_notify == SIGEV_THREAD )
279         {
280             // Thread style notification
281             // FIXME: implement SIGEV_THREAD
282         }
283         // else do nothing
284     }
285 }
286
287 //==========================================================================
288 // Timer ASR routine
289
290 externC void cyg_posix_timer_asr( pthread_info *self )
291 {
292
293     // Loop over the timers looking for any that have an
294     // expiry pending and call cyg_sigqueue() for each.
295     
296     for( int i = 0; i < _POSIX_TIMER_MAX; i++ )
297     {
298         posix_timer *timer = &timer_table[i];
299
300         if( timer->id != 0 && timer->pending )
301         {
302             timer->pending = false;
303                 
304             // Call into signal subsystem...
305             cyg_sigqueue( &timer->sigev, SI_TIMER );
306
307             timer->overrun = 0;
308         }
309     }
310 }
311
312 #endif // ifdef CYGPKG_POSIX_TIMERS
313
314 //==========================================================================
315 // Clock functions
316
317 //-----------------------------------------------------------------------------
318 // Set the clocks current time
319
320 externC int clock_settime( clockid_t clock_id, const struct timespec *tp)
321 {
322     TIME_ENTRY();
323
324     if( clock_id != CLOCK_REALTIME )
325         TIME_RETURN(EINVAL);
326
327     if( !valid_timespec( tp ) )
328         TIME_RETURN(EINVAL);
329         
330     cyg_tick_count ticks = cyg_timespec_to_ticks( tp );
331
332     Cyg_Clock::real_time_clock->set_value( ticks );
333     
334     TIME_RETURN(0);
335 }
336
337 //-----------------------------------------------------------------------------
338 // Get the clocks current time
339
340 externC int clock_gettime( clockid_t clock_id, struct timespec *tp)
341 {
342     TIME_ENTRY();
343
344     if( clock_id != CLOCK_REALTIME )
345         TIME_RETURN(EINVAL);
346
347     if( tp == NULL )
348         TIME_RETURN(EINVAL);
349     
350     cyg_tick_count ticks = Cyg_Clock::real_time_clock->current_value();
351
352     cyg_ticks_to_timespec( ticks, tp );
353
354     TIME_RETURN(0);
355 }
356    
357
358 //-----------------------------------------------------------------------------
359 // Get the clocks resolution
360
361 externC int clock_getres( clockid_t clock_id, struct timespec *tp)
362 {
363     TIME_ENTRY();
364
365     if( clock_id != CLOCK_REALTIME )
366         TIME_RETURN(EINVAL);
367
368     if( tp == NULL )
369         TIME_RETURN(EINVAL);
370
371     // Get the resolution of 1 tick
372     cyg_ticks_to_timespec( 1, tp );
373     
374     TIME_RETURN(0);
375 }
376     
377
378 //==========================================================================
379 // Timer functions
380
381 #ifdef CYGPKG_POSIX_TIMERS
382
383 //-----------------------------------------------------------------------------
384 // Create a timer based on the given clock.
385
386 externC int timer_create( clockid_t clock_id,
387                           struct sigevent *evp,
388                           timer_t *timer_id)
389 {
390     TIME_ENTRY();
391
392     if( clock_id != CLOCK_REALTIME )
393         TIME_RETURN(EINVAL);
394
395     timer_mutex.lock();
396
397     posix_timer *timer;
398     int next = timer_next;
399
400     // Look for an unused slot in the table
401     while( timer_table[next].id != 0 )
402     {
403         next++;
404         if( next >= _POSIX_TIMER_MAX )
405             next = 0;
406
407         if( next == timer_next )
408         {
409             timer_mutex.unlock();
410             TIME_RETURN(EAGAIN);
411         }
412     }
413
414     timer = &timer_table[next];
415
416     timer_next = next;
417
418     // Make sure we never allocate a zero timer id.
419     while( timer->id == 0 )
420     {
421         timer_id_cookie += TIMER_ID_COOKIE_INC;
422         timer->id        = next+timer_id_cookie;
423     }
424
425     if( evp == NULL )
426     {
427         // If no evp is supplied, set up the timer
428         // to use a default set.
429         timer->sigev.sigev_notify               = SIGEV_SIGNAL;
430         timer->sigev.sigev_signo                = SIGALRM;
431         timer->sigev.sigev_value.sival_int      = timer->id;
432     }
433     else timer->sigev = *evp;
434         
435     timer->alarm        = new( timer->alarm_obj )
436                                Cyg_Alarm( Cyg_Clock::real_time_clock,
437                                           alarm_action,
438                                           (CYG_ADDRWORD)timer );
439
440     timer->armed        = false;
441     timer->overrun      = 0;
442
443     *timer_id = timer->id;
444     
445     timer_mutex.unlock();
446     
447     TIME_RETURN(0);
448 }
449
450 //-----------------------------------------------------------------------------
451 // Delete the timer
452
453 externC int timer_delete( timer_t timerid )
454 {
455     int err = EINVAL;
456     TIME_ENTRY();
457     
458     posix_timer *timer = &timer_table[timerid & TIMER_ID_COOKIE_MASK];
459
460     timer_mutex.lock();
461
462     if( timer->id == timerid )
463     {
464         // This is a valid timer, disable the kernel
465         // alarm and delete it.
466
467         // disable alarm
468         timer->alarm->disable();
469
470         // destroy it
471         timer->alarm->~Cyg_Alarm();
472
473         // Mark POSIX timer free
474         timer->id = 0;
475
476         err = 0;
477     }
478
479     timer_mutex.unlock();
480     
481     TIME_RETURN( err );
482 }
483
484 //-----------------------------------------------------------------------------
485 // Set the expiration time of the timer.
486
487 externC int timer_settime( timer_t timerid, int flags,
488                            const struct itimerspec *value,
489                            struct itimerspec *ovalue )
490 {
491     int err = EINVAL;
492     TIME_ENTRY();
493     
494     if( value == NULL )
495         TIME_RETURN(EINVAL);
496
497     // convert trigger and interval values to ticks.
498     cyg_tick_count trigger = cyg_timespec_to_ticks( &value->it_value, true );
499     cyg_tick_count interval = cyg_timespec_to_ticks( &value->it_interval, true );
500     
501     posix_timer *timer = &timer_table[timerid & TIMER_ID_COOKIE_MASK];
502
503     timer_mutex.lock();
504
505     if( timer->id == timerid )
506     {
507         // disable the timer
508         timer->alarm->disable();
509         
510         if( ovalue != NULL )
511         {
512             cyg_tick_count otrigger, ointerval;
513
514             timer->alarm->get_times( &otrigger, &ointerval );
515
516             if( timer->armed )
517             {
518                 // convert absolute trigger time to interval until next trigger
519                 otrigger -= Cyg_Clock::real_time_clock->current_value();
520             }
521             else otrigger = 0;
522
523             // convert ticks to timespecs
524             cyg_ticks_to_timespec( otrigger, &ovalue->it_value );
525             cyg_ticks_to_timespec( ointerval, &ovalue->it_interval );
526         }
527         
528         if( trigger == 0 )
529         {
530             // Mark timer disarmed
531             timer->armed = false;            
532         }
533         else
534         {
535             // If the ABSTIME flag is not set, add the current time
536             if( (flags & TIMER_ABSTIME) == 0 )
537                 trigger += Cyg_Clock::real_time_clock->current_value();
538
539             // Set the alarm running.
540             timer->alarm->initialize( trigger, interval );
541
542             // Mark timer armed
543             timer->armed = true;
544
545         }
546         
547         err = 0;
548     }
549     
550     timer_mutex.unlock();
551     
552     TIME_RETURN(err);
553 }
554
555 //-----------------------------------------------------------------------------
556 // Get current timer values
557
558 externC int timer_gettime( timer_t timerid, struct itimerspec *value )
559 {
560     int err = EINVAL;
561
562     TIME_ENTRY();
563     
564     if( value == NULL )
565         TIME_RETURN(EINVAL);
566     
567     posix_timer *timer = &timer_table[timerid & TIMER_ID_COOKIE_MASK];
568
569     timer_mutex.lock();
570
571     if( timer->id == timerid )
572     {
573         cyg_tick_count trigger, interval;
574
575         timer->alarm->get_times( &trigger, &interval );
576
577         if( timer->armed )
578         {
579             // convert absolute trigger time to interval until next trigger
580             trigger -= Cyg_Clock::real_time_clock->current_value();
581         }
582         else trigger = 0;
583
584         // convert ticks to timespecs
585         cyg_ticks_to_timespec( trigger, &value->it_value );
586         cyg_ticks_to_timespec( interval, &value->it_interval );
587         err = 0;
588     }
589     
590     timer_mutex.unlock();
591     
592     TIME_RETURN(err);
593 }
594
595 //-----------------------------------------------------------------------------
596 // Get number of missed triggers
597
598 externC int timer_getoverrun( timer_t timerid )
599 {
600     int overrun = 0;
601     
602     TIME_ENTRY();
603
604     posix_timer *timer = &timer_table[timerid & TIMER_ID_COOKIE_MASK];
605
606     timer_mutex.lock();
607
608     if( timer->id == timerid )
609     {
610         overrun = timer->overrun;
611     }
612     
613     timer_mutex.unlock();
614     
615     CYG_REPORT_RETVAL(overrun);
616     return overrun;
617 }
618
619 #endif // ifdef CYGPKG_POSIX_TIMERS
620
621 //==========================================================================
622 // Nanosleep
623 // Sleep for the given time.
624
625 externC int nanosleep( const struct timespec *rqtp,
626                        struct timespec *rmtp)
627 {
628     cyg_tick_count ticks, now, then;
629
630     TIME_ENTRY();
631
632     // check for cancellation first.
633     PTHREAD_TESTCANCEL();
634
635     // Fail an invalid timespec
636     if( !valid_timespec( rqtp ) )
637         TIME_RETURN(EINVAL);
638
639     // Return immediately for a zero delay.
640     if( rqtp->tv_sec == 0 && rqtp->tv_nsec == 0 )
641         TIME_RETURN(0);
642
643     // Convert timespec to ticks
644     ticks = cyg_timespec_to_ticks( rqtp, true );
645
646     CYG_ASSERT( ticks != 0, "Zero tick count");
647     
648     Cyg_Thread *self = Cyg_Thread::self();
649     
650     // Do the delay, keeping track of how long we actually slept for.
651     then = Cyg_Clock::real_time_clock->current_value();
652
653     self->delay( ticks );
654
655     now = Cyg_Clock::real_time_clock->current_value();
656
657     
658     if( rmtp != NULL && (then+ticks) > now )
659     {
660         // We woke up early, return the time left.
661         // FIXME: strictly we only need to do this if we were woken
662         //        by a signal.
663
664         // Calculate remaining number of ticks.
665         ticks -= (now-then);
666
667         cyg_ticks_to_timespec( ticks, rmtp );
668     }
669     
670     // check if we were woken up because we were cancelled.
671     PTHREAD_TESTCANCEL();
672
673     TIME_RETURN(0);
674 }    
675
676 // -------------------------------------------------------------------------
677 // Wait for a signal, or the given number of seconds
678
679 externC unsigned int sleep( unsigned int seconds )
680 {
681     TIME_ENTRY();
682
683     struct timespec timeout;
684
685     timeout.tv_sec = seconds;
686     timeout.tv_nsec = 0;
687
688     if( nanosleep( &timeout, &timeout ) != 0 )
689     {
690         CYG_REPORT_RETVAL(timeout.tv_sec);
691         return timeout.tv_sec;
692     }
693
694     TIME_RETURN(0);
695
696
697 #endif // ifdef CYGPKG_POSIX_CLOCKS
698
699 // -------------------------------------------------------------------------
700 // EOF time.cxx