diff -uNr linux-2.4.17.nofmr/drivers/media/radio/Config.in linux-2.4.17/drivers/media/radio/Config.in
--- linux-2.4.17.nofmr/drivers/media/radio/Config.in	Fri Nov  9 23:01:22 2001
+++ linux-2.4.17/drivers/media/radio/Config.in	Fri Jan 18 13:00:37 2002
@@ -27,6 +27,10 @@
 dep_tristate '  miroSOUND PCM20 radio' CONFIG_RADIO_MIROPCM20 $CONFIG_VIDEO_DEV $CONFIG_SOUND_ACI_MIXER
 dep_tristate '    miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL)' CONFIG_RADIO_MIROPCM20_RDS $CONFIG_RADIO_MIROPCM20 $CONFIG_EXPERIMENTAL
 dep_tristate '  SF16FMI Radio' CONFIG_RADIO_SF16FMI $CONFIG_VIDEO_DEV
+dep_tristate '  SF16FMR Radio' CONFIG_RADIO_SF16FMR $CONFIG_VIDEO_DEV
+if [ "$CONFIG_RADIO_SF16FMR" = "y" ]; then
+   hex '    SF16FMR I/O port (Probably 0x384)' CONFIG_RADIO_SF16FMR_PORT 384
+fi
 dep_tristate '  TerraTec ActiveRadio ISA Standalone' CONFIG_RADIO_TERRATEC $CONFIG_VIDEO_DEV
 if [ "$CONFIG_RADIO_TERRATEC" = "y" ]; then
    hex '    Terratec i/o port (normally 0x590)' CONFIG_RADIO_TERRATEC_PORT 590
diff -uNr linux-2.4.17.nofmr/drivers/media/radio/Makefile linux-2.4.17/drivers/media/radio/Makefile
--- linux-2.4.17.nofmr/drivers/media/radio/Makefile	Sun Aug  5 22:15:05 2001
+++ linux-2.4.17/drivers/media/radio/Makefile	Fri Jan 18 12:59:30 2002
@@ -30,6 +30,7 @@
 obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
 obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
 obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
+obj-$(CONFIG_RADIO_SF16FMR) += radio-sf16fmr.o
 obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
 obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
 obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
diff -uNr linux-2.4.17.nofmr/drivers/media/radio/radio-sf16fmr.c linux-2.4.17/drivers/media/radio/radio-sf16fmr.c
--- linux-2.4.17.nofmr/drivers/media/radio/radio-sf16fmr.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.17/drivers/media/radio/radio-sf16fmr.c	Sat May 11 14:32:05 2002
@@ -0,0 +1,427 @@
+/*
+ *
+ * SF16FMR radio driver for Linux radio support
+ *
+ * heavily based on SF16FMI driver...
+ *
+ * Copyright 1997 M. Kirkwood
+ * Copyright 1998 Petr Vandrovec, vandrove@vc.cvut.cz
+ * Copyright 2001 Wilmer van der Gaast, lintux@lintux.cx
+ *
+ * Specs from a program called fmr, which allowed me to use the radio card,
+ * but only by accessing the ports from a user space program so only as
+ * root... This driver allows you to use any program as any user. I lost the
+ * URL of the original program and can't find it anymore. I should still
+ * thank this guy since he did all the hard work reverse-engineering this
+ * card. THANKS!!! :)
+ *
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_SF16FMR_PORT 	*/
+#include <asm/semaphore.h>
+
+struct fmr_device
+{
+	int port;
+        int curvol; /* 0-15 */
+        unsigned long curfreq; /* freq in kHz */
+        __u32 flags;
+};
+
+#ifndef CONFIG_RADIO_SF16FMR_PORT
+#define CONFIG_RADIO_SF16FMR_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_SF16FMR_PORT; 
+static int radio_nr = -1;
+static int users = 0;
+static struct semaphore lock;
+
+#define FMR_ENCODE( x ) ( 9870 + ( x - 88000 ) / 10 )
+#define FMR_MINFREQ ( 88000 * 16 )
+#define FMR_MAXFREQ ( 108000 * 16 )
+
+#define outp( port, value ) outb( value, port )
+#define inp inb
+
+#define fmr_fone \
+	{ outp( port, 0x05 ); outp( port, 0x07 ); inp( port ); inp( port ); }
+#define fmr_fzero \
+	{ outp( port, 0x04 ); outp( port, 0x06 ); inp( port ); inp( port ); }
+#define fmr_sone \
+	{ outp( port, 0x27 ); outp( port, 0x37 ); inp( port ); inp( port ); }
+#define fmr_szero \
+	{ outp( port, 0x07 ); outp( port, 0x17 ); inp( port ); inp( port ); }
+
+static inline void fmr_reset( struct fmr_device *dev )
+{
+	int port = dev->port;
+	
+	down( &lock );
+	
+	outp( port, 0x07 );
+	outp( port, 0x03 );
+	outp( port, 0x00 );
+	outp( port, 0x02 );
+	
+	/* 1001 011 */
+	fmr_fone; fmr_fzero; fmr_fzero; fmr_fone;
+	fmr_fzero; fmr_fone; fmr_fone;
+	
+	/* 0000 0000 0000 0000 (16x0) */
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	
+	/* 0101 1010 */
+	fmr_fzero; fmr_fone; fmr_fzero; fmr_fone;
+	fmr_fone; fmr_fzero; fmr_fone; fmr_fzero;
+	
+	outp( port, 0x03 );
+	outp( port, 0x07 );
+	outp( port, 0x03 );
+	outp( port, 0x01 );
+	outp( port, 0x03 );
+	
+	/* 0001 0111 */
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fone;
+	fmr_fzero; fmr_fone; fmr_fone; fmr_fone;
+	
+	/* 0000 0000 0000 0000 0000 0000 (24x0) */
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	
+	outp( port, 0x03 );
+	
+	up( &lock );
+}
+
+int fmr_setvol( struct fmr_device *dev )
+{
+	int port = dev->port;
+	int vol = dev->curvol;
+	int fmr_vtable[16] =
+	{
+		0x0, 0x84, 0x90, 0x104, 0x110, 0x204, 0x210, 0x402,
+		0x404, 0x408, 0x410, 0x801, 0x802, 0x804, 0x808, 0x810
+	};
+	int i, x;
+	
+	down( &lock );
+
+	outp( port, 0x07 );
+	
+	x = fmr_vtable[vol];
+	for( i = 0; i < 12; i ++ )
+	{
+		if( x & 0x800 )
+		{
+			fmr_sone;
+		}
+		else
+		{
+			fmr_szero;
+		}
+		x <<= 1;
+	}
+	
+	/* 011000 */
+	fmr_szero; fmr_sone; fmr_sone; fmr_szero; fmr_szero; fmr_szero; 
+	
+	outp( port, 0x0f );
+	
+	up( &lock );
+	
+	return( 0 );
+}
+
+static inline int fmr_setfreq(struct fmr_device *dev)
+{
+        int port = dev->port;
+	unsigned long freq = dev->curfreq / 16;
+	int i;
+	
+	down( &lock );
+	
+	outp( port, 0x07 );
+	outp( port, 0x03 );
+	outp( port, 0x00 );
+	outp( port, 0x02 );
+	
+	/* 0001 011 */
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fone;
+	fmr_fzero; fmr_fone; fmr_fone;
+	
+	freq = FMR_ENCODE( freq );
+	for( i = 0; i < 16; i ++ )
+	{
+		if( freq & 1 )
+		{
+			fmr_fone;
+		}
+		else
+		{
+			fmr_fzero;
+		}
+		freq >>= 1;
+	}
+
+	/* 0101 1010 */
+	fmr_fzero; fmr_fone; fmr_fzero; fmr_fone;
+	fmr_fone; fmr_fzero; fmr_fone; fmr_fzero;
+	
+	outp( port, 0x03 );
+	outp( port, 0x07 );
+	outp( port, 0x03 );
+	outp( port, 0x01 );
+	outp( port, 0x03 );
+	
+	/* 0001 0111 */
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fone;
+	fmr_fzero; fmr_fone; fmr_fone; fmr_fone;
+	
+	/* 0000 0000 0000 0000 0000 0000 (24x0) */
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fzero;
+	
+	outp( port, 0x03 );
+	
+	up( &lock );
+	
+	return( 0 );
+}
+
+static int fmr_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct fmr_device *fmr=dev->priv;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability v;
+			strcpy(v.name, "SF16-FMR radio");
+			v.type=VID_TYPE_TUNER;
+			v.channels=1;
+			v.audios=1;
+			/* No we don't do pictures */
+			v.maxwidth=0;
+			v.maxheight=0;
+			v.minwidth=0;
+			v.minheight=0;
+			if(copy_to_user(arg,&v,sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner v;
+			int mult;
+
+			if(copy_from_user(&v, arg,sizeof(v))!=0)
+				return -EFAULT;
+			if(v.tuner)	/* Only 1 tuner */
+				return -EINVAL;
+			strcpy(v.name, "FM");
+			mult = (fmr->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
+			v.rangelow = FMR_MINFREQ/mult;
+			v.rangehigh = FMR_MAXFREQ/mult;
+			v.flags=fmr->flags;
+			v.mode=VIDEO_MODE_AUTO;
+		/*	Not yet supported:
+			v.signal = fmr_getsigstr(fmr);
+		*/
+			v.signal = 0;
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.tuner!=0)
+				return -EINVAL;
+			fmr->flags = v.flags & VIDEO_TUNER_LOW;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+		{
+			unsigned long tmp = fmr->curfreq;
+			if (!(fmr->flags & VIDEO_TUNER_LOW))
+				tmp /= 1000;
+			if(copy_to_user(arg, &tmp, sizeof(tmp)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSFREQ:
+		{
+			unsigned long tmp;
+			if(copy_from_user(&tmp, arg, sizeof(tmp)))
+				return -EFAULT;
+			if (!(fmr->flags & VIDEO_TUNER_LOW))
+				tmp *= 1000;
+			if ( tmp<FMR_MINFREQ || tmp>FMR_MAXFREQ )
+				return -EINVAL;
+			fmr->curfreq = tmp;
+			fmr_setfreq(fmr);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio v;
+			v.audio=0;
+			v.volume=4096*fmr->curvol;
+			v.bass=0;
+			v.treble=0;
+			v.flags=fmr->flags;
+			strcpy(v.name, "Radio");
+			v.mode=VIDEO_SOUND_STEREO;
+			v.balance=0;
+			v.step=4096;
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.audio)
+				return -EINVAL;
+			fmr->curvol = v.volume / 4096;
+			fmr_setvol( fmr );
+			return 0;
+		}
+	        case VIDIOCGUNIT:
+		{
+               		struct video_unit v;
+			v.video=VIDEO_NO_UNIT;
+			v.vbi=VIDEO_NO_UNIT;
+			v.radio=dev->minor;
+			v.audio=0;
+			v.teletext=VIDEO_NO_UNIT;
+			if(copy_to_user(arg, &v, sizeof(v)))
+				return -EFAULT;
+			return 0;			
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int fmr_open(struct video_device *dev, int flags)
+{
+	if(users)
+		return -EBUSY;
+	users++;
+	return 0;
+}
+
+static void fmr_close(struct video_device *dev)
+{
+	users--;
+}
+
+static struct fmr_device fmr_unit;
+
+static struct video_device fmr_radio=
+{
+	owner:		THIS_MODULE,
+	name:		"SF16FMR radio",
+	type:		VID_TYPE_TUNER,
+	/* Other drivers leave it this way too */
+	hardware:	VID_HARDWARE_SF16MI,
+	open:		fmr_open,
+	close:		fmr_close,
+	ioctl:		fmr_ioctl,
+};
+
+static inline void fmr_wait( int c )
+{
+	while( c )
+	{
+		udelay( 1400 );
+		if( current -> need_resched)
+		{
+			schedule();
+		}
+		c --;
+	}
+}
+
+static int __init fmr_init(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	if (!request_region(io, 2, "fmr")) 
+	{
+		printk(KERN_ERR "fmr: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	fmr_unit.port = io;
+	fmr_unit.curvol = 0;
+	fmr_unit.curfreq = FMR_MINFREQ;
+	fmr_unit.flags = VIDEO_TUNER_LOW;
+	fmr_radio.priv = &fmr_unit;
+	
+	init_MUTEX(&lock);
+	
+	if(video_register_device(&fmr_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+	{
+		release_region(io, 2);
+		return -EINVAL;
+	}
+	
+	fmr_reset( &fmr_unit );
+	fmr_wait( 100 );
+	fmr_reset( &fmr_unit );
+	fmr_wait( 100 );
+	fmr_setvol( &fmr_unit );
+	fmr_setfreq( &fmr_unit );
+		
+	printk(KERN_INFO "SF16FMR radio card driver at 0x%x.\n", io);
+	printk(KERN_INFO "Copyright 2001 Wilmer van der Gaast.\n");
+	
+	return( 0 );
+}
+
+MODULE_AUTHOR("Wilmer van der Gaast, Petr Vandrovec and M. Kirkwood");
+MODULE_DESCRIPTION("A driver for the SF16FMR radio.");
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O address of the SF16FMR card (Probably 0x384)");
+MODULE_PARM(radio_nr, "i");
+
+EXPORT_NO_SYMBOLS;
+
+static void __exit fmr_cleanup_module(void)
+{
+	video_unregister_device(&fmr_radio);
+	release_region(io,2);
+}
+
+module_init(fmr_init);
+module_exit(fmr_cleanup_module);
diff -uNr linux-2.4.17.nofmr/Documentation/Configure.help linux-2.4.17/Documentation/Configure.help
--- linux-2.4.17.nofmr/Documentation/Configure.help	Fri Dec 21 18:41:53 2001
+++ linux-2.4.17/Documentation/Configure.help	Fri Jan 18 16:42:33 2002
@@ -21236,6 +21343,26 @@
   inserted in and removed from the running kernel whenever you want),
   say M here and read <file:Documentation/modules.txt>.  The module
   will be called radio-sf16fmi.o.
+
+SF16FMR Radio
+CONFIG_RADIO_SF16FMR
+  Choose Y here if you have one of these FM radio cards, and then fill
+  in the port address below.
+
+  In order to control your radio card, you will need to use programs
+  that are compatible with the Video for Linux API. Information on 
+  this API and pointers to "v4l" programs may be found on the WWW at
+  http://roadrunner.swansea.uk.linux.org/v4l.shtml .
+
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. The module will be
+  called radio-sf16fmi.o
+
+SF16FMR I/O port (Probably 0x384)
+CONFIG_RADIO_SF16FMR_PORT
+  Enter the I/O port of your SF16FMR radio card. On my system it was
+  0x384 by default so I think most cards use that one by default.
 
 Typhoon Radio (a.k.a. EcoRadio)
 CONFIG_RADIO_TYPHOON
--- linux-2.4.17.nofmr/CREDITS	Fri Dec 21 18:41:53 2001
+++ linux-2.4.17/CREDITS	Fri Jan 18 16:42:33 2002
@@ -969,6 +969,14 @@
 S: 80050-430 - Curitiba - Paraná
 S: Brazil
 
+N: Wilmer van der Gaast
+E: lintux@lintux.cx
+W: http://www.lintux.cx/
+D: SF16-FMR driver
+S: Vorrelveen 4
+S: 9411 VP Beilen
+S: The Netherlands
+
 N: Nigel Gamble
 E: nigel@nrg.org
 E: nigel@sgi.com

