/*
 * Interrupt handling in Real-Time. Play sounds with PC speaker.
 *
 * (C) Michael Barabanov, 1997
 *  (C) FSMLabs  1999. baraban@fsmlabs.com
 *  Released under the GNU GENERAL PUBLIC LICENSE Version 2, June 1991
 *  Any use of this code must include this notice.
 *
 */

#include <linux/mc146818rtc.h>

#include <pthread.h>
#include <rtl_fifo.h>
#include <rtl_core.h>
#include <rtl_time.h>
#include <rtl.h>

#define FIFO_NO 3

static int filter(int x)
{
	static int oldx;
	int ret;

	if (x & 0x80) {
		x = 382 - x;
	}
	ret = x > oldx;
	oldx = x;
	return ret;
	
}

pthread_t thread;

void *sound_thread(void *param)
{
	char data;
	char temp;
	rtl_stop_interrupts();
	while (1) {
		pthread_suspend_np(pthread_self());
		if (rtf_get(FIFO_NO, &data, 1) > 0) {
			data = filter(data);
	/* 		if (data) conpr("1"); else conpr("0"); */
			temp = inb(0x61);            
			temp &= 0xfc;
			if (data) {
				temp |= 3;
			}
			outb(temp,0x61);
		}
	}

}

unsigned int intr_handler(unsigned int irq, struct pt_regs *regs) {
	pthread_wakeup_np (thread);
 	(void) CMOS_READ(RTC_REG_C);        /* clear IRQ */ 
	rtl_hard_enable_irq (8);
	return 0;
}

char save_cmos_A;
char save_cmos_B;


int init_module(void)
{
	char ctemp;
	pthread_create (&thread,  NULL, sound_thread, NULL);
	if (!I8253_channel2_free()) {
		conpr("RTLinux sound: can't use channel 2; bailing out\n");
		return -1;
	}
	rtf_create(FIFO_NO, 4000);

	/* this is just to ensure that the output of the counter is 1 */
	outb_p(0xb0, 0x43);	/* binary, mode 0, LSB/MSB, ch 2 */
	outb_p(0x1, 0x42);
	outb_p(0x0, 0x42);

	rtl_request_irq (8, intr_handler);

	/* program the RTC to interrupt at 8192 Hz */
	save_cmos_A = CMOS_READ(RTC_REG_A);
	save_cmos_B = CMOS_READ(RTC_REG_B);

	CMOS_WRITE(0x23, RTC_REG_A);  	/* 32kHz Time Base, 8192 Hz interrupt frequency */
	ctemp = CMOS_READ(RTC_REG_B);
	ctemp &= 0x8f;                	/* Clear */
	ctemp |= 0x40;                	/* Periodic interrupt enable */
	CMOS_WRITE(ctemp, RTC_REG_B); 

	rtl_hard_enable_irq (8);
	(void) CMOS_READ(RTC_REG_C);

	return 0;
} 


void cleanup_module(void)
{
	rtf_destroy(FIFO_NO);
	outb_p(0xb6, 0x43);	/* restore the original mode */
	CMOS_WRITE(save_cmos_A, RTC_REG_A);
	CMOS_WRITE(save_cmos_B, RTC_REG_B);
	rtl_free_irq(8);
	pthread_cancel (thread);
	pthread_join (thread, NULL);
}

