/*
Produces a beep on the Crusoe NetWinder
*/

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/time.h>

#define stop_count()    outb(inb(0x61) & ~0x01, 0x61)
#define start_count()   outb(inb(0x61) | 0x01, 0x61)
#define set_freq(freq) \
        do {                                                            \
                unsigned count = 1191380 / freq;                        \
                outb(0xb6, 0x43);                                       \
                outb(count & 0xff, 0x42);                               \
                outb(count >> 8, 0x42);                                 \
        } while (0)
#define sound_on()      outb(inb(0x61) | 0x02, 0x61)
#define sound_off()     outb(inb(0x61) & ~0x02, 0x61)

int alarm_handler_called = 0;

void alarm_handler(int arg)
{
        alarm_handler_called = 1;
}

int main(int argc, char *argv[])
{
        int retval;
        struct itimerval itime;

        /* Make sure two args (frequency in Hz, duration in ms) */
        if (argc < 3)
                return 1;

        /* Allow I/O to timer */
        retval = ioperm(0x40, 4, 0x44);
        if (retval) {
                perror("ioperm (0x40, 4, 0x44");
                return 2;
        }

        /* Allow I/O to sound control */
        retval = ioperm(0x61, 1, 0x62);
        if (retval) {
                perror("ioperm (0x61, 1, 0x61");
                return 2;
        }

        sound_off();
        stop_count();
        set_freq(atoi(argv[1]));
        start_count();

        signal(SIGALRM, &alarm_handler);

        /* Set the duration timer */
        itime.it_interval.tv_sec = 0;
        itime.it_interval.tv_usec = 0;
        itime.it_value.tv_sec = atoi(argv[2]) / 1000;
        itime.it_value.tv_usec = atoi(argv[2]) % 1000 * 1000;
        retval = setitimer(ITIMER_REAL, &itime, NULL);
        if (retval) {
                perror("setitimer");
                return 3;
        }

        /* Play the sound */
        sound_on();
        pause();
        sound_off();

        return (alarm_handler_called) ? 0: 4;
}


