source: rtems/cpukit/libi2c/libi2c.c @ 2015ca6

4.104.114.95
Last change on this file since 2015ca6 was d2ff24c2, checked in by Till Straumann <strauman@…>, on 11/21/07 at 06:20:49

2007-11-17 Till Straumann <strauman@…>

  • libi2c/libi2c.c, libi2c/libi2c.h, libi2c/README_libi2c: Added checks so that use of 'stdio' is avoided (falling back to 'printk') before the system is up. Publish driver entry points so that the libi2c driver could be added to the applications 'device driver table'. This is not fully implemented yet, though, since in addition to initializing libi2c the low-level i2c bus drivers as well as high-level i2c device drivers need to be registered with the library. Updated README_libi2c accordingly.
  • Property mode set to 100644
File size: 18.2 KB
Line 
1/* $Id$ */
2
3/* libi2c Implementation */
4
5/*
6 * Authorship
7 * ----------
8 * This software was created by
9 *     Till Straumann <strauman@slac.stanford.edu>, 2005,
10 *         Stanford Linear Accelerator Center, Stanford University.
11 *
12 * Acknowledgement of sponsorship
13 * ------------------------------
14 * This software was produced by
15 *     the Stanford Linear Accelerator Center, Stanford University,
16 *         under Contract DE-AC03-76SFO0515 with the Department of Energy.
17 *
18 * Government disclaimer of liability
19 * ----------------------------------
20 * Neither the United States nor the United States Department of Energy,
21 * nor any of their employees, makes any warranty, express or implied, or
22 * assumes any legal liability or responsibility for the accuracy,
23 * completeness, or usefulness of any data, apparatus, product, or process
24 * disclosed, or represents that its use would not infringe privately owned
25 * rights.
26 *
27 * Stanford disclaimer of liability
28 * --------------------------------
29 * Stanford University makes no representations or warranties, express or
30 * implied, nor assumes any liability for the use of this software.
31 *
32 * Stanford disclaimer of copyright
33 * --------------------------------
34 * Stanford University, owner of the copyright, hereby disclaims its
35 * copyright and all other rights in this software.  Hence, anyone may
36 * freely use it for any purpose without restriction. 
37 *
38 * Maintenance of notices
39 * ----------------------
40 * In the interest of clarity regarding the origin and status of this
41 * SLAC software, this and all the preceding Stanford University notices
42 * are to remain affixed to any copy or derivative of this software made
43 * or distributed by the recipient and are to be affixed to any copy of
44 * software made or distributed by the recipient that contains a copy or
45 * derivative of this software.
46 *
47 * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
48 */
49/*
50 * adaptations to also handle SPI devices
51 * by Thomas Doerfler, embedded brains GmbH, Puchheim, Germany
52 */
53#if HAVE_CONFIG_H
54#include "config.h"
55#endif
56
57#include <string.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <errno.h>
61#include <assert.h>
62#include <stdarg.h>
63
64#include <rtems.h>
65#include <rtems/error.h>
66#include <rtems/bspIo.h>
67#include <rtems/libio.h>
68
69#include <rtems/libi2c.h>
70
71#define DRVNM "libi2c:"
72
73#define MAX_NO_BUSSES   8       /* Also limited by the macro building minor numbers */
74#define MAX_NO_DRIVERS  16      /* Number of high level drivers we support          */
75
76#define MINOR2ADDR(minor)       ((minor)&((1<<10)-1))
77#define MINOR2BUS(minor)        (((minor)>>10)&7)
78#define MINOR2DRV(minor)        ((minor)>>13)
79
80/* Check the 'minor' argument, i.e., verify that
81 * we have a driver connected
82 */
83#define DECL_CHECKED_BH(b, bh, m, s)\
84        unsigned b = MINOR2BUS(m);              \
85        rtems_libi2c_bus_t      *bh;            \
86        if ( b >= MAX_NO_BUSSES || 0 == (bh=busses[b].bush) ) { \
87                return s RTEMS_INVALID_NUMBER;  \
88        }
89
90#define DECL_CHECKED_DRV(d, b, m)       \
91        unsigned d = MINOR2DRV(m);              \
92        unsigned b = MINOR2BUS(m);              \
93        if (   b >= MAX_NO_BUSSES  || 0 == busses[b].bush       \
94                || d >  MAX_NO_DRIVERS || (d && 0 == drvs[d-1].drv )) {\
95                return RTEMS_INVALID_NUMBER;    \
96        }
97
98#define DISPATCH(rval, entry, dflt)     \
99        do {                            \
100                rtems_driver_address_table *ops = drvs[--drv].drv->ops; \
101                rval = ops->entry ? ops->entry(major,minor,arg) : dflt; \
102        } while (0)
103
104
105rtems_device_major_number rtems_libi2c_major;
106
107static boolean            is_initialized = FALSE;
108
109static struct i2cbus
110{
111  rtems_libi2c_bus_t *bush;
112  volatile rtems_id mutex;      /* lock this across start -> stop */
113  volatile short waiting;
114  volatile char started;
115  char *name;
116} busses[MAX_NO_BUSSES] = { { 0 } };
117
118static struct
119{
120  rtems_libi2c_drv_t *drv;
121} drvs[MAX_NO_DRIVERS] = { { 0} };
122
123static rtems_id libmutex = 0;
124
125#define LOCK(m)         assert(!rtems_semaphore_obtain((m), RTEMS_WAIT, RTEMS_NO_TIMEOUT))
126#define UNLOCK(m)       rtems_semaphore_release((m))
127
128#define LIBLOCK()       LOCK(libmutex)
129#define LIBUNLOCK()     UNLOCK(libmutex)
130
131#define MUTEX_ATTS      \
132        (  RTEMS_PRIORITY                       \
133         | RTEMS_BINARY_SEMAPHORE       \
134         |RTEMS_INHERIT_PRIORITY        \
135         |RTEMS_NO_PRIORITY_CEILING     \
136         |RTEMS_LOCAL )
137
138/* During early stages of life, stdio is not available */
139
140static void
141safe_printf (const char *fmt, ...)
142{
143va_list ap;
144
145        va_start(ap, fmt);
146        if ( _System_state_Is_up( _System_state_Get() ) )
147                vfprintf( stderr, fmt, ap );
148        else
149                vprintk( fmt, ap );
150        va_end(ap);
151}
152
153static rtems_status_code
154mutexCreate (rtems_name nm, rtems_id *pm)
155{
156  rtems_status_code sc;
157
158  if (RTEMS_SUCCESSFUL !=
159      (sc = rtems_semaphore_create (nm, 1, MUTEX_ATTS, 0, pm))) {
160        if ( _System_state_Is_up( _System_state_Get() ) )
161        rtems_error (sc, DRVNM " unable to create mutex\n");
162        else
163                printk (DRVNM " unable to crate mutex (status code %i)\n", sc);
164  }
165  return sc;
166}
167
168/* Lock a bus avoiding to have a mutex, which is mostly
169 * unused, hanging around all the time. We just create
170 * and delete it on the fly...
171 *
172 * ASSUMES: argument checked by caller
173 */
174
175static void
176lock_bus (int busno)
177{
178rtems_status_code sc;
179struct i2cbus *bus = &busses[busno];
180
181  LIBLOCK ();
182  if (!bus->waiting) {
183        rtems_id m;
184    /* nobody is holding the bus mutex - it's not there. Create it on the fly */
185    sc = mutexCreate (rtems_build_name ('i', '2', 'c', '0' + busno), &m);
186    if ( RTEMS_SUCCESSFUL != sc ) {
187      LIBUNLOCK ();
188      rtems_panic (DRVNM " unable to create bus lock");
189    } else {
190          bus->mutex = m;
191        }
192  }
193  /* count number of people waiting on this bus; only the last one deletes the mutex */
194  bus->waiting++;
195  LIBUNLOCK ();
196  /* Now lock this bus */
197  LOCK (bus->mutex);
198}
199
200static void
201unlock_bus (int busno)
202{
203  struct i2cbus *bus = &busses[busno];
204  LIBLOCK ();
205  UNLOCK (bus->mutex);
206  if (!--bus->waiting) {
207    rtems_semaphore_delete (bus->mutex);
208  }
209  LIBUNLOCK ();
210}
211
212/* Note that 'arg' is always passed in as NULL */
213rtems_status_code
214rtems_i2c_init (rtems_device_major_number major, rtems_device_minor_number minor,
215          void *arg)
216{
217  rtems_status_code rval;
218  /* No busses or drivers can be registered at this point;
219   * avoid the macro aborting with an error
220  DECL_CHECKED_DRV (drv, busno, minor)
221   */
222
223  rval = mutexCreate (rtems_build_name ('l', 'I', '2', 'C'), &libmutex);
224
225  if ( RTEMS_SUCCESSFUL == rval ) {
226        is_initialized     = TRUE;
227        rtems_libi2c_major = major;
228  } else {
229        libmutex = 0;
230  }
231  return rval;
232}
233
234rtems_status_code
235rtems_i2c_open (rtems_device_major_number major, rtems_device_minor_number minor,
236          void *arg)
237{
238  rtems_status_code rval;
239  DECL_CHECKED_DRV (drv, busno, minor)
240
241    if (0 == drv) {
242    rval = RTEMS_SUCCESSFUL;
243  } else {
244    DISPATCH (rval, open_entry, RTEMS_SUCCESSFUL);
245  }
246  return rval;
247}
248
249rtems_status_code
250rtems_i2c_close (rtems_device_major_number major, rtems_device_minor_number minor,
251           void *arg)
252{
253  rtems_status_code rval;
254  DECL_CHECKED_DRV (drv, busno, minor)
255
256    if (0 == drv) {
257    rval = RTEMS_SUCCESSFUL;
258  } else {
259    DISPATCH (rval, close_entry, RTEMS_SUCCESSFUL);
260  }
261  return rval;
262}
263
264rtems_status_code
265rtems_i2c_read (rtems_device_major_number major, rtems_device_minor_number minor,
266          void *arg)
267{
268  int rval;                     /* int so we can check for negative value */
269  rtems_libio_rw_args_t *rwargs = arg;
270  DECL_CHECKED_DRV (drv, busno, minor)
271
272    if (0 == rwargs->count) {
273    rwargs->bytes_moved = 0;
274    return RTEMS_SUCCESSFUL;
275  }
276
277  if (0 == drv) {
278    rval =
279      rtems_libi2c_start_read_bytes (minor, (unsigned char *) rwargs->buffer,
280                                     rwargs->count);
281    if (rval >= 0) {
282      rwargs->bytes_moved = rval;
283      rtems_libi2c_send_stop (minor);
284      rval = RTEMS_SUCCESSFUL;
285    } else {
286      rval = -rval;
287    }
288  } else {
289    DISPATCH (rval, read_entry, RTEMS_NOT_IMPLEMENTED);
290  }
291  return rval;
292}
293
294rtems_status_code
295rtems_i2c_write (rtems_device_major_number major, rtems_device_minor_number minor,
296           void *arg)
297{
298  int rval;                     /* int so we can check for negative value */
299  rtems_libio_rw_args_t *rwargs = arg;
300  DECL_CHECKED_DRV (drv, busno, minor)
301
302    if (0 == rwargs->count) {
303    rwargs->bytes_moved = 0;
304    return RTEMS_SUCCESSFUL;
305  }
306
307  if (0 == drv) {
308    rval =
309      rtems_libi2c_start_write_bytes (minor, (unsigned char *) rwargs->buffer,
310                                      rwargs->count);
311    if (rval >= 0) {
312      rwargs->bytes_moved = rval;
313      rtems_libi2c_send_stop (minor);
314      rval = RTEMS_SUCCESSFUL;
315    } else {
316      rval = -rval;
317    }
318  } else {
319    DISPATCH (rval, write_entry, RTEMS_NOT_IMPLEMENTED);
320  }
321  return rval;
322}
323
324rtems_status_code
325rtems_i2c_ioctl (rtems_device_major_number major, rtems_device_minor_number minor,
326           void *arg)
327{
328  rtems_status_code rval;
329  DECL_CHECKED_DRV (drv, busno, minor)
330
331    if (0 == drv) {
332    rval = RTEMS_NOT_IMPLEMENTED;
333  } else {
334    DISPATCH (rval, control_entry, RTEMS_NOT_IMPLEMENTED);
335  }
336  return rval;
337}
338
339
340/* Our ops just dispatch to the registered drivers */
341rtems_driver_address_table rtems_libi2c_io_ops = {
342  initialization_entry:  rtems_i2c_init,
343  open_entry:            rtems_i2c_open,
344  close_entry:           rtems_i2c_close,
345  read_entry:            rtems_i2c_read,
346  write_entry:           rtems_i2c_write,
347  control_entry:         rtems_i2c_ioctl,
348};
349
350int
351rtems_libi2c_initialize ()
352{
353  rtems_status_code sc;
354
355  if (is_initialized) {
356    /*
357     * already called before? then skip this step
358     */
359    return 0;
360  }
361 
362  /* rtems_io_register_driver does NOT currently check nor report back
363   * the return code of the 'init' operation, so we cannot
364   * rely on return code since it may seem OK even if the driver 'init;
365   * op failed.
366   * Let 'init' handle 'is_initialized'...
367   */
368  sc = rtems_io_register_driver (0, &rtems_libi2c_io_ops, &rtems_libi2c_major);
369  if (RTEMS_SUCCESSFUL != sc) {
370    safe_printf(
371             DRVNM " Claiming driver slot failed (rtems status code %i)\n",
372             sc);
373        if ( libmutex )
374        rtems_semaphore_delete (libmutex);
375    libmutex = 0;
376        is_initialized = FALSE;
377    return -1;
378  }
379
380  return 0;
381}
382
383int
384rtems_libi2c_register_bus (char *name, rtems_libi2c_bus_t * bus)
385{
386  int i;
387  rtems_status_code err;
388  char *nmcpy = malloc (name ? strlen (name) + 1 : 20);
389  char tmp, *chpt;
390  struct stat sbuf;
391
392  strcpy (nmcpy, name ? name : "/dev/i2c");
393
394
395  /* check */
396  if ('/' != *nmcpy) {
397    safe_printf ( DRVNM "Bad name; must be an absolute path starting with '/'\n");
398    return -RTEMS_INVALID_NAME;
399  }
400  /* file must not exist */
401  if (!stat (nmcpy, &sbuf)) {
402    safe_printf ( DRVNM "Bad name; file exists already\n");
403    return -RTEMS_INVALID_NAME;
404  }
405
406  /* we already verified that there is at least one '/' */
407  chpt = strrchr (nmcpy, '/') + 1;
408  tmp = *chpt;
409  *chpt = 0;
410  i = stat (nmcpy, &sbuf);
411  *chpt = tmp;
412  if (i) {
413    safe_printf ( DRVNM "Get %s status failed: %s\n",
414             nmcpy, strerror(errno));
415    return -RTEMS_INVALID_NAME;
416  }
417  /* should be a directory since name terminates in '/' */
418
419
420  if (!libmutex) {
421    safe_printf ( DRVNM " library not initialized\n");
422    return -RTEMS_NOT_DEFINED;
423  }
424
425  if (bus->size < sizeof (*bus)) {
426    safe_printf ( DRVNM " bus-ops size too small -- misconfiguration?\n");
427    return -RTEMS_NOT_CONFIGURED;
428  }
429
430  LIBLOCK ();
431  for (i = 0; i < MAX_NO_BUSSES; i++) {
432    if (!busses[i].bush) {
433      /* found a free slot */
434      busses[i].bush = bus;
435      busses[i].mutex = 0;
436      busses[i].waiting = 0;
437      busses[i].started = 0;
438
439      if (!name)
440        sprintf (nmcpy + strlen (nmcpy), "%i", i);
441
442      if ((err = busses[i].bush->ops->init (busses[i].bush))) {
443        /* initialization failed */
444        i = -err;
445      } else {
446        busses[i].name = nmcpy;;
447        nmcpy = 0;
448      }
449
450      break;
451    }
452  }
453  LIBUNLOCK ();
454
455  if (i >= MAX_NO_BUSSES) {
456    i = -RTEMS_TOO_MANY;
457  }
458
459  free (nmcpy);
460
461  return i;
462}
463
464static int
465not_started (int busno)
466{
467  int rval;
468  lock_bus (busno);
469  rval = !busses[busno].started;
470  unlock_bus (busno);
471  return rval;
472}
473
474rtems_status_code
475rtems_libi2c_send_start (rtems_device_minor_number minor)
476{
477  int rval;
478  DECL_CHECKED_BH (busno, bush, minor, +)
479
480    lock_bus (busno);
481  rval = bush->ops->send_start (bush);
482
483  /* if this failed or is not the first start, unlock */
484  if (rval || busses[busno].started) {
485    /* HMM - what to do if the 1st start failed ?
486     * try to reset...
487     */
488    if (!busses[busno].started) {
489      /* just in case the bus driver fiddles with errno */
490      int errno_saved = errno;
491      bush->ops->init (bush);
492      errno = errno_saved;
493    } else if (rval) {
494      /* failed restart */
495      rtems_libi2c_send_stop (minor);
496    }
497    unlock_bus (busno);
498  } else {
499    /* successful 1st start; keep bus locked until stop is sent */
500    busses[busno].started = 1;
501  }
502  return rval;
503}
504
505rtems_status_code
506rtems_libi2c_send_stop (rtems_device_minor_number minor)
507{
508  rtems_status_code rval;
509  DECL_CHECKED_BH (busno, bush, minor, +)
510
511    if (not_started (busno))
512    return RTEMS_NOT_OWNER_OF_RESOURCE;
513
514  rval = bush->ops->send_stop (bush);
515
516  busses[busno].started = 0;
517
518  unlock_bus (busno);
519  return rval;
520}
521
522rtems_status_code
523rtems_libi2c_send_addr (rtems_device_minor_number minor, int rw)
524{
525  rtems_status_code sc;
526  DECL_CHECKED_BH (busno, bush, minor, +)
527
528    if (not_started (busno))
529    return RTEMS_NOT_OWNER_OF_RESOURCE;
530
531  sc = bush->ops->send_addr (bush, MINOR2ADDR (minor), rw);
532  if (RTEMS_SUCCESSFUL != sc)
533    rtems_libi2c_send_stop (minor);
534  return sc;
535}
536
537int
538rtems_libi2c_read_bytes (rtems_device_minor_number minor,
539                         unsigned char *bytes,
540                         int nbytes)
541{
542  int sc;
543  DECL_CHECKED_BH (busno, bush, minor, -)
544
545    if (not_started (busno))
546    return -RTEMS_NOT_OWNER_OF_RESOURCE;
547
548  sc = bush->ops->read_bytes (bush, bytes, nbytes);
549  if (sc < 0)
550    rtems_libi2c_send_stop (minor);
551  return sc;
552}
553
554int
555rtems_libi2c_write_bytes (rtems_device_minor_number minor,
556                          unsigned char *bytes,
557                          int nbytes)
558{
559  int sc;
560  DECL_CHECKED_BH (busno, bush, minor, -)
561
562    if (not_started (busno))
563    return -RTEMS_NOT_OWNER_OF_RESOURCE;
564
565  sc = bush->ops->write_bytes (bush, bytes, nbytes);
566  if (sc < 0)
567    rtems_libi2c_send_stop (minor);
568  return sc;
569}
570
571int
572rtems_libi2c_ioctl (rtems_device_minor_number minor,
573                    int cmd,
574                    ...)
575{
576  va_list            ap;
577  int sc = 0;
578  void *args;
579  DECL_CHECKED_BH (busno, bush, minor, -)
580
581    if (not_started (busno))
582    return -RTEMS_NOT_OWNER_OF_RESOURCE;
583
584  va_start(ap, cmd);
585  args = va_arg(ap, void *);
586
587  switch(cmd) {
588    /*
589     * add ioctls defined for this level here:   
590     */
591   
592  case RTEMS_LIBI2C_IOCTL_START_TFM_READ_WRITE:
593    /*
594     * address device, then set transfer mode and perform read_write transfer
595     */
596    /*
597     * perform start/address
598     */
599    if (sc == 0) {
600      sc = rtems_libi2c_send_start (minor);
601    }
602    /*
603     * set tfr mode
604     */
605    if (sc == 0) {
606      sc = bush->ops->ioctl
607        (bush,
608         RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
609         &((rtems_libi2c_tfm_read_write_t *)args)->tfr_mode);
610    }
611    /*
612     * perform read_write
613     */
614    if (sc == 0) {
615      sc = bush->ops->ioctl
616        (bush,
617         RTEMS_LIBI2C_IOCTL_READ_WRITE,
618         &((rtems_libi2c_tfm_read_write_t *)args)->rd_wr);
619    }
620    break;
621  default:
622    sc = bush->ops->ioctl (bush, cmd, args);
623    break;
624  }
625  if (sc < 0)
626    rtems_libi2c_send_stop (minor);
627  return sc;
628}
629
630static int
631do_s_rw (rtems_device_minor_number minor,
632         unsigned char *bytes,
633         int nbytes,
634         int rw)
635{
636  rtems_status_code sc;
637  rtems_libi2c_bus_t *bush;
638
639  if ((sc = rtems_libi2c_send_start (minor)))
640    return -sc;
641
642  /* at this point, we hold the bus and are sure the minor number is valid */
643  bush = busses[MINOR2BUS (minor)].bush;
644
645  if ((sc = bush->ops->send_addr (bush, MINOR2ADDR (minor), rw))) {
646    rtems_libi2c_send_stop (minor);
647    return -sc;
648  }
649
650  if (rw)
651    sc = bush->ops->read_bytes (bush, bytes, nbytes);
652  else
653    sc = bush->ops->write_bytes (bush, bytes, nbytes);
654
655  if (sc < 0) {
656    rtems_libi2c_send_stop (minor);
657  }
658  return sc;
659}
660
661int
662rtems_libi2c_start_read_bytes (rtems_device_minor_number minor,
663                               unsigned char *bytes,
664                               int nbytes)
665{
666  return do_s_rw (minor, bytes, nbytes, 1);
667}
668
669int
670rtems_libi2c_start_write_bytes (rtems_device_minor_number minor,
671                                unsigned char *bytes,
672                                int nbytes)
673{
674  return do_s_rw (minor, bytes, nbytes, 0);
675}
676
677int
678rtems_libi2c_register_drv (char *name, rtems_libi2c_drv_t * drvtbl,
679                           unsigned busno, unsigned i2caddr)
680{
681  int i;
682  rtems_status_code err;
683  rtems_device_minor_number minor;
684
685  if (!libmutex) {
686    safe_printf ( DRVNM " library not initialized\n");
687    return -RTEMS_NOT_DEFINED;
688  }
689
690  if (name && strchr (name, '/')) {
691    safe_printf ( DRVNM "Invalid name: '%s' -- must not contain '/'\n", name);
692    return -RTEMS_INVALID_NAME;
693  }
694
695  if (busno >= MAX_NO_BUSSES || !busses[busno].bush || i2caddr >= 1 << 10) {
696    errno = EINVAL;
697    return -RTEMS_INVALID_NUMBER;
698  }
699
700  if (drvtbl->size < sizeof (*drvtbl)) {
701    safe_printf ( DRVNM " drv-ops size too small -- misconfiguration?\n");
702    return -RTEMS_NOT_CONFIGURED;
703  }
704
705  /* allocate slot */
706  LIBLOCK ();
707  for (i = 0; i < MAX_NO_DRIVERS; i++) {
708    /* driver # 0 is special, it is the built-in raw driver */
709    if (!drvs[i].drv) {
710      char *str;
711      dev_t dev;
712      uint32_t mode;
713
714      /* found a free slot; encode slot + 1 ! */
715      minor = ((i + 1) << 13) | RTEMS_LIBI2C_MAKE_MINOR (busno, i2caddr);
716
717      if (name) {
718        str = malloc (strlen (busses[busno].name) + strlen (name) + 2);
719        sprintf (str, "%s.%s", busses[busno].name, name);
720
721        dev = rtems_filesystem_make_dev_t (rtems_libi2c_major, minor);
722
723        mode = 0111 | S_IFCHR;
724        if (drvtbl->ops->read_entry)
725          mode |= 0444;
726        if (drvtbl->ops->write_entry)
727          mode |= 0222;
728
729        /* note that 'umask' is applied to 'mode' */
730        if (mknod (str, mode, dev)) {
731          safe_printf( DRVNM
732                   "Creating device node failed: %s; you can try to do it manually...\n",
733                   strerror (errno));
734        }
735
736        free (str);
737      }
738
739      drvs[i].drv = drvtbl;
740
741      if (drvtbl->ops->initialization_entry)
742        err =
743          drvs[i].drv->ops->initialization_entry (rtems_libi2c_major, minor,
744                                                  0);
745      else
746        err = RTEMS_SUCCESSFUL;
747
748      LIBUNLOCK ();
749      return err ? -err : minor;
750    }
751  }
752  LIBUNLOCK ();
753  return -RTEMS_TOO_MANY;
754}
Note: See TracBrowser for help on using the repository browser.