/* * Many thanks to Fred Seidel , the * designer of the RDS decoder hardware. With his help * I was able to code this driver. * Thanks also to Norberto Pellicci, Dominic Mounteney * 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 * RDS support for MiroSound PCM20 radio */ #include #include #include #include #include #include #include #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=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");