]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/compat/posix/v2_0/src/mqueue.cxx
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / compat / posix / v2_0 / src / mqueue.cxx
1 /*========================================================================
2 //
3 //      mqueue.cxx
4 //
5 //      Message queues tests
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):     jlarmour
44 // Contributors:  
45 // Date:          2000-05-14
46 // Purpose:       This file provides the implementation for POSIX message
47 //                queues
48 // Description:   It uses eCos kernel mqueues as the underlying
49 //                implementation
50 // Usage:         
51 //
52 //####DESCRIPTIONEND####
53 //
54 //======================================================================
55 */
56
57 /* CONFIGURATION */
58
59 #include <pkgconf/posix.h>
60
61 #include <pkgconf/kernel.h>
62
63 /* INCLUDES */
64
65 #include <cyg/infra/cyg_type.h>      // common types etc.
66 #include <cyg/infra/cyg_ass.h>       // Assertion support
67 #include <cyg/infra/cyg_trac.h>      // Tracing support
68 #include <cyg/kernel/mqueue.hxx>     // eCos Mqueue Header
69 #include <cyg/kernel/sched.hxx>      // Cyg_Scheduler::lock()
70 #include <cyg/kernel/sched.inl>      // inlines for above
71 #include <mqueue.h>                  // Standard POSIX mqueue header
72 #include <sys/types.h>               // mode_t, ssize_t
73 #include <limits.h>                  // PATH_MAX
74 #include <stdlib.h>                  // malloc, etc.
75 #include <errno.h>                   // errno
76 #include <fcntl.h>                   // O_*
77 #include <stdarg.h>                  // varargs
78 #include <pthread.h>                 // mutexes
79 #include <string.h>                  // strncpy
80 #ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
81 # include <signal.h>
82 # include "pprivate.h"               // cyg_sigqueue()
83 #endif
84 #ifdef CYGFUN_KERNEL_THREADS_TIMER
85 # include <time.h>
86 # include "pprivate.h"               // cyg_timespec_to_ticks()
87 #endif
88
89 /* CONSTANTS */
90
91 #define MQ_VALID_MAGIC  0x6db256c1
92
93 /* TYPE DEFINITIONS */
94
95 struct mqtabent;
96
97 // this is a queue user - each one of these corresponds to a mqd_t
98 struct mquser {
99     int flags;               // O_RDONLY, O_WRONLY, O_RDWR, O_NONBLOCK
100     struct mqtabent *tabent; // back pointer to table entry
101     struct mquser *next;
102     bool notifieruser;       // POSIX sucks so bad. It requires a mq_close
103                              // to only deregister the notification if it
104                              // was done via this descriptor. So we have to
105                              // know if it was this one
106 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
107     cyg_uint32 magic;        // magic number: MQ_VALID_MAGIC if valid
108 #endif    
109 };
110
111 struct mqtabent {
112     char name[ PATH_MAX ]; // ascii name - set to "" when unused
113     Cyg_Mqueue *mq;        // the underlying queue object
114     long maxmsg;           // as set on creation
115     long msgsize;          // as set on creation
116     bool unlinkme;         // unlink when final user closes?
117     struct mquser *users;  // each user
118
119 #ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
120     const struct sigevent *sigev; // notification event
121 #endif
122 };
123
124 /* GLOBALS */
125
126 static struct mqtabent mqtab[ CYGNUM_POSIX_MQUEUE_OPEN_MAX ];
127 static pthread_mutex_t mqtab_mut = PTHREAD_MUTEX_INITIALIZER;
128
129 /* LOCAL FUNCTIONS */
130
131 //------------------------------------------------------------------------
132
133 // placement new definition
134 inline void *operator new(size_t size, void *ptr) 
135
136     CYG_CHECK_DATA_PTR( ptr, "Bad pointer" ); 
137     return ptr; 
138
139
140 // Deallocation callback from Cyg_Mqueue
141 static void
142 my_free( void *ptr, size_t )
143 {
144     free( ptr );
145 }
146
147 //------------------------------------------------------------------------
148
149 // Do the actual "unlink" of a queue, i.e. mark it invalid in the table.
150 // The table mutex is assumed to be locked
151 static void
152 do_mq_unlink( struct mqtabent *tabent )
153 {
154     CYG_REPORT_FUNCTION();
155     CYG_CHECK_DATA_PTRC( tabent );
156
157     tabent->name[0] = '\0'; // won't match anything the user sends now
158     tabent->mq->~Cyg_Mqueue();
159     free( tabent->mq );
160     tabent->mq=NULL;
161
162     CYG_REPORT_RETURN();
163 }
164
165 //------------------------------------------------------------------------
166
167 #ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
168
169 static void
170 notifyme( Cyg_Mqueue &q, CYG_ADDRWORD data )
171 {
172     CYG_REPORT_FUNCTION();
173     struct mquser *user = (struct mquser *)data;
174     CYG_CHECK_DATA_PTRC( user );
175     struct mqtabent *tabent = user->tabent;
176     CYG_CHECK_DATA_PTRC( tabent );
177
178     Cyg_Scheduler::lock();
179     // we may have been pre-empted before this, so check there's still a
180     // notification to do
181
182     if ( NULL == tabent->sigev ) {
183         Cyg_Scheduler::unlock();
184         CYG_REPORT_RETURN();
185         return;
186     } // if
187
188     const struct sigevent *ev = tabent->sigev;
189     
190     // first deregister
191     q.setnotify( NULL, 0 );
192     tabent->sigev = NULL;
193     user->notifieruser = false; // not any more
194     
195     // now the rest of the world can go
196     Cyg_Scheduler::unlock();
197     
198     // queue event. If it fails... nothing we can do :-( so ignore return code
199     cyg_sigqueue( ev, SI_MESGQ );
200
201     cyg_deliver_signals();
202     
203     CYG_REPORT_RETURN();
204 }
205
206 #endif // ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
207
208 //------------------------------------------------------------------------
209
210 /* EXPORTED FUNCTIONS */
211
212 externC mqd_t
213 mq_open( const char *name, int oflag, ... )
214 {
215     CYG_REPORT_FUNCTYPE( "returning %08x" );
216     CYG_REPORT_FUNCARG2( "name=%08x, oflag=%d", name, oflag );
217     CYG_CHECK_DATA_PTRC( name );
218
219     if ( ((oflag & O_RDONLY) != O_RDONLY) &&
220          ((oflag & O_WRONLY) != O_WRONLY) &&
221          ((oflag & O_RDWR) != O_RDWR)) {
222         // user didn't specify mode
223         errno = EINVAL;
224         CYG_REPORT_RETVAL( -1 );
225         return (mqd_t)-1;
226     } // if
227
228     mqd_t retval;
229     cyg_ucount32 i;
230     struct mqtabent *qtabent=NULL;
231     int interr;
232
233     interr = pthread_mutex_lock( &mqtab_mut );
234     // should never fail
235     CYG_ASSERT( interr == 0, "internal lock failed!" );
236     
237     // find if a matching entry exists first
238     // FIXME: Should check for length and return ENAMETOOLONG
239     for ( i=0; i < CYGNUM_POSIX_MQUEUE_OPEN_MAX; i++ ) {
240         if ( 0 == strncmp(name, mqtab[i].name, PATH_MAX) ) {
241             qtabent = &mqtab[i];
242             break;
243         } // if
244     } // for
245     
246     if ( (NULL != qtabent) && (O_EXCL == (oflag & O_EXCL)) ) {
247         errno = EEXIST;
248         retval = (mqd_t)-1;
249         goto exit_unlock;
250     }
251         
252     if ( (NULL == qtabent) && (O_CREAT != (oflag & O_CREAT)) ) {
253         errno = ENOENT;
254         retval = (mqd_t)-1;
255         goto exit_unlock;
256     }
257
258     // so if we didn't find something, we must be being asked to create it
259     if (NULL == qtabent) {
260         mode_t mode; // FIXME: mode ignored for now
261         const struct mq_attr *attr;
262         const struct mq_attr default_attr = { 0, MQ_OPEN_MAX, 128 };
263         va_list args;
264         
265         va_start( args, oflag );
266         mode = va_arg( args, mode_t );
267         attr = va_arg( args, struct mq_attr * );
268         va_end( args );
269
270         // find an empty table entry
271         for ( i=0; i < CYGNUM_POSIX_MQUEUE_OPEN_MAX; i++ ) {
272             if ( NULL == mqtab[i].mq )
273                 break;
274         }
275
276         // if not found, table is full
277         if ( i == CYGNUM_POSIX_MQUEUE_OPEN_MAX ) {
278             errno = ENFILE;
279             retval = (mqd_t)-1;
280             goto exit_unlock;
281         }
282
283         Cyg_Mqueue::qerr_t qerr;
284
285         // user can specify NULL attr, which means arbitrary message queue
286         // size! Duh.
287         if ( NULL == attr )
288             attr = &default_attr;
289         else {
290             // if they do supply one, POSIX says we're meant to check it
291             if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0) {
292                 errno = EINVAL;
293                 retval = (mqd_t)-1;
294                 goto exit_unlock;
295             }
296         } // else
297
298         // allocate the underlying queue
299         Cyg_Mqueue *mqholder = (Cyg_Mqueue *)malloc( sizeof(Cyg_Mqueue) );
300         if ( NULL == mqholder ) {
301             errno = ENOSPC;
302             retval = (mqd_t)-1;
303             goto exit_unlock;
304         }
305             
306         // construct it with placement new
307         mqtab[i].mq = new (mqholder) Cyg_Mqueue( attr->mq_maxmsg,
308                                                  attr->mq_msgsize,
309                                                  &malloc, &my_free, &qerr );
310             
311         switch (qerr) {
312         case Cyg_Mqueue::OK:
313             break;
314         case Cyg_Mqueue::NOMEM:
315             free( mqholder );
316             errno = ENOSPC;
317             retval = (mqd_t)-1;
318             goto exit_unlock;
319         default:
320             CYG_FAIL("Unhandled Cyg_Mqueue constructor return error");
321             break;
322         } // switch
323
324         mqtab[i].users = (struct mquser *) malloc( sizeof(struct mquser) );
325         if ( NULL == mqtab[i].users ) {
326             mqtab[i].mq->~Cyg_Mqueue();
327             free( mqholder );
328             errno = ENOSPC;
329             retval = (mqd_t)-1;
330             goto exit_unlock;
331         }
332
333         // initialize mqtab[i]
334         mqtab[i].maxmsg  = attr->mq_maxmsg;
335         mqtab[i].msgsize = attr->mq_msgsize;
336         mqtab[i].unlinkme = false;
337 #ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
338         mqtab[i].sigev = NULL;
339 #endif
340         strncpy( mqtab[i].name, name, PATH_MAX );
341
342         // initialize first mqtab[i].users
343         mqtab[i].users->next = NULL;
344         // set the mode for later, but also note that O_NONBLOCK can
345         // be set in oflags *or* the attr the user passed
346         mqtab[i].users->flags = oflag | (attr->mq_flags & O_NONBLOCK);
347
348         // set back pointer so that message queue handle can find actual queue
349         mqtab[i].users->tabent = &mqtab[i]; 
350         
351         mqtab[i].users->notifieruser = false;
352
353 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
354         mqtab[i].users->magic = MQ_VALID_MAGIC; // now valid
355 #endif
356         
357         retval=(mqd_t)mqtab[i].users;
358
359         goto exit_unlock;
360     } // if (NULL == qtabent)
361
362     // so we're not creating, and we have a valid qtabent
363
364     // But this qtabent may be being unlinked. If so, we are permitted
365     // to return an error, so we will. (see under mq_unlink() in POSIX)
366     // Which error though? EINVAL seems best, but POSIX doesn't say :-/
367
368     if (true == qtabent->unlinkme) {
369         errno = EINVAL;
370         retval = (mqd_t)-1;
371         goto exit_unlock;
372     }
373     
374     // now we have a usable qtabent
375
376     struct mquser *user;
377     user = (struct mquser *) malloc( sizeof(struct mquser) );
378     if ( NULL == user ) {
379             errno = ENOSPC;
380             retval = (mqd_t)-1;
381             goto exit_unlock;
382     }
383
384     // prepend to qtab user list
385     user->next = qtabent->users;
386     qtabent->users = user;
387
388     // set back pointer so that message queue handle can find actual queue
389     user->tabent = qtabent;
390
391     user->flags = oflag;
392
393 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
394     user->magic = MQ_VALID_MAGIC; // now valid
395 #endif
396
397     retval=(mqd_t)user;
398
399  exit_unlock:
400     interr = pthread_mutex_unlock( &mqtab_mut );
401     // should never fail
402     CYG_ASSERT( interr == 0, "internal lock failed!" );
403     CYG_REPORT_RETVAL( retval );
404     return retval;
405 } // mq_open()
406
407 //------------------------------------------------------------------------
408
409 // NOTE: It is the *user*'s responsibility to ensure that nothing is
410 // blocked in mq_send() or mq_receive() when closing the queue with
411 // that descriptor. The standard does not specify the behaviour, so that's
412 // what I am assuming
413
414 externC int
415 mq_close( mqd_t mqdes )
416 {
417     CYG_REPORT_FUNCTYPE( "returning %d" );
418     CYG_REPORT_FUNCARG1XV( mqdes );
419     
420     struct mquser *user = (struct mquser *)mqdes;
421
422 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
423     if ( user->magic != MQ_VALID_MAGIC ) {
424         errno  = EBADF;
425         CYG_REPORT_RETVAL( -1 );
426         return -1;
427     }
428 #endif
429     
430     int interr;
431
432     interr = pthread_mutex_lock( &mqtab_mut );
433     // should never fail
434     CYG_ASSERT( interr == 0, "internal lock failed!" );
435
436     struct mqtabent *tabent = user->tabent;
437     struct mquser *usertmp;
438     
439     // perhaps should return EBADF instead of assert?
440     CYG_ASSERT( tabent->users != NULL, "Null message queue user list" );
441
442 #ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
443     // deregister notification iff this was the message queue descriptor
444     // that was used to register it (POSIX says)
445     if ( true == user->notifieruser ) {
446         tabent->mq->setnotify( NULL, 0 );
447         tabent->sigev = NULL;
448         // not worth clearing notifieruser
449     }
450 #endif
451
452     // find in the list for this queue and remove - sucks a bit, but seems
453     // best over all - the list shouldn't be too long
454     if ( tabent->users == user ) {
455         tabent->users = user->next;  // remove
456     } else {
457         for ( usertmp=tabent->users;
458               NULL != usertmp->next;
459               usertmp = usertmp->next ) {
460             if ( usertmp->next == user )
461                 break;
462         } // for
463
464         // perhaps should return EBADF instead of assert?
465         CYG_ASSERT( usertmp->next != NULL, "Couldn't find message queue user" );
466
467         usertmp->next = user->next; // remove
468     } // else
469     
470 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
471     user->magic = 0; // invalidate
472 #endif
473
474     // free it up
475     free( user );
476
477     if ( (true == tabent->unlinkme) && (NULL == tabent->users) ) {
478         do_mq_unlink( tabent );
479     } // if
480
481     interr = pthread_mutex_unlock( &mqtab_mut );
482     // should never fail
483     CYG_ASSERT( interr == 0, "internal lock failed!" );
484     CYG_REPORT_RETVAL( 0 );
485     return 0;
486 } // mq_close()
487
488
489 //------------------------------------------------------------------------
490
491 externC int
492 mq_unlink( const char *name )
493 {
494     CYG_REPORT_FUNCTYPE( "returning %d" );
495     CYG_REPORT_FUNCARG1( "name=%s", name );
496
497     int retval, interr;
498     cyg_ucount32 i;
499     struct mqtabent *qtabent=NULL;
500
501     interr = pthread_mutex_lock( &mqtab_mut );
502     // should never fail
503     CYG_ASSERT( interr == 0, "internal lock failed!" );
504
505     // find the entry first
506     // FIXME: Should check for length and return ENAMETOOLONG
507     for ( i=0; i < CYGNUM_POSIX_MQUEUE_OPEN_MAX; i++ ) {
508         if ( 0 == strncmp(name, mqtab[i].name, PATH_MAX) ) {
509             qtabent = &mqtab[i];
510             break;
511         } // if
512     } // for
513
514     if ( NULL == qtabent ) { // not found
515         errno = ENOENT;
516         retval = -1;
517         goto exit_unlock;
518     }
519
520     if ( NULL != qtabent->users ) {   // still in use
521         qtabent->unlinkme = true;     // so mark it as pending deletion
522     } else {
523         do_mq_unlink( qtabent );
524     } // else
525
526     retval = 0;
527
528  exit_unlock:
529     interr = pthread_mutex_unlock( &mqtab_mut );
530     // should never fail
531     CYG_ASSERT( interr == 0, "internal lock failed!" );
532     CYG_REPORT_RETVAL( retval );
533     return retval;
534 } // mq_unlink()
535
536 //------------------------------------------------------------------------
537
538 externC int
539 mq_send( mqd_t mqdes, const char *msg_ptr, size_t msg_len,
540          unsigned int msg_prio )
541 {
542     CYG_REPORT_FUNCTYPE( "returning %d" );
543     CYG_REPORT_FUNCARG4( "mqdes=%08x, msg_ptr=%08x, msg_len=%u, msg_prio=%u",
544                          mqdes, msg_ptr, msg_len, msg_prio );
545     CYG_CHECK_DATA_PTRC( msg_ptr );
546     
547     struct mquser *user = (struct mquser *)mqdes;
548     struct mqtabent *tabent = user->tabent;
549
550 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
551     if ( user->magic != MQ_VALID_MAGIC ) {
552         errno  = EBADF;
553         CYG_REPORT_RETVAL( -1 );
554         return -1;
555     }
556 #endif
557     
558     if ( msg_len > (size_t)tabent->msgsize ) {
559         errno = EMSGSIZE;
560         CYG_REPORT_RETVAL( -1 );
561         return -1;
562     }
563
564     if ( msg_prio > MQ_PRIO_MAX ) {
565         errno = EINVAL;
566         CYG_REPORT_RETVAL( -1 );
567         return -1;
568     }
569
570     if ( (O_WRONLY != (user->flags & O_WRONLY)) && 
571          (O_RDWR != (user->flags & O_RDWR)) ) {
572         errno = EBADF;
573         CYG_REPORT_RETVAL( -1 );
574         return -1;
575     }
576
577     // go for it
578     Cyg_Mqueue::qerr_t err;
579     err = tabent->mq->put( msg_ptr, msg_len, msg_prio,
580                            ((user->flags & O_NONBLOCK) != O_NONBLOCK) );
581     switch (err) {
582
583     case Cyg_Mqueue::INTR:
584         errno = EINTR;
585         CYG_REPORT_RETVAL( -1 );
586         return -1;
587
588     case Cyg_Mqueue::WOULDBLOCK:
589         CYG_ASSERT( (user->flags & O_NONBLOCK) == O_NONBLOCK,
590                     "Message queue assumed non-blocking when blocking requested"
591             );
592         errno = EAGAIN;
593         CYG_REPORT_RETVAL( -1 );
594         return -1;
595         
596     case Cyg_Mqueue::OK:
597         CYG_REPORT_RETVAL( 0 );
598         return 0;
599
600     default:
601         CYG_FAIL( "unhandled message queue return code" );
602         return -1; // keep compiler happy
603     } // switch
604 } // mq_send()
605
606 //------------------------------------------------------------------------
607
608
609 externC ssize_t
610 mq_receive( mqd_t mqdes, char *msg_ptr, size_t msg_len,
611             unsigned int *msg_prio )
612 {
613     CYG_REPORT_FUNCTYPE( "returning %ld" );
614     CYG_REPORT_FUNCARG4( "mqdes=%08x, msg_ptr=%08x, msg_len=%u, msg_prio=%08x",
615                          mqdes, msg_ptr, msg_len, msg_prio );
616     CYG_CHECK_DATA_PTRC( msg_ptr );
617     CYG_CHECK_DATA_PTRC( msg_ptr+msg_len-1 );
618     if ( NULL != msg_prio )
619         CYG_CHECK_DATA_PTRC( msg_prio );
620     
621     
622     struct mquser *user = (struct mquser *)mqdes;
623     struct mqtabent *tabent = user->tabent;
624
625 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
626     if ( user->magic != MQ_VALID_MAGIC ) {
627         errno  = EBADF;
628         CYG_REPORT_RETVAL( -1 );
629         return (ssize_t)-1;
630     }
631 #endif
632     
633     if ( (O_RDONLY != (user->flags & O_RDONLY)) && 
634          (O_RDWR != (user->flags & O_RDWR)) ) {
635         errno = EBADF;
636         CYG_REPORT_RETVAL( -1 );
637         return (ssize_t)-1;
638     }
639
640     if ( msg_len < (size_t)tabent->msgsize ) {
641         errno = EMSGSIZE;
642         CYG_REPORT_RETVAL( -1 );
643         return (ssize_t)-1;
644     }
645
646     // go for it
647     Cyg_Mqueue::qerr_t err;
648     err = tabent->mq->get( msg_ptr, &msg_len, msg_prio,
649                            ((user->flags & O_NONBLOCK) != O_NONBLOCK) );
650     switch (err) {
651
652     case Cyg_Mqueue::INTR:
653         errno = EINTR;
654         CYG_REPORT_RETVAL( -1 );
655         return (ssize_t)-1;
656
657     case Cyg_Mqueue::WOULDBLOCK:
658         CYG_ASSERT( (user->flags & O_NONBLOCK) == O_NONBLOCK,
659                     "Message queue assumed non-blocking when blocking requested"
660             );
661         errno = EAGAIN;
662         CYG_REPORT_RETVAL( -1 );
663         return (ssize_t)-1;
664         
665     case Cyg_Mqueue::OK:
666         CYG_ASSERT( msg_len <= (size_t)tabent->msgsize,
667                     "returned message too long" );
668         if ( NULL != msg_prio )
669             CYG_ASSERT( *msg_prio <= MQ_PRIO_MAX,
670                         "returned message has invalid priority" );
671         CYG_REPORT_RETVAL( msg_len );
672         return (ssize_t)msg_len;
673
674     default:
675         CYG_FAIL( "unhandled message queue return code" );
676         return (ssize_t)-1; // keep compiler happy
677     } // switch
678     
679 } // mq_receive()
680
681
682 //------------------------------------------------------------------------
683 #ifdef CYGFUN_KERNEL_THREADS_TIMER
684 externC int
685 mq_timedsend( mqd_t mqdes, const char *msg_ptr, size_t msg_len,
686               unsigned int msg_prio, const struct timespec *abs_timeout)
687 {
688     CYG_REPORT_FUNCTYPE( "returning %d" );
689     CYG_REPORT_FUNCARG6( "mqdes=%08x, msg_ptr=%08x, msg_len=%u, msg_prio=%u, "
690                          "abs_timeout = %lu, %ld",
691                          mqdes, msg_ptr, msg_len, msg_prio, 
692                          abs_timeout->tv_sec, abs_timeout->tv_nsec);
693     CYG_CHECK_DATA_PTRC( msg_ptr );
694     
695     struct mquser *user = (struct mquser *)mqdes;
696     struct mqtabent *tabent = user->tabent;
697
698 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
699     if ( user->magic != MQ_VALID_MAGIC ) {
700         errno  = EBADF;
701         CYG_REPORT_RETVAL( -1 );
702         return -1;
703     }
704 #endif
705     
706     if ( msg_len > (size_t)tabent->msgsize ) {
707         errno = EMSGSIZE;
708         CYG_REPORT_RETVAL( -1 );
709         return -1;
710     }
711
712     if ( msg_prio > MQ_PRIO_MAX ) {
713         errno = EINVAL;
714         CYG_REPORT_RETVAL( -1 );
715         return -1;
716     }
717
718     if ( (O_WRONLY != (user->flags & O_WRONLY)) && 
719          (O_RDWR != (user->flags & O_RDWR)) ) {
720         errno = EBADF;
721         CYG_REPORT_RETVAL( -1 );
722         return -1;
723     }
724
725     // go for it
726     Cyg_Mqueue::qerr_t err;
727     bool nonblocking = ((user->flags & O_NONBLOCK) == O_NONBLOCK);
728     bool badtimespec = (abs_timeout->tv_nsec < 0) ||
729         (abs_timeout->tv_nsec > 999999999l);
730     cyg_tick_count abs_ticks = cyg_timespec_to_ticks(abs_timeout);
731
732     // We should never time out if there is room in the queue.  Simplest
733     // way to ensure this is to try the non-blocking put() first.
734     err = tabent->mq->put( msg_ptr, msg_len, msg_prio, false, abs_ticks );
735
736     // If the blocking variant would have blocked and that is what's wanted
737     if ( Cyg_Mqueue::WOULDBLOCK == err && !nonblocking && !badtimespec ) {
738         err = tabent->mq->put( msg_ptr, msg_len, msg_prio, true, 
739                                abs_ticks );
740     }
741
742     switch (err) {
743
744     case Cyg_Mqueue::INTR:
745         errno = EINTR;
746         CYG_REPORT_RETVAL( -1 );
747         return -1;
748
749     case Cyg_Mqueue::WOULDBLOCK:
750         if (badtimespec) {
751             errno = EINVAL;
752             CYG_REPORT_RETVAL( -1 );
753             return -1;
754         }
755         CYG_ASSERT( (user->flags & O_NONBLOCK) == O_NONBLOCK,
756                     "Message queue assumed non-blocking when blocking requested"
757             );
758         errno = EAGAIN;
759         CYG_REPORT_RETVAL( -1 );
760         return -1;
761
762     case Cyg_Mqueue::TIMEOUT:
763         errno = ETIMEDOUT;
764         CYG_REPORT_RETVAL( -1 );
765         return -1;
766         
767     case Cyg_Mqueue::OK:
768         CYG_REPORT_RETVAL( 0 );
769         return 0;
770
771     default:
772         CYG_FAIL( "unhandled message queue return code" );
773         return -1; // keep compiler happy
774     } // switch
775 } // mq_timedsend()
776
777 //------------------------------------------------------------------------
778
779
780 externC ssize_t
781 mq_timedreceive( mqd_t mqdes, char *msg_ptr, size_t msg_len,
782             unsigned int *msg_prio, const struct timespec *abs_timeout)
783 {
784     CYG_REPORT_FUNCTYPE( "returning %ld" );
785     CYG_REPORT_FUNCARG6( "mqdes=%08x, msg_ptr=%08x, msg_len=%u, msg_prio=%08x, "
786                          "abs_timeout = %lu, %ld",
787                          mqdes, msg_ptr, msg_len, msg_prio,
788                          abs_timeout->tv_sec, abs_timeout->tv_nsec );
789     CYG_CHECK_DATA_PTRC( msg_ptr );
790     CYG_CHECK_DATA_PTRC( msg_ptr+msg_len-1 );
791     if ( NULL != msg_prio )
792         CYG_CHECK_DATA_PTRC( msg_prio );
793     
794     
795     struct mquser *user = (struct mquser *)mqdes;
796     struct mqtabent *tabent = user->tabent;
797
798 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
799     if ( user->magic != MQ_VALID_MAGIC ) {
800         errno  = EBADF;
801         CYG_REPORT_RETVAL( -1 );
802         return (ssize_t)-1;
803     }
804 #endif
805     
806     if ( (O_RDONLY != (user->flags & O_RDONLY)) && 
807          (O_RDWR != (user->flags & O_RDWR)) ) {
808         errno = EBADF;
809         CYG_REPORT_RETVAL( -1 );
810         return (ssize_t)-1;
811     }
812
813     if ( msg_len < (size_t)tabent->msgsize ) {
814         errno = EMSGSIZE;
815         CYG_REPORT_RETVAL( -1 );
816         return (ssize_t)-1;
817     }
818
819     // go for it
820     Cyg_Mqueue::qerr_t err;
821     bool nonblocking = ((user->flags & O_NONBLOCK) == O_NONBLOCK);
822     bool badtimespec = (abs_timeout->tv_nsec < 0) ||
823         (abs_timeout->tv_nsec > 999999999l);
824     cyg_tick_count abs_ticks = cyg_timespec_to_ticks(abs_timeout);
825
826     // We should never time out if there is something to read.  Simplest
827     // way to ensure this is to try the non-blocking get() first.
828     err = tabent->mq->get( msg_ptr, &msg_len, msg_prio, false, abs_ticks );
829
830     // If the blocking variant would have blocked and that is what's wanted
831     if ( Cyg_Mqueue::WOULDBLOCK == err && !nonblocking && !badtimespec ) {
832         err = tabent->mq->get( msg_ptr, &msg_len, msg_prio, true, abs_ticks );
833     }
834
835     switch (err) {
836
837     case Cyg_Mqueue::INTR:
838         errno = EINTR;
839         CYG_REPORT_RETVAL( -1 );
840         return (ssize_t)-1;
841
842     case Cyg_Mqueue::WOULDBLOCK:
843         if (badtimespec) {
844             errno = EINVAL;
845             CYG_REPORT_RETVAL( -1 );
846             return -1;
847         }
848         CYG_ASSERT( (user->flags & O_NONBLOCK) == O_NONBLOCK,
849                     "Message queue assumed non-blocking when blocking requested"
850             );
851         errno = EAGAIN;
852         CYG_REPORT_RETVAL( -1 );
853         return (ssize_t)-1;
854
855     case Cyg_Mqueue::TIMEOUT:
856         errno = ETIMEDOUT;
857         CYG_REPORT_RETVAL( -1 );
858         return -1;
859         
860     case Cyg_Mqueue::OK:
861         CYG_ASSERT( msg_len <= (size_t)tabent->msgsize,
862                     "returned message too long" );
863         if ( NULL != msg_prio )
864             CYG_ASSERT( *msg_prio <= MQ_PRIO_MAX,
865                         "returned message has invalid priority" );
866         CYG_REPORT_RETVAL( msg_len );
867         return (ssize_t)msg_len;
868
869     default:
870         CYG_FAIL( "unhandled message queue return code" );
871         return (ssize_t)-1; // keep compiler happy
872     } // switch
873     
874 } // mq_timedreceive()
875
876 //------------------------------------------------------------------------
877 #endif
878
879 #ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
880
881 externC int
882 mq_notify( mqd_t mqdes, const struct sigevent *notification )
883 {
884     CYG_REPORT_FUNCTYPE( "returning %d" );
885     CYG_REPORT_FUNCARG2( "mqdes=%08x, notification=%08x", mqdes, notification );
886     if ( NULL != notification )
887         CYG_CHECK_DATA_PTRC( notification );
888     
889     struct mquser *user = (struct mquser *)mqdes;
890     struct mqtabent *tabent = user->tabent;
891
892 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
893     if ( user->magic != MQ_VALID_MAGIC ) {
894         errno  = EBADF;
895         CYG_REPORT_RETVAL( -1 );
896         return -1;
897     }
898 #endif
899     
900     // lock scheduler since we test and set non-atomically
901     Cyg_Scheduler::lock();
902     
903     // we are being told to clear the notification function
904     if ( NULL == notification ) {
905         tabent->mq->setnotify( NULL, 0 );
906         tabent->sigev = NULL;
907         Cyg_Scheduler::unlock();
908         CYG_REPORT_RETVAL( 0 );
909         return 0;
910     } // if
911     
912     if ( NULL != tabent->sigev ) {  // already registered
913         Cyg_Scheduler::unlock();
914         errno = EBUSY;
915         CYG_REPORT_RETVAL( -1 );
916         return -1;
917     } // if
918
919     tabent->sigev = notification;
920     user->notifieruser = true; // Used for deciding about whether to
921                                // deregister in mq_close()
922     tabent->mq->setnotify( &notifyme, (CYG_ADDRWORD) user );
923     Cyg_Scheduler::unlock();
924     
925     CYG_REPORT_RETVAL( 0 );
926     return 0;
927 } // mq_notify()
928
929 #endif // ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
930
931 //------------------------------------------------------------------------
932
933 externC int
934 mq_setattr( mqd_t mqdes, const struct mq_attr *mqstat,
935             struct mq_attr *omqstat )
936 {
937     CYG_REPORT_FUNCTYPE( "returning %d" );
938     CYG_REPORT_FUNCARG3( "mqdes=%08x, mqstat=%08x, omqstat=%08x",
939                          mqdes, mqstat, omqstat );
940     CYG_CHECK_DATA_PTRC( mqstat );
941
942     struct mquser *user = (struct mquser *)mqdes;
943
944 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
945     if ( user->magic != MQ_VALID_MAGIC ) {
946         errno  = EBADF;
947         CYG_REPORT_RETVAL( -1 );
948         return -1;
949     }
950 #endif
951     
952     if ( NULL != omqstat ) {
953         CYG_CHECK_DATA_PTRC( omqstat );
954         mq_getattr( mqdes, omqstat );
955     } // if
956
957     // Two-stage update, so lock sched since it's quick
958     Cyg_Scheduler::lock();
959     user->flags &= ~O_NONBLOCK;  // clear
960     if ( (mqstat->mq_flags & O_NONBLOCK) == O_NONBLOCK ) {
961         user->flags |= O_NONBLOCK;
962     } // if
963     Cyg_Scheduler::unlock();
964
965     CYG_REPORT_RETVAL( 0 );
966     return 0;
967 } // mq_setattr()
968
969 //------------------------------------------------------------------------
970
971 externC int
972 mq_getattr( mqd_t mqdes, struct mq_attr *mqstat )
973 {
974     CYG_REPORT_FUNCTYPE( "returning %d" );
975     CYG_REPORT_FUNCARG2( "mqdes=%08x, mqstat=%08x", mqdes, mqstat );
976     CYG_CHECK_DATA_PTRC( mqstat );
977
978     struct mquser *user = (struct mquser *)mqdes;
979     struct mqtabent *tabent = user->tabent;
980
981 #ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
982     if ( user->magic != MQ_VALID_MAGIC ) {
983         errno  = EBADF;
984         CYG_REPORT_RETVAL( -1 );
985         return -1;
986     }
987 #endif
988     
989     mqstat->mq_flags   = user->flags;
990     mqstat->mq_maxmsg  = tabent->maxmsg;
991     mqstat->mq_msgsize = tabent->msgsize;
992     mqstat->mq_curmsgs = tabent->mq->count();    
993     
994     CYG_REPORT_RETVAL( 0 );
995     return 0;
996 } // mq_getattr()
997
998
999 //------------------------------------------------------------------------
1000
1001 /* EOF mqueue.cxx */