Messages in this thread Patch in this message | | | Date | Sat, 1 May 1999 00:11:00 -0400 (EDT) | From | "Frederick F. Gleason" <> | Subject | [patch] Revised ADS Cadet Radio Driver |
| |
Here's the latest revision for the ADS Cadet AM/FM radio driver. Changes in this version:
1) Broken compile under 2.2.7 fixed 2) Full Radio Data System (RDS) support added 3) Signal strength function now fully implemented 4) RDS extensions documented in Documentation/video4linux/API.html
|Frederick F. Gleason, Jr.|WAVA Radio - 105 FM |Voice: 1-(703)-807-2266 | | Chief Engineer |1901 N. Moore Street| FAX: 1-(703)-807-2248 | | |Arlington, VA 22209 | Web: HTTP://www.wava.com|
-- patch starts --
diff -u --recursive --new-file linux-2.2.7/Documentation/video4linux/API.html linux/Documentation/video4linux/API.html --- linux-2.2.7/Documentation/video4linux/API.html Thu Jan 7 11:41:55 1999 +++ linux/Documentation/video4linux/API.html Fri Apr 30 23:40:19 1999 @@ -1,6 +1,9 @@ <HTML><HEAD> -<TITLE>Video4Linux Kernel API Reference v0.1:19980516</TITLE> +<TITLE>Video4Linux Kernel API Reference v0.1:19990430</TITLE> </HEAD> +<! Revision History: > +<! 4/30/1999 - Fred Gleason (fredg@wava.com)> +<! Documented extensions for the Radio Data System (RDS) extensions > <BODY bgcolor="#ffffff"> <H3>Devices</H3> Video4Linux provides the following sets of device files. These live on the @@ -249,6 +252,8 @@ <TR><TD><b>VIDEO_TUNER_LOW</b><TD>Frequency is in a lower range</TD> <TR><TD><b>VIDEO_TUNER_NORM</b><TD>The norm for this tuner is settable</TD> <TR><TD><b>VIDEO_TUNER_STEREO_ON</b><TD>The tuner is seeing stereo audio</TD> +<TR><TD><b>VIDEO_TUNER_RDS_ON</b><TD>The tuner is seeing a RDS datastream</TD> +<TR><TD><b>VIDEO_TUNER_MBS_ON</b><TD>The tuner is seeing a MBS datastream</TD> </TABLE> <P> The following modes are defined @@ -349,6 +354,21 @@ <TR><TD><b>teletext</b><TD>Teletext device</TD> </TABLE> <P> - +<H3>RDS Datastreams</H3> +For radio devices that support it, it is possible to receive Radio Data +System (RDS) data by means of a read() on the device. The data is packed in +groups of three, as follows: +<TABLE> +<TR><TD>First Octet</TD><TD>Least Siginificant Byte of RDS Block</TD></TR> +<TR><TD>Second Octet</TD><TD>Most Siginificant Byte of RDS Block +<TR><TD>Third Octet</TD><TD>Bit 7:</TD><TD>Error bit. Indicates that +an uncorrectable error occured during reception of this block.</TD></TR> +<TR><TD> </TD><TD>Bit 6:</TD><TD>Corrected bit. Indicates that +an error was corrected for this data block.</TD></TR> +<TR><TD> </TD><TD>Bits 5-3:</TD><TD>Reeived Offset. Indicates the +offset received by the sync system.</TD></TR> +<TR><TD> </TD><TD>Bits 2-0:</TD><TD>Offset Name. Indicates the +offset applied to this data.</TD></TR> +</TABLE> </BODY> </HTML> diff -u --recursive --new-file linux-2.2.7/drivers/char/radio-cadet.c linux/drivers/char/radio-cadet.c --- linux-2.2.7/drivers/char/radio-cadet.c Mon Apr 19 20:44:43 1999 +++ linux/drivers/char/radio-cadet.c Fri Apr 30 23:07:34 1999 @@ -1,7 +1,7 @@ /* cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card * * by Fred Gleason <fredg@wava.com> - * Version 0.1.2 + * Version 0.3.1 * * (Loosely) based on code for the Aztech radio card by * @@ -22,42 +22,110 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev.h> /* kernel radio structs */ #include <linux/config.h> /* CONFIG_RADIO_CADET_PORT */ +#include <linux/param.h> #ifndef CONFIG_RADIO_CADET_PORT #define CONFIG_RADIO_CADET_PORT 0x330 #endif +#define RDS_BUFFER 256 static int io=CONFIG_RADIO_CADET_PORT; static int users=0; static int curtuner=0; +static int tunestat=0; +static int sigstrength=0; +struct wait_queue *tunerq,*rdsq,*readq; +struct timer_list tunertimer,rdstimer,readtimer; +static __u8 rdsin=0,rdsout=0,rdsstat=0; +static unsigned char rdsbuf[RDS_BUFFER]; +static int cadet_lock=0; + +/* + * Signal Strength Threshold Values + * The V4L API spec does not define any particular unit for the signal + * strength value. These values are in microvolts of RF at the tuner's input. + */ +static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; + + + +void cadet_wake(unsigned long qnum) +{ + switch(qnum) { + case 0: /* cadet_setfreq */ + wake_up(&tunerq); + break; + case 1: /* cadet_getrds */ + wake_up(&rdsq); + break; + } +} + + + +static int cadet_getrds(void) +{ + int rdsstat=0; + + cadet_lock++; + outb(3,io); /* Select Decoder Control/Status */ + outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ + cadet_lock--; + init_timer(&rdstimer); + rdstimer.function=cadet_wake; + rdstimer.data=(unsigned long)1; + rdstimer.expires=jiffies+(HZ/10); + rdsq=NULL; + add_timer(&rdstimer); + sleep_on(&rdsq); + + cadet_lock++; + outb(3,io); /* Select Decoder Control/Status */ + if((inb(io+1)&0x80)!=0) { + rdsstat|=VIDEO_TUNER_RDS_ON; + } + if((inb(io+1)&0x10)!=0) { + rdsstat|=VIDEO_TUNER_MBS_ON; + } + cadet_lock--; + return rdsstat; +} + + + static int cadet_getstereo(void) { - if(curtuner!=0) { /* Only FM has stereo capability! */ + if(curtuner!=0) { /* Only FM has stereo capability! */ return 0; } + cadet_lock++; outb(7,io); /* Select tuner control */ if((inb(io+1)&0x40)==0) { + cadet_lock--; return 1; /* Stereo pilot detected */ } else { + cadet_lock--; return 0; /* Mono */ } } -static unsigned cadet_getfreq(void) + +static unsigned cadet_gettune(void) { int curvol,i; - unsigned freq=0,test,fifo=0; - + unsigned fifo=0; /* * Prepare for read */ + cadet_lock++; outb(7,io); /* Select tuner control */ curvol=inb(io+1); /* Save current volume/mute setting */ outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */ + tunestat=0xffff; /* * Read the shift register @@ -66,6 +134,7 @@ fifo=(fifo<<1)|((inb(io+1)>>7)&0x01); if(i<24) { outb(0x01,io+1); + tunestat&=inb(io+1); outb(0x00,io+1); } } @@ -74,6 +143,22 @@ * Restore volume/mute setting */ outb(curvol,io+1); + cadet_lock--; + + return fifo; +} + + + +static unsigned cadet_getfreq(void) +{ + int i; + unsigned freq=0,test,fifo=0; + + /* + * Read current tuning + */ + fifo=cadet_gettune(); /* * Convert to actual frequency @@ -98,10 +183,40 @@ } + +static void cadet_settune(unsigned fifo) +{ + int i; + unsigned test; + + cadet_lock++; + outb(7,io); /* Select tuner control */ + /* + * Write the shift register + */ + test=0; + test=(fifo>>23)&0x02; /* Align data for SDO */ + test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */ + outb(7,io); /* Select tuner control */ + outb(test,io+1); /* Initialize for write */ + for(i=0;i<25;i++) { + test|=0x01; /* Toggle SCK High */ + outb(test,io+1); + test&=0xfe; /* Toggle SCK Low */ + outb(test,io+1); + fifo=fifo<<1; /* Prepare the next bit */ + test=0x1c|((fifo>>23)&0x02); + outb(test,io+1); + } + cadet_lock--; +} + + + static void cadet_setfreq(unsigned freq) { unsigned fifo; - int i,test; + int i,j,test; int curvol; /* @@ -129,39 +244,47 @@ /* * Save current volume/mute setting */ + cadet_lock++; + outb(7,io); /* Select tuner control */ curvol=inb(io+1); - /* - * Write the shift register - */ - test=0; - test=(fifo>>23)&0x02; /* Align data for SDO */ - test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */ - outb(7,io); /* Select tuner control */ - outb(test,io+1); /* Initialize for write */ - for(i=0;i<25;i++) { - test|=0x01; /* Toggle SCK High */ - outb(test,io+1); - test&=0xfe; /* Toggle SCK Low */ - outb(test,io+1); - fifo=fifo<<1; /* Prepare the next bit */ - test=0x1c|((fifo>>23)&0x02); - outb(test,io+1); - } - /* - * Restore volume/mute setting - */ - outb(curvol,io+1); + /* + * Tune the card + */ + for(j=3;j>-1;j--) { + cadet_settune(fifo|(j<<16)); + outb(7,io); /* Select tuner control */ + outb(curvol,io+1); + cadet_lock--; + init_timer(&tunertimer); + tunertimer.function=cadet_wake; + tunertimer.data=(unsigned long)0; + tunertimer.expires=jiffies+(HZ/10); + tunerq=NULL; + add_timer(&tunertimer); + sleep_on(&tunerq); + cadet_gettune(); + if((tunestat&0x40)==0) { /* Tuned */ + sigstrength=sigtable[curtuner][j]; + return; + } + cadet_lock++; + } + cadet_lock--; + sigstrength=0; } static int cadet_getvol(void) { + cadet_lock++; outb(7,io); /* Select tuner control */ if((inb(io+1)&0x20)!=0) { + cadet_lock--; return 0xffff; } else { + cadet_lock--; return 0; } } @@ -169,6 +292,7 @@ static void cadet_setvol(int vol) { + cadet_lock++; outb(7,io); /* Select tuner control */ if(vol>0) { outb(0x20,io+1); @@ -176,9 +300,88 @@ else { outb(0x00,io+1); } + cadet_lock--; } + +void cadet_handler(unsigned long data) +{ + /* + * Service the RDS fifo + */ + if(cadet_lock==0) { + outb(0x3,io); /* Select RDS Decoder Control */ + if((inb(io+1)&0x20)!=0) { + printk(KERN_CRIT "cadet: RDS fifo overflow\n"); + } + outb(0x80,io); /* Select RDS fifo */ + while((inb(io)&0x80)!=0) { + rdsbuf[rdsin++]=inb(io+1); + if(rdsin==rdsout) { + printk(KERN_CRIT "cadet: RDS buffer overflow\n"); + } + } + } + + /* + * Service pending read + */ + if((rdsin!=rdsout)&&(readq!=NULL)) { + wake_up_interruptible(&readq); + } + + /* + * Clean up and exit + */ + init_timer(&readtimer); + readtimer.function=cadet_handler; + readtimer.data=(unsigned long)0; + readtimer.expires=jiffies+(HZ/20); + add_timer(&readtimer); +} + + + +static long cadet_read(struct video_device *v,char *buf,unsigned long count, + int nonblock) +{ + int i=0,c; + unsigned char readbuf[RDS_BUFFER]; + + if(rdsstat==0) { + cadet_lock++; + rdsstat=1; + outb(0x80,io); /* Select RDS fifo */ + c=3*(inb(io)&0x03); + for(i=0;i<c;i++) { /* Flush the fifo */ + inb(io+1); + } + cadet_lock--; + init_timer(&readtimer); + readtimer.function=cadet_handler; + readtimer.data=(unsigned long)0; + readtimer.expires=jiffies+(HZ/20); + add_timer(&readtimer); + } + if(rdsin==rdsout) { + if(nonblock) { + return -EWOULDBLOCK; + } + interruptible_sleep_on(&readq); + readq=NULL; + } + while((i<count)&&(rdsin!=rdsout)) { + readbuf[i++]=rdsbuf[rdsout++]; + } + if(copy_to_user(buf,readbuf,i)) { + return -EFAULT; + } + return i; +} + + + static int cadet_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { unsigned freq; @@ -217,10 +420,11 @@ v.flags=0; v.mode=0; v.mode|=VIDEO_MODE_AUTO; - v.signal=0xFFFF; + v.signal=sigstrength; if(cadet_getstereo()==1) { v.flags|=VIDEO_TUNER_STEREO_ON; } + v.flags|=cadet_getrds(); if(copy_to_user(arg,&v, sizeof(v))) { return -EFAULT; } @@ -233,7 +437,7 @@ v.flags|=VIDEO_TUNER_LOW; v.mode=0; v.mode|=VIDEO_MODE_AUTO; - v.signal=0xFFFF; + v.signal=sigstrength; if(copy_to_user(arg,&v, sizeof(v))) { return -EFAULT; } @@ -313,11 +517,16 @@ return -EBUSY; users++; MOD_INC_USE_COUNT; + readq=NULL; return 0; } static void cadet_close(struct video_device *dev) { + if(rdsstat==1) { + del_timer(&readtimer); + rdsstat=0; + } users--; MOD_DEC_USE_COUNT; } @@ -330,7 +539,7 @@ VID_HARDWARE_CADET, cadet_open, cadet_close, - NULL, /* Can't read (no capture ability) */ + cadet_read, NULL, /* Can't write */ NULL, /* No poll */ cadet_ioctl, @@ -354,6 +563,7 @@ } +#ifndef MODULE static int cadet_probe(void) { static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e}; @@ -371,6 +581,7 @@ } return -1; } +#endif diff -u --recursive --new-file linux-2.2.7/include/linux/videodev.h linux/include/linux/videodev.h --- linux-2.2.7/include/linux/videodev.h Wed Mar 17 21:20:22 1999 +++ linux/include/linux/videodev.h Fri Apr 30 23:29:35 1999 @@ -95,6 +95,8 @@ #define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ #define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ #define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ +#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ +#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ __u16 mode; /* PAL/NTSC/SECAM/OTHER */ #define VIDEO_MODE_PAL 0 #define VIDEO_MODE_NTSC 1 @@ -275,6 +277,7 @@ #define VID_HARDWARE_GEMTEK 18 #define VID_HARDWARE_TYPHOON 19 #define VID_HARDWARE_VINO 20 /* Reserved for SGI Indy Vino */ +#define VID_HARDWARE_CADET 21 /* * Initialiser list -- patch ends --
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |