Logo Search packages:      
Sourcecode: linux version File versions  Download package

miropcm20-rds-core.c

/*
 *  Many thanks to Fred Seidel <seidel@metabox.de>, the
 *  designer of the RDS decoder hardware. With his help
 *  I was able to code this driver.
 *  Thanks also to Norberto Pellicci, Dominic Mounteney
 *  <DMounteney@pinnaclesys.com> and www.teleauskunft.de
 *  for good hints on finding Fred. It was somewhat hard
 *  to locate him here in Germany... [:
 *
 * Revision history:
 *
 *   2000-08-09  Robert Siemer <Robert.Siemer@gmx.de>
 *        RDS support for MiroSound PCM20 radio
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>

#include <asm/io.h>
#include "oss/aci.h"
#include "miropcm20-rds-core.h"

#define DEBUG 0

static struct mutex aci_rds_mutex;

#define RDS_DATASHIFT          2   /* Bit 2 */
#define RDS_DATAMASK        (1 << RDS_DATASHIFT)
#define RDS_BUSYMASK        0x10   /* Bit 4 */
#define RDS_CLOCKMASK       0x08   /* Bit 3 */

#define RDS_DATA(x)         (((x) >> RDS_DATASHIFT) & 1)


#if DEBUG
static void print_matrix(char array[], unsigned int length)
{
      int i, j;

      for (i=0; i<length; i++) {
            printk(KERN_DEBUG "aci-rds: ");
            for (j=7; j>=0; j--) {
                  printk("%d", (array[i] >> j) & 0x1);
            }
            if (i%8 == 0)
                  printk(" byte-border\n");
            else
                  printk("\n");
      }
}
#endif /* DEBUG */

static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size)
{
      int i;

      if (size != 8)
            return -1;
      for (i = 7; i >= 0; i--)
            sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
      sendbuffer[0] |= RDS_CLOCKMASK;

      return 0;
}

static int rds_waitread(void)
{
      unsigned char byte;
      int i=2000;

      do {
            byte=inb(RDS_REGISTER);
            i--;
      }
      while ((byte & RDS_BUSYMASK) && i);

      if (i) {
            #if DEBUG
            printk(KERN_DEBUG "rds_waitread()");
            print_matrix(&byte, 1);
            #endif
            return (byte);
      } else {
            printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n");
            return -1;
      }
}

/* don't use any ..._nowait() function if you are not sure what you do... */

static inline void rds_rawwrite_nowait(unsigned char byte)
{
      #if DEBUG
      printk(KERN_DEBUG "rds_rawwrite()");
      print_matrix(&byte, 1);
      #endif
      outb(byte, RDS_REGISTER);
}

static int rds_rawwrite(unsigned char byte)
{
      if (rds_waitread() >= 0) {
            rds_rawwrite_nowait(byte);
            return 0;
      } else
            return -1;
}

static int rds_write(unsigned char cmd)
{
      unsigned char sendbuffer[8];
      int i;

      if (byte2trans(cmd, sendbuffer, 8) != 0){
            return -1;
      } else {
            for (i=0; i<8; i++) {
                  rds_rawwrite(sendbuffer[i]);
            }
      }
      return 0;
}

static int rds_readcycle_nowait(void)
{
      rds_rawwrite_nowait(0);
      return rds_waitread();
}

static int rds_readcycle(void)
{
      if (rds_rawwrite(0) < 0)
            return -1;
      return rds_waitread();
}

static int rds_read(unsigned char databuffer[], int datasize)
{
      #define READSIZE (8*datasize)

      int i,j;

      if (datasize < 1)  /* nothing to read */
            return 0;

      /* to be able to use rds_readcycle_nowait()
         I have to waitread() here */
      if (rds_waitread() < 0)
            return -1;

      memset(databuffer, 0, datasize);

      for (i=0; i< READSIZE; i++)
            if((j=rds_readcycle_nowait()) < 0) {
                  return -1;
            } else {
                  databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8)));
            }

      return 0;
}

static int rds_ack(void)
{
      int i=rds_readcycle();

      if (i < 0)
            return -1;
      if (i & RDS_DATAMASK) {
            return 0;  /* ACK  */
      } else {
            printk(KERN_DEBUG "aci-rds: NACK\n");
            return 1;  /* NACK */
      }
}

int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize)
{
      int ret;

      if (mutex_lock_interruptible(&aci_rds_mutex))
            return -EINTR;

      rds_write(cmd);

      /* RDS_RESET doesn't need further processing */
      if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize)))
            ret = -1;
      else
            ret = 0;

      mutex_unlock(&aci_rds_mutex);

      return ret;
}
EXPORT_SYMBOL(aci_rds_cmd);

int __init attach_aci_rds(void)
{
      mutex_init(&aci_rds_mutex);
      return 0;
}

void __exit unload_aci_rds(void)
{
}
MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index