/*========================================================================
//
//      mqueue2.c
//
//      POSIX Message queues tests - mq_notify
//
//========================================================================
//####COPYRIGHTBEGIN####
//                                                                          
// -------------------------------------------                              
// The contents of this file are subject to the Red Hat eCos Public License 
// Version 1.1 (the "License"); you may not use this file except in         
// compliance with the License.  You may obtain a copy of the License at    
// http://www.redhat.com/                                                   
//                                                                          
// Software distributed under the License is distributed on an "AS IS"      
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
// License for the specific language governing rights and limitations under 
// the License.                                                             
//                                                                          
// The Original Code is eCos - Embedded Configurable Operating System,      
// released September 30, 1998.                                             
//                                                                          
// The Initial Developer of the Original Code is Red Hat.                   
// Portions created by Red Hat are                                          
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
// All Rights Reserved.                                                     
// -------------------------------------------                              
//                                                                          
//####COPYRIGHTEND####
//========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     jlarmour
// Contributors:  
// Date:          2000-05-18
// Purpose:       This file provides tests for POSIX mqueue mq_notify
// Description:   
// Usage:         
//
//####DESCRIPTIONEND####
//
//======================================================================
*/

/* CONFIGURATION */

#include <pkgconf/posix.h>

#ifndef CYGPKG_POSIX_MQUEUES
# define NA_MSG "Message queues not configured"
#elif !defined(CYGPKG_POSIX_SIGNALS)
# define NA_MSG "No POSIX signals configured"
#endif

#ifdef NA_MSG
#include <cyg/infra/testcase.h>      // test API
void
cyg_user_start(void)
{
    CYG_TEST_INIT();
    CYG_TEST_NA( NA_MSG );
}

#else

/* INCLUDES */

#include <fcntl.h>                   // O_*
#include <errno.h>                   // errno
#include <sys/stat.h>                // file modes
#include <mqueue.h>                  // Mqueue Header
#include <cyg/infra/testcase.h>      // test API
#include <signal.h>                  // signals

/* GLOBALS */
sig_atomic_t signals=0;
char buf[20];
unsigned int prio;


/* FUNCTIONS */

static int
my_memcmp(const void *m1, const void *m2, size_t n)
{
    char *s1 = (char *)m1;
    char *s2 = (char *)m2;

    while (n--) {
        if (*s1 != *s2)
            return *s1 - *s2;
        s1++;
        s2++;
    }
    return 0;
} // my_memcmp()

static char *
my_strcpy(char *s1, const char *s2)
{
    char *s = s1;
    while (*s2) {
        *s1++ = *s2++;
    }
    return s;
} // my_strcpy()

static size_t
my_strlen(const char *s)
{
    const char *start = s;
    while (*s)
        s++;
    return (s - start);
} // my_strcpy()



//************************************************************************

static void
sigusr1_handler( int signo, siginfo_t *info, void *context )
{
    ssize_t recvlen;
    char mybuf[20];
    unsigned int myprio;
    mqd_t *q = (mqd_t *)info->si_value.sival_ptr;

    CYG_TEST_PASS_FAIL( SIGUSR1 == signo, "correct signal number #1" );
    CYG_TEST_PASS_FAIL( SIGUSR1 == info->si_signo, "correct signal number #2" );
    CYG_TEST_PASS_FAIL( SI_MESGQ == info->si_code, "correct signal code" );

    signals++;

    // retrieve message and compare with buf
    recvlen = mq_receive( *q, mybuf, sizeof(mybuf), &myprio );
    CYG_TEST_PASS_FAIL( recvlen == my_strlen(buf),
                        "receive message length" );
    CYG_TEST_PASS_FAIL( 0 == my_memcmp( buf, mybuf, my_strlen(buf)),
                        "received message data intact" );
    CYG_TEST_PASS_FAIL( prio == myprio,
                        "received at correct priority" );
}

//************************************************************************

int
main(void)
{
    mqd_t q1;
    struct mq_attr attr;
    mode_t mode;
    int err;
    ssize_t recvlen;
    char mybuf[20];
    unsigned int myprio;
    struct sigevent ev;
    struct sigaction act;

    CYG_TEST_INIT();
    CYG_TEST_INFO( "Starting POSIX message test 2" );

#if 0
    if ( 0 != pthread_create( &thr, NULL, &thread, NULL ) ) {
        CYG_TEST_FAIL_FINISH( "Couldn't create a helper thread" );
    }
#endif

    attr.mq_flags = 0;
    attr.mq_maxmsg = 4;
    attr.mq_msgsize = 20;
    mode = S_IRWXU|S_IRWXG|S_IRWXO; // rwx for all

    q1 = mq_open( "/mq1", O_CREAT|O_NONBLOCK|O_RDWR, mode, &attr );
    CYG_TEST_PASS_FAIL( q1 != (mqd_t)-1, "simple mq_open (write only)" );
    
    err = mq_getattr( q1, &attr );
    CYG_TEST_PASS_FAIL( 0 == err, "simple mq_getattr" );
    CYG_TEST_PASS_FAIL( (4 == attr.mq_maxmsg) &&
                        (20 == attr.mq_msgsize) &&
                        (O_NONBLOCK == (attr.mq_flags & O_NONBLOCK)) &&
                        (O_RDWR == (attr.mq_flags & O_RDWR)) &&
                        (0 == attr.mq_curmsgs ), "getattr attributes correct" );


    act.sa_sigaction = &sigusr1_handler;
    sigfillset( &act.sa_mask ); // enable all signals
    act.sa_flags = SA_SIGINFO;
    
    if ( 0 != sigaction( SIGUSR1, &act, NULL ) ) {
        CYG_TEST_FAIL_FINISH( "Couldn't register signal handler" );
    }

    ev.sigev_notify = SIGEV_SIGNAL;
    ev.sigev_signo = SIGUSR1;
    ev.sigev_value.sival_ptr = (void *)&q1;

    err = mq_notify( q1, &ev );
    CYG_TEST_PASS_FAIL( 0 == err, "simple mq_notify" );

    my_strcpy( buf, "Vik is the best" );
    prio = 7;
    err = mq_send( q1, buf, my_strlen(buf), prio );

    CYG_TEST_PASS_FAIL( 0 == err, "mq_send #1" );

    CYG_TEST_PASS_FAIL( 1 == signals, "got notification" );

    my_strcpy( buf, "Scrummy Vik" );
    prio = 6;
    err = mq_send( q1, buf, my_strlen(buf), prio );
    CYG_TEST_PASS_FAIL( 0 == err, "mq_send #2" );

    CYG_TEST_PASS_FAIL( 1 == signals, "correctly didn't get notification" );

    recvlen = mq_receive( q1, mybuf, sizeof(mybuf), &myprio );
    CYG_TEST_PASS_FAIL( recvlen == my_strlen(buf),
                        "receive message length" );
    CYG_TEST_PASS_FAIL( 0 == my_memcmp( buf, mybuf, my_strlen(buf)),
                        "received message data intact" );
    CYG_TEST_PASS_FAIL( prio == myprio,
                        "received at correct priority" );

    err = mq_notify( q1, &ev );
    CYG_TEST_PASS_FAIL( 0 == err, "mq_notify #2" );

    err = mq_notify( q1, &ev );
    CYG_TEST_PASS_FAIL( -1 == err, "second mq_notify returns error" );
    CYG_TEST_PASS_FAIL( EBUSY == errno,
                        "errno correct for second mq_notify error" );

    err = mq_notify( q1, NULL );
    CYG_TEST_PASS_FAIL( 0 == err, "clear notification" );

    my_strcpy( buf, "Vik is k3wl" );
    prio = 8;
    err = mq_send( q1, buf, my_strlen(buf), prio );

    CYG_TEST_PASS_FAIL( 0 == err, "mq_send #2" );

    CYG_TEST_PASS_FAIL( 1 == signals, "correctly didn't get notification #2" );

    recvlen = mq_receive( q1, mybuf, sizeof(mybuf), &myprio );
    CYG_TEST_PASS_FAIL( recvlen == my_strlen(buf),
                        "receive message length" );
    CYG_TEST_PASS_FAIL( 0 == my_memcmp( buf, mybuf, my_strlen(buf)),
                        "received message data intact" );
    CYG_TEST_PASS_FAIL( prio == myprio,
                        "received at correct priority" );
    
    err = mq_close( q1 );
    CYG_TEST_PASS_FAIL( 0 == err, "mq_close" );

    err = mq_unlink( "/mq1" );
    CYG_TEST_PASS_FAIL( 0 == err, "mq_unlink" );

    CYG_TEST_EXIT("POSIX message test 2");

    return 0;
} // main()

//------------------------------------------------------------------------

#endif

/* EOF mqueue2.c */
