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