/* * Copyright (c) 1997-2001 University of Utah and the Flux Group. * All rights reserved. * * This file is part of the Flux OSKit. The OSKit is free software, also known * as "open source;" you can redistribute it and/or modify it under the terms * of the GNU General Public License (GPL), version 2, as published by the Free * Software Foundation (FSF). To explore alternate licensing terms, contact * the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271. * * The OSKit 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 GPL for more details. You should have * received a copy of the GPL along with the OSKit; see the file COPYING. If * not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA. */ /* * a driver for the system clock. */ #include #include #include #include #include #include #include #include "pit_param.h" #define VERBOSITY 0 /* * This file needs these osenv interfaces. The clock code is written on * top of the osenv interfaces, and is intended to be standalone from the * device library. It should be moved elsewhere, since it needs to use * the external interfaces, not the internal ones. */ #include #include #include #include #include #ifdef KNIT #define BEGIN_CRITICAL_SECTION(intr,flags) \ flags = osenv_intr_enabled(); \ osenv_intr_disable(); #define END_CRITICAL_SECTION(intr,flags) \ if (flags) osenv_intr_enable(); #undef oskit_osenv_mem_alloc #define oskit_osenv_mem_alloc(a,b,c,d) osenv_mem_alloc((b),(c),(d)) #undef oskit_osenv_mem_free #define oskit_osenv_mem_free(a,b,c,d) osenv_mem_free((b),(c),(d)) #undef oskit_osenv_timer_spin #define oskit_osenv_timer_spin(a,b) osenv_timer_spin((b)) #undef oskit_osenv_timer_register #define oskit_osenv_timer_register(a,b,c) osenv_timer_register((b),(c)) #else /* !KNIT */ /* * Locally cached pointers to the COM objects needed. */ static oskit_osenv_log_t *clock_osenv_log; /* * For simplicity, lets map these older osenv_log calls to the new object * invocations to avoid a ton of textual changes. */ #undef osenv_assert #define osenv_assert(x) \ oskit_osenv_assert(clock_osenv_log, x) #define osenv_log(pri, fmt...) \ oskit_osenv_log_log(clock_osenv_log, (pri), ##fmt) #define BEGIN_CRITICAL_SECTION(intr,flags) \ flags = oskit_osenv_intr_save_disable(intr); #define END_CRITICAL_SECTION(intr,flags) \ if (flags) oskit_osenv_intr_enable(intr); #endif /* !KNIT */ /* * What does a clock do, really? * * Two things: * a) it entertains a counter which can be queried for the * current time. * * b) it entertains a bunch of oskit_timers which are to triggered * at specified intervals. Here we have basically two kinds: * * i) periodic timers running at every clock tick * ii) other timers. There are two subtypes: * 1. oneshot timers that are not being reloaded after they * expire * 2. periodic timers that are being reloaded. */ /* * this describes the data structure used to represent a timer */ typedef struct timer_impl { oskit_timer_t iot; /* COM timer interface */ unsigned count; /* reference count */ oskit_s32_t left; /* ticks to next alarm */ oskit_s32_t reload; /* the reload value in ticks */ struct oskit_listener *listener; /* my listener */ oskit_itimerspec_t itimer; /* our itimerspec */ struct clock_impl *clock; /* our clock */ struct timer_impl **clocklist; /* head of list we're on */ struct timer_impl *next; } timer_impl_t; /* * this describes the data structure used to represent our clock */ typedef struct clock_impl { oskit_clock_t ioc; /* COM clock interface */ unsigned count; /* reference count */ oskit_timespec_t time; /* "current" time */ #ifndef KNIT /* * Locally cached pointers to the COM objects needed. */ oskit_osenv_mem_t *mem; oskit_osenv_timer_t *timer; oskit_osenv_intr_t *intr; #endif /* * Please see the distinction I made above. * Godmar feels it is important to optimize the case where a timer * expires at every clock tick * Thus, we keep two queues. Both of them are used by * the interrupt code, so changes to them must be protected. */ /* this is our "callout" queue for one-shot timers - a differential * list of events essentially. This list is ordered by * timer_impl::left. Negative values indicate overdue events. */ struct timer_impl *callout; /* * these timers are triggered at every clock interrupt */ struct timer_impl *everytime; } clock_impl_t; /* * Currently, we only support one instance * Note the COM code is written for dynamic allocation/deallocation, * which is why we keep a pointer here */ clock_impl_t *oskit_system_clock; /* forward declarations */ static timer_impl_t * create_timer(clock_impl_t *clock); static void add_synchronous_timer(oskit_osenv_intr_t *intr, timer_impl_t **head, timer_impl_t *t); static void add_oneshot_timer(oskit_osenv_intr_t *intr, timer_impl_t **head, timer_impl_t *n, oskit_s32_t ticks); /* * these macros should probably go elsewhere, maybe in oskit/time.h */ #define TIMESPEC2NANOSEC(t) ((t)->tv_sec * 1000000000 + (t)->tv_nsec) #define NULL_TIMESPEC(t) ((t)->tv_sec = (t)->tv_nsec = 0) #define ADDNANO2TIMESPEC(n, t) { \ assert(n <= 1000000000); \ (t)->tv_nsec += (n); \ if ((t)->tv_nsec >= 1000000000) { \ (t)->tv_sec++; \ (t)->tv_nsec -= 1000000000; \ } \ } #define SUBTIMESPEC(s1, s2, d) \ ((d)->tv_nsec = ((s1)->tv_nsec >= (s2)->tv_nsec) ? \ (((d)->tv_sec = (s1)->tv_sec - (s2)->tv_sec), \ (s1)->tv_nsec - (s2)->tv_nsec) \ : \ (((d)->tv_sec = (s1)->tv_sec - (s2)->tv_sec - 1), \ (1000000000 + (s1)->tv_nsec - (s2)->tv_nsec))) /* * timer interrupt handler. */ static void fdev_handle_clock(clock_impl_t *clock); static void timer_intr() { if (oskit_system_clock) fdev_handle_clock(oskit_system_clock); } /* * take any action that needs to be taken at a (hardware) clock tick */ static void fdev_handle_clock(clock_impl_t *clock) { timer_impl_t *th; int mustcallout = 0; /* update our current time */ ADDNANO2TIMESPEC(NANOPERTICK, &clock->time); /* handle simple, synchronously running timers */ for (th = clock->everytime; th; th = th->next) oskit_listener_notify(th->listener, (oskit_iunknown_t *)th); /* handle callout queue */ for (th = clock->callout; th; th = th->next) { if (--th->left > 0) break; mustcallout = 1; if (th->left == 0) break; } if (!mustcallout) return; /* Call and remove all handlers that are due */ while (((th = clock->callout) != NULL) && th->left <= 0) { /* remove it from list */ clock->callout = th->next; th->next = 0; th->clocklist = 0; /* notify the object */ oskit_listener_notify(th->listener, (oskit_iunknown_t *)th); /* * reload timer if necessary */ if (th->reload) { #ifdef KNIT if (th->reload == 1) add_synchronous_timer(0, &clock->everytime, th); else add_oneshot_timer(0, &clock->callout, th, th->reload); #else /* !KNIT */ if (th->reload == 1) add_synchronous_timer(clock->intr, &clock->everytime, th); else add_oneshot_timer(clock->intr, &clock->callout, th, th->reload); #endif /* !KNIT */ } } } #if VERBOSITY > 0 /* * for debugging - print out the contents of a queue */ static void print_queue(timer_impl_t **head) { timer_impl_t *t = *head; while (t) { osenv_log(OSENV_LOG_DEBUG, "(%d, %d, %p) ", t->left, t->reload, t->clocklist); t = t->next; } osenv_log(OSENV_LOG_DEBUG, "\n"); } #endif /* * add a timer to an unordered list of timers */ static void add_synchronous_timer(oskit_osenv_intr_t *intr, timer_impl_t **head, timer_impl_t *t) { unsigned int flags; BEGIN_CRITICAL_SECTION(intr, flags); t->clocklist = head; t->next = *head; *head = t; t->left = t->reload; END_CRITICAL_SECTION(intr, flags); } /* * remove a timer from an unordered list of timers */ static void remove_synchronous_timer(oskit_osenv_intr_t *intr, timer_impl_t **head, timer_impl_t *t) { unsigned int flags; BEGIN_CRITICAL_SECTION(intr, flags); while (*head) { if (*head == t) { *head = t->next; t->clocklist = 0; break; } head = &(*head)->next; } END_CRITICAL_SECTION(intr, flags); } /* * add a timer to be fired in "ticks" * this is programmed analogous to BSD's timeout function */ static void add_oneshot_timer(oskit_osenv_intr_t *intr, timer_impl_t **head, timer_impl_t *n, oskit_s32_t ticks) { timer_impl_t *t, **p; unsigned int flags; BEGIN_CRITICAL_SECTION(intr, flags); n->clocklist = head; /* walk through list, finding spot, adjusting ticks parameter */ for (p = head; (t = *p) && ticks > t->left; p = &t->next) if (t->left > 0) ticks -= t->left; n->left = ticks; /* adjust next entry */ if (t) t->left -= ticks; n->next = t; *p = n; END_CRITICAL_SECTION(intr, flags); } /* * remove a one-shot timer, correcting the next queue entry */ static void remove_oneshot_timer(oskit_osenv_intr_t *intr, timer_impl_t **head, timer_impl_t *f) { timer_impl_t *t, **p; unsigned int flags; BEGIN_CRITICAL_SECTION(intr, flags); for (p = head; (t = *p) != NULL; p = &t->next) { if (t == f) { if (t->next && t->left > 0) t->next->left += t->left; *p = t->next; f->clocklist = 0; f->next = 0; break; } } END_CRITICAL_SECTION(intr, flags); } /* * figure out how much many ticks are left on a particular timer */ static oskit_s32_t ticks_left_on_oneshot_timer(timer_impl_t *t, timer_impl_t *f) { unsigned int flags; oskit_s32_t left = 0; BEGIN_CRITICAL_SECTION(f->clock->intr, flags); while (t) { if (t->left > 0) left += t->left; if (t == f) break; t = t->next; } END_CRITICAL_SECTION(f->clock->intr,flags); /* * if we didn't find t, lets return zero instead of returning * the accumulated sum of all time diffs */ return t ? left : 0; } /* * sometimes we have the need to remove a timer from its clock */ static void remove_from_clocklist(timer_impl_t *ti) { clock_impl_t *clock = ti->clock; #ifdef KNIT if (ti->clocklist == &ti->clock->everytime) remove_synchronous_timer(0, &clock->everytime, ti); else if (ti->clocklist == &ti->clock->callout) remove_oneshot_timer(0, &clock->callout, ti); #else if (ti->clocklist == &ti->clock->everytime) remove_synchronous_timer(clock->intr, &clock->everytime, ti); else if (ti->clocklist == &ti->clock->callout) remove_oneshot_timer(clock->intr, &clock->callout, ti); #endif } /************************************************************************* * * oskit_timer COM interface implementation * ************************************************************************/ /* Basic COM functions */ /* * Query a timer object for its interfaces. */ static OSKIT_COMDECL timer_query(oskit_timer_t *io, const oskit_iid_t *iid, void **out_ihandle) { timer_impl_t *po = (timer_impl_t *)io; osenv_assert(po != NULL); osenv_assert(po->count != 0); if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_timer_iid, sizeof(*iid)) == 0) { *out_ihandle = &po->iot; ++po->count; return 0; } *out_ihandle = NULL; return OSKIT_E_NOINTERFACE; } /* * Clone a reference to a timer */ static OSKIT_COMDECL_U timer_addref(oskit_timer_t *io) { timer_impl_t *po = (timer_impl_t *)io; osenv_assert(po != NULL); osenv_assert(po->count != 0); return ++po->count; } /* * release a timer */ static OSKIT_COMDECL_U timer_release(oskit_timer_t *io) { timer_impl_t *ti = (timer_impl_t *)io; unsigned newcount; osenv_assert(ti != NULL); osenv_assert(ti->count != 0); newcount = --ti->count; if (newcount == 0) { /* if we're still on a list, remove ourselves! */ if (ti->clocklist) remove_from_clocklist(ti); if (ti->listener) oskit_listener_release(ti->listener); oskit_osenv_mem_free(ti->clock->mem, ti, 0, sizeof(*ti)); } return newcount; } /* Set a listener to be notifed when timer expires */ static OSKIT_COMDECL_V timer_setlistener(oskit_timer_t *timer, struct oskit_listener *listener) { timer_impl_t *ti = (timer_impl_t *)timer; osenv_assert(ti != NULL); osenv_assert(ti->count != 0); oskit_listener_addref(listener); if (ti->listener) oskit_listener_release(ti->listener); ti->listener = listener; } /* Set the timer's expiration and period. */ static OSKIT_COMDECL timer_settime(oskit_timer_t *timer, int flags, const struct oskit_itimerspec *value) { timer_impl_t *ti = (timer_impl_t *)timer; clock_impl_t *clock; oskit_s32_t left, reload; osenv_assert(ti != NULL); osenv_assert(ti->count != 0); clock = ti->clock; /* * Step 1: compute the new parameters */ ti->itimer = *value; if (flags & OSKIT_TIMER_ABSTIME) { oskit_timespec_t exp; /* get time from clock and subtract from value->it_value */ SUBTIMESPEC(&value->it_value, &clock->time, &exp); ti->itimer.it_value = exp; } left = TIMESPEC2TICKS(&ti->itimer.it_value); reload = TIMESPEC2TICKS(&ti->itimer.it_interval); #if VERBOSITY > 0 osenv_log(OSENV_LOG_DEBUG, __FUNCTION__" it_value = { %d, %d }, left = %d\n", ti->itimer.it_value.tv_sec, ti->itimer.it_value.tv_nsec, left); osenv_log(OSENV_LOG_DEBUG, __FUNCTION__" it_interval = { %d, %d }, reload = %d\n", ti->itimer.it_interval.tv_sec, ti->itimer.it_interval.tv_nsec, reload); #endif /* * Step 2: remove ourselves from any lists - should we be on one */ if (ti->clocklist) remove_from_clocklist(ti); /* * If left and reload are zero, do nothing. Might have been a * timer removal. */ if (left == 0 && reload == 0) return 0; /* * set reload in any event so that one-shot timers with a period * can be inserted in the periodic queue after they first expire */ ti->reload = reload; /* * Step 3: Figure out just what kind of a timer we are */ #ifdef KNIT if (left == 1 && left == reload) { /* this timer expires at every clock tick */ add_synchronous_timer(0, &clock->everytime, ti); } else { /* this timer starts out as a one-shot timer */ add_oneshot_timer(0, &clock->callout, ti, left); } #else if (left == 1 && left == reload) { /* this timer expires at every clock tick */ add_synchronous_timer(clock->intr, &clock->everytime, ti); } else { /* this timer starts out as a one-shot timer */ add_oneshot_timer(clock->intr, &clock->callout, ti, left); } #endif return 0; } /* Query the timer's current expiration and period. */ static OSKIT_COMDECL_V timer_gettime(oskit_timer_t *timer, struct oskit_itimerspec *out_value) { timer_impl_t *ti = (timer_impl_t *)timer; oskit_s32_t left = 0; osenv_assert(ti != NULL); osenv_assert(ti->count != 0); /* set current period */ out_value->it_interval = ti->itimer.it_interval; /* * try to figure out how much time is left to next expiration */ if (ti->clocklist == &ti->clock->everytime) left = 1; /* one tick is left */ else if (ti->clocklist == &ti->clock->callout) left = ticks_left_on_oneshot_timer(ti->clock->callout, ti); /* set expiration time left */ TICKS2TIMESPEC(left, &out_value->it_value); } /* Return number of overruns */ static OSKIT_COMDECL timer_getoverrun(oskit_timer_t *timer) { /* we don't run over, do we? */ return 0; } /************************************************************************* * * oskit_clock COM interface implementation * ************************************************************************/ /* Basic COM functions */ /* * Query a clock object for its interfaces. */ static OSKIT_COMDECL clock_query(oskit_clock_t *io, const oskit_iid_t *iid, void **out_ihandle) { clock_impl_t *po = (clock_impl_t *)io; osenv_assert(po != NULL); osenv_assert(po->count != 0); if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_clock_iid, sizeof(*iid)) == 0) { *out_ihandle = &po->ioc; ++po->count; return 0; } *out_ihandle = NULL; return OSKIT_E_NOINTERFACE; } /* * Clone a reference to a clock device */ static OSKIT_COMDECL_U clock_addref(oskit_clock_t *io) { clock_impl_t *po = (clock_impl_t *)io; osenv_assert(po != NULL); osenv_assert(po->count != 0); return ++po->count; } /* * Close ("release") a clock device. */ static OSKIT_COMDECL_U clock_release(oskit_clock_t *io) { clock_impl_t *po = (clock_impl_t *)io; unsigned newcount; osenv_assert(po != NULL); osenv_assert(po->count != 0); newcount = --po->count; if (newcount == 0) { #ifdef KNIT osenv_timer_unregister(timer_intr, TIMER_FREQ); osenv_mem_free(po, 0, sizeof(*po)); #else oskit_osenv_mem_t *mem = po->mem; oskit_osenv_timer_unregister(po->timer, timer_intr, TIMER_FREQ); oskit_osenv_timer_release(po->timer); oskit_osenv_intr_release(po->intr); oskit_osenv_mem_free(mem, po, 0, sizeof(*po)); oskit_osenv_mem_release(mem); #endif } return newcount; } /* Base fdev device interface operations */ static OSKIT_COMDECL clock_getinfo(oskit_clock_t *fdev, oskit_devinfo_t *out_info) { /* * this gives out info about the actual hardware device in use */ out_info->name = "clock"; out_info->description = "Simple pit-based clock device"; out_info->vendor = "UofU/CSL"; out_info->author = "Godmar Back"; out_info->version = "1.0"; return 0; } static OSKIT_COMDECL clock_getdriver(oskit_clock_t *fdev, oskit_driver_t **out_driver) { /* * this a reference about the "more general" driver of * which we are an instance * * for now, just return an additional reference to us */ return clock_query(fdev, &oskit_clock_iid,(void **)out_driver); } /* Clock device interface operations */ /* Return basic information about this clock, defined above */ static OSKIT_COMDECL_V clock_getclockinfo(oskit_clock_t *dev, struct oskit_clock_info *out_info) { oskit_timespec_t res = { 0, NANOPERTICK }; oskit_timespec_t pitns = { 0, PIT_NS }; out_info->flags = OSKIT_CLOCK_SETTABLE; out_info->timer_precision = res; out_info->query_precision = res; out_info->sleep_precision = res; out_info->spin_precision = pitns; } /* Query the clock's current time. */ static OSKIT_COMDECL_V clock_gettime(oskit_clock_t *dev, struct oskit_timespec *out_time) { clock_impl_t *po = (clock_impl_t *)dev; osenv_assert(po != NULL); osenv_assert(po->count != 0); *out_time = po->time; } /* * Wait for a period of time, possibly blocking during the wait. */ static OSKIT_COMDECL_V clock_sleep(oskit_clock_t *dev, const struct oskit_timespec *time) { /* * this would be implemented by posting an "internal" * timeout and then sleeping */ } /* * Wait for a period of time without blocking * or enabling interrupts if interrupts are disabled on call. * This should only be used for very short waits. */ static OSKIT_COMDECL_V clock_spin(oskit_clock_t *dev, const struct oskit_timespec *time) { clock_impl_t *po = (clock_impl_t *)dev; oskit_osenv_timer_spin(po->timer, TIMESPEC2NANOSEC(time)); } /* * Create a new timer based on this clock. */ static OSKIT_COMDECL clock_createtimer(oskit_clock_t *dev, struct oskit_timer **out_timer) { clock_impl_t *po = (clock_impl_t *)dev; timer_impl_t *t; osenv_assert(po != NULL); osenv_assert(po->count != 0); /* that's all we can do here since we don't know nothing about it yet */ t = create_timer(po); *out_timer = &t->iot; return 0; } /* * Set the clock's current time immediately. * Clocks that aren't settable will return OSKIT_E_NOTIMPL. */ static OSKIT_COMDECL clock_settime(oskit_clock_t *dev, const struct oskit_timespec *new_time) { /* yeah, set me - like you know what you're doing... */ clock_impl_t *po = (clock_impl_t *)dev; osenv_assert(po != NULL); osenv_assert(po->count != 0); po->time = *new_time; return 0; } /* * Set the clock's current time gradually, * so that the clock's value does not jump or go backward. * Clocks that aren't adjustable will return OSKIT_E_NOTIMPL. */ static OSKIT_COMDECL clock_adjtime(oskit_clock_t *dev, const struct oskit_timespec *new_time) { /* there's no way I'm going to implement that... */ return OSKIT_E_NOTIMPL; } /* * Set the clock's period, or timer_precision. * Clocks that aren't programmable will return OSKIT_E_NOTIMPL. * Even if the clock is programmable, * it may not support the exact period requested; * the caller can get_clock_info() and check timer_precision * to determine the actual period for which the clock was set. * If the requested period is completely outside of the range * supported by the clock, this method returns OSKIT_E_DEV_BAD_PERIOD. */ static OSKIT_COMDECL clock_setperiod(oskit_clock_t *dev, const struct oskit_timespec *period) { return OSKIT_E_NOTIMPL; } /* * Open this clock device for exclusive access. * The returned object exports the same clock interface, * but changes made to its period using set_period() only apply * until the open clock object reference is released; * then the clock reverts to its original period. * Callbacks registered through either interface will still be called, * though their exactness may change when the period changes. * This method may return OSKIT_E_NOTIMPL * if exclusive access to the clock is not supported. */ static OSKIT_COMDECL clock_open(oskit_clock_t *dev, oskit_clock_t **out_opendev) { return OSKIT_E_NOTIMPL; } /***********************************************************************/ /* * vtables */ static struct oskit_clock_ops oskit_clock_ops = { clock_query, clock_addref, clock_release, clock_getinfo, clock_getdriver, clock_getclockinfo, clock_gettime, clock_sleep, clock_spin, clock_createtimer, clock_settime, clock_adjtime, clock_setperiod, clock_open }; static struct oskit_timer_ops oskit_timer_ops = { timer_query, timer_addref, timer_release, timer_setlistener, timer_settime, timer_gettime, timer_getoverrun }; static timer_impl_t * create_timer(clock_impl_t *clock) { timer_impl_t *t; t = (timer_impl_t *)oskit_osenv_mem_alloc(clock->mem, sizeof *t, 0, 0); memset(t, 0, sizeof *t); t->iot.ops = &oskit_timer_ops; t->count = 1; t->clock = clock; return t; } /* * return a reference to the system clock; creating the object if necessary * * Requires the osenv interface. */ oskit_clock_t * oskit_clock_init() { clock_impl_t *c; #ifndef KNIT oskit_osenv_t *osenv; oskit_osenv_mem_t *mem; oskit_osenv_timer_t *timer; oskit_osenv_intr_t *intr; #endif /* there can be only one */ if (oskit_system_clock) { ++oskit_system_clock->count; return &oskit_system_clock->ioc; } #ifndef KNIT /* * Find the osenv, and then the various osenv interfaces this * component needs. */ oskit_lookup_first(&oskit_osenv_iid, (void *) &osenv); if (!osenv) panic("oskit_clock_init(): osenv not yet registered."); oskit_osenv_lookup(osenv, &oskit_osenv_mem_iid, (void *) &mem); oskit_osenv_lookup(osenv, &oskit_osenv_intr_iid, (void *) &intr); oskit_osenv_lookup(osenv, &oskit_osenv_timer_iid, (void *)&timer); oskit_osenv_lookup(osenv, &oskit_osenv_log_iid, (void *) &clock_osenv_log); oskit_osenv_release(osenv); #endif /* KNIT */ /* * initialize the COM object */ c = (clock_impl_t *)oskit_osenv_mem_alloc(mem, sizeof(*c), 0, 0); if (c == NULL) return NULL; memset(c, 0, sizeof *c); c->ioc.ops = &oskit_clock_ops; c->count = 1; #ifndef KNIT c->mem = mem; c->timer = timer; c->intr = intr; /* safe to call multiple times */ oskit_osenv_timer_init(timer); #endif oskit_system_clock = c; oskit_osenv_timer_register(timer, timer_intr, TIMER_FREQ); return &c->ioc; } #ifdef KNIT oskit_error_t init(void) { if (oskit_clock_init()) { return 0; } else { return OSKIT_ENXIO; } } #endif