
/*
 *  intwait.c
 *
 *  This module demonstrates a resource manager that contains one
 *  thread for handling interrupts and one thread for resource
 *  manager things.
 *
 *  To test is, simply run it.  It handles the timer interrupt.
 *
 *    intwait -v &
 *
*/

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>
#include <sys/resmgr.h>
#include <sys/syspage.h>

void *interrupt_thread (void *data);
void options (int argc, char **argv);

resmgr_connect_funcs_t  connect_funcs;
resmgr_io_funcs_t       io_funcs;
dispatch_t              *dpp;
resmgr_attr_t           rattr;
dispatch_context_t      *ctp;
iofunc_attr_t           ioattr;

char            *progname = "intwait";
int             verbose;        // -v for verbose operation
struct sigevent int_event;      // the SIGEV_INTR event the ISR will return

int main (int argc, char **argv)
{
    printf ("%s:  starting...\n", progname);

    options (argc, argv);

    dpp = dispatch_create ();
    memset (&rattr, 0, sizeof (rattr)); /* using the defaults for rattr */
    iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
                      _RESMGR_IO_NFUNCS, &io_funcs);
    iofunc_attr_init (&ioattr, S_IFCHR | 0666, NULL, NULL);
    resmgr_attach (dpp, &rattr, "/dev/intwait", _FTYPE_ANY, 0,
                     &connect_funcs, &io_funcs, &ioattr);
    
    ctp = dispatch_context_alloc (dpp);

    pthread_create (NULL, NULL, interrupt_thread, NULL);
    
    while (1) {
        if ((ctp = dispatch_block (ctp)) == NULL) {
            fprintf (stderr, "%s:  dispatch_block failed: %s\n",
                     progname, strerror (errno));
            exit (1);
        }
        dispatch_handler (ctp);
    }
    return(0);
}

const struct sigevent *
interrupt_handler(void *area, int id)
{
    static int  counter = 0;
    
    // wake up every 100'th interrupt
    if (++counter == 100) {
        counter = 0;
        return &int_event;
    } else {
        return NULL;
    }
}

void *
interrupt_thread (void *data)
{
//  int                 coid;
    struct _clockperiod period;
    
    // display the timer interrupt granularity for testing purposes
    ClockPeriod(CLOCK_REALTIME, NULL, &period, 0);
    printf("Timer is set to %u nsecs\n", (unsigned int)period.nsec);
        
    ThreadCtl(_NTO_TCTL_IO, 0);         // Request I/O privity

    // set up a SIGEV_INTR event structure to for the ISR to return, the ISR
    // will deliver this event after every 100th interrupt.  When
    // it does, InterruptWait() will unblock.

    // this macro fills in the event structure with SIGEV_INTR
    SIGEV_INTR_INIT(&int_event);

    if (InterruptAttach(SYSPAGE_ENTRY(qtime)->intr, interrupt_handler, NULL, 0, _NTO_INTR_FLAGS_TRK_MSK) == -1) {
        fprintf (stderr, "%s:  InterruptAttach failed: %s\n",
                 progname, strerror (errno));
        exit(EXIT_FAILURE);
    }

    while (1) {
        InterruptWait (NULL, NULL);

        printf ("%s:  we unblocked from InterruptWait()\n", progname);
    }   
}

/*
 *  options
 *
 *  This routine handles the command line options:
 *      -v          verbose operation
*/

void
options (int argc, char **argv)
{
    int     opt;
    int     i;

    verbose = 0;

    i = 0;
    while ((opt = getopt (argc, argv, "v")) != -1) {
        switch (opt) {
        case 'v':
            verbose = 1;
            break;
        }
    }   
}
