/* xcard.c 
 * io code for the xradiotrackprogram 
 * courtesy Gideon le Grange 
 *
 * this is the code that does the actual talking to the card 
 *
 * July 29th 1995
 *
 * With thanks to Gideon, I've used lots of his code for my
 * xradiotrack program. See the README file for complete
 * acknowledgements which are richly deserved.
 *
 * V.1.1 March '96
 *
 * Added stuff for tuning indicator and muting
 * Some simplification of code by just doing byte I/O. No need
 * to use an offset from the card's base I/O port, It seems they're
 * all the same, which probably saved a few transistors in the
 * hardware.
 *
 *          Frans Brinkman
 */

#include <asm/io.h>
#include <unistd.h>

extern int muted;

unsigned long RadioBase; /* base port for RadioTrack */

void CardOff(void)
{
   /* switch card off */

   outb(0,RadioBase);
   outb(0,RadioBase);
}

int CardMute(void)
{
  /* return 1 if now muted, 0 if muting now off */

  if ( ! muted )              /* case we weren't muted */
   {
   /* make it shut up */
   outb(0,RadioBase);
   outb(0,RadioBase);
   return(1);
   }
  else
   {
   /* switch sound back on */
   outb(0,RadioBase);
   outb(0xc8,RadioBase);
   return(0);
   }

}

void VolumeDown(void)
{
  if ( ! muted )
   {
   /* vol down a notch */
   outb(0x48,RadioBase);
   usleep(10000);
   outb(0xc8,RadioBase);
   }
  else
   {
   /* just un-mute */
   outb(0,RadioBase);
   outb(0xc8,RadioBase);
   }
}

void VolumeUp(void)
{
  if ( ! muted )
   {
   /* vol up a notch */
   outb(0x88,RadioBase);
   usleep(10000);
   outb(0xc8,RadioBase);
   }
  else
   {
   /* just un-mute */
   outb(0,RadioBase);
   outb(0xc8,RadioBase);
   }
}

void Tune(float freq)
{
   /* tunes the radio to the desired frequency */
   unsigned long int bits;
   int i,mask;
   
   bits=(freq*40)+10486188;
   /* yes, i know. now where does THAT come from? 
    * working out that bits=(freq*multiplier)+base 
    * was hard, and working out the base and multiplier
    * was tricky. */
   mask=1;
   for (i=0; i<24; ++i)
     {
	if ((bits&mask)==0)
	  {
	     /* 0 bit */
             outb(0x1,RadioBase);
             outb(0x1,RadioBase);
             outb(0x3,RadioBase);
             outb(0x3,RadioBase);
	  }
	else
	  {
	     /* bit is 1 */
             outb(0x5,RadioBase);
             outb(0x5,RadioBase);
             outb(0x7,RadioBase);
             outb(0x7,RadioBase);
	  }
	mask=mask*2;
     }
   /* termination sequence */
   outb(0x0,RadioBase);            /* keeps current volume setting */
   usleep(10000);
   outb(0xc8,RadioBase);
}

int IsTuned(void)
{
int x;

/* test tuning */
/*
	DO NOT CHANGE THE TIMING !?!?!?

	Scanning seems to be pretty unreliable, or at least using
	the one card I've got. A shorter wait will miss lots of valid
	stations, longer ones will make it find lots of noisy signals.
        Should anyone ever find out how it is supposed to work please
	let me know. For absolutely no reason that I can see it also seems
        to work slightly better if the frequencies used are multiples
        of .05, but that might just be coincidence
*/
      outb(0xf8,RadioBase);
      usleep(200000);              /* any faster and it won't work? */
      x = inb(RadioBase);
      usleep(10000);
      outb(0xe8,RadioBase);

      /* if x == ff not tuned, if x == fd then tuned */

      if (( x & 255) != 0x0ff) return(1);
      else return(0);
}
