diff -uNr linux-2.2.14/Documentation/Configure.help linux-2.2.14-radio/Documentation/Configure.help
--- linux-2.2.14/Documentation/Configure.help	Wed Jan  5 02:28:24 2000
+++ linux-2.2.14-radio/Documentation/Configure.help	Fri Apr  7 20:02:51 2000
@@ -11302,6 +11302,27 @@
 CONFIG_RADIO_SF16FMI_PORT
   Enter the I/O port of your SF16FMI radio card.
 
+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; to browse the WWW,
+  you need to have access to a machine on the Internet that has a 
+  program like lynx or netscape.
+
+  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-sf16fmr.o
+
+SF16FMR I/O port (Probably 0x384)
+CONFIG_RADIO_SF16FMR_PORT
+  Enter the I/O port of your SF16FMR radio card.
+
 Typhoon Radio
 CONFIG_RADIO_TYPHOON
   Choose Y here if you have one of these FM radio cards, and then fill
diff -uNr linux-2.2.14/drivers/char/Config.in linux-2.2.14-radio/drivers/char/Config.in
--- linux-2.2.14/drivers/char/Config.in	Wed Jan  5 02:28:31 2000
+++ linux-2.2.14-radio/drivers/char/Config.in	Fri Apr  7 19:53:41 2000
@@ -159,6 +159,10 @@
   if [ "$CONFIG_RADIO_SF16FMI" = "y" ]; then
     hex '  SF16FMI I/O port (0x284 or 0x384)' CONFIG_RADIO_SF16FMI_PORT 284
   fi
+  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
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     if [ "$CONFIG_SGI" = "y" ]; then
       dep_tristate 'SGI Vino Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_VINO $CONFIG_VIDEO_DEV
diff -uNr linux-2.2.14/drivers/char/Makefile linux-2.2.14-radio/drivers/char/Makefile
--- linux-2.2.14/drivers/char/Makefile	Wed Jan  5 02:28:31 2000
+++ linux-2.2.14-radio/drivers/char/Makefile	Fri Apr  7 19:58:22 2000
@@ -480,6 +480,14 @@
   endif
 endif                                             
 
+ifeq ($(CONFIG_RADIO_SF16FMR),y)
+L_OBJS += radio-sf16fmr.o
+else
+  ifeq ($(CONFIG_RADIO_SF16FMR),m)
+  M_OBJS += radio-sf16fmr.o
+  endif
+endif                                             
+
 ifeq ($(CONFIG_RADIO_RTRACK),y)
 L_OBJS += radio-aimslab.o
 else
diff -uNr linux-2.2.14/drivers/char/radio-sf16fmr.c linux-2.2.14-radio/drivers/char/radio-sf16fmr.c
--- linux-2.2.14/drivers/char/radio-sf16fmr.c	Thu Jan  1 01:00:00 1970
+++ linux-2.2.14-radio/drivers/char/radio-sf16fmr.c	Sat Apr  8 14:00:14 2000
@@ -0,0 +1,403 @@
+/* SF16FMR radio driver for Linux radio support
+ * heavily based on sf16fmi driver...
+ * 'specs' from fmr
+ *
+ * Written to allow me to use any radio program I want, not just fmr.
+ *
+ *
+ * Copyright 1997 M. Kirkwood
+ * Copyright 1998 Petr Vandrovec, vandrove@vc.cvut.cz
+ * Copyright 2000 Wilmer van der Gaast
+ *
+ */
+
+#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 	*/
+
+struct fmr_device
+{
+	int port;
+        int curvol; /* 0 to 15 */
+        int tmpvol; /* 0 to 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 users = 0;
+
+#define outp( port, value ) outb( value, port )
+#define inp inb
+
+#define RSF16_ENCODE(x)	( 9870 + ( x - 88000 ) / 10 )
+#define RSF16_DECODE(x) ( ( x - 9870 ) * 10 + 88000 )
+#define V4L_ENCODE(x) ( x * 16 )
+#define V4L_DECODE(x) ( x / 16 )
+#define RSF16_MINFREQ 88000
+#define RSF16_MAXFREQ 108000
+
+#define fmr_fone outp( port, 0x5 ); outp( port, 0x7 ); inp( port ); inp( port )
+#define fmr_fzero outp( port, 0x4 ); outp( port, 0x6 ); 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 )
+
+void fmr_enable( int port )
+{
+	outp( port, 0x07 );
+	outp( port, 0x03 );
+	outp( port, 0x00 );
+	outp( port, 0x02 );
+	
+	// 1001011
+	fmr_fone; fmr_fzero; fmr_fzero; fmr_fone; fmr_fzero; fmr_fone; fmr_fone;
+	
+	// 0000000000000000 (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;
+	
+	// 01011010
+	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 );
+	
+	// 00010111
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fone; fmr_fzero; fmr_fone; fmr_fone; fmr_fone;
+	
+	// 000000000000000000000000 (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 );
+}
+
+int fmr_setfreq(struct fmr_device *dev)
+{
+        int port = dev->port;
+	unsigned long freq = dev->curfreq;
+	int i;
+	
+	outp( port, 0x07 );
+	outp( port, 0x03 );
+	outp( port, 0x00 );
+	outp( port, 0x02 );
+	
+	// 0001011
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fone; fmr_fzero; fmr_fone; fmr_fone;
+	
+	freq = RSF16_ENCODE( freq );
+	for( i = 0; i < 16; i ++ )
+	{
+		if( freq & 1 )
+		{
+			fmr_fone;
+		}
+		else
+		{
+			fmr_fzero;
+		}
+		freq >>= 1;
+	}
+
+	// 01011010
+	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 );
+	
+	// 00010111
+	fmr_fzero; fmr_fzero; fmr_fzero; fmr_fone; fmr_fzero; fmr_fone; fmr_fone; fmr_fone;
+	
+	// 000000000000000000000000 (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 );
+	
+	return 0;
+}
+
+int fmr_setvol( struct fmr_device *dev )
+{
+	int port = dev -> port;
+	int vol = dev -> curvol;
+	int i, x;
+	int fmr_vtable[16] = { 0x0, 0x84, 0x90, 0x104, 0x110, 0x204, 0x210, 0x402, 0x404, 0x408, 0x410, 0x801, 0x802, 0x804, 0x808, 0x810 };
+	
+	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 );
+}
+
+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;
+
+			if( copy_from_user( &v, arg, sizeof( v ) ) != 0 )
+				return( -EFAULT );
+			if( v.tuner )	/* Only 1 tuner */
+				return( -EINVAL );
+			strcpy( v.name, "FM" );
+			v.rangelow = V4L_ENCODE( RSF16_MINFREQ );
+			v.rangehigh = V4L_ENCODE( RSF16_MAXFREQ );
+			v.flags = fmr -> flags;
+			v.mode = VIDEO_MODE_AUTO;
+			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;
+			tmp = V4L_ENCODE( tmp );
+			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;
+			tmp = V4L_DECODE( tmp );
+			if( tmp < RSF16_MINFREQ )
+				tmp = RSF16_MINFREQ;
+			if( tmp > RSF16_MAXFREQ )
+				tmp = RSF16_MAXFREQ;
+			fmr->curfreq = tmp; 
+			fmr_setfreq( fmr );
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio v;
+			v.audio = 0;
+			v.volume = 4369 * fmr -> curvol;
+			v.bass = 0;
+			v.treble = 0;
+			v.flags = ( ( fmr -> curvol == 0 ) * VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE );
+			strcpy(v.name, "Radio");
+			v.mode = VIDEO_SOUND_STEREO;
+			v.balance = 0;
+			v.step = 1;
+			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 );
+			
+			if( fmr -> flags & VIDEO_AUDIO_MUTE )
+			{
+				fmr -> tmpvol = fmr -> curvol;
+				fmr -> curvol = 0;
+				fmr_setvol( fmr );
+			}
+			else if( fmr -> tmpvol != 0 )
+			{
+				fmr -> curvol = fmr -> tmpvol;
+				fmr -> tmpvol = 0;
+				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;
+	}
+}
+
+int fmr_open(struct video_device *dev, int flags)
+{
+	if(users)
+		return -EBUSY;
+	users++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+void fmr_close(struct video_device *dev)
+{
+	users--;
+	MOD_DEC_USE_COUNT;
+}
+
+static struct fmr_device fmr_unit;
+
+static struct video_device fmr_radio=
+{
+	"SF16FMR radio",
+	VID_TYPE_TUNER,
+	VID_HARDWARE_SF16MI,
+	fmr_open,
+	fmr_close,
+	NULL,	/* Can't read  (no capture ability) */
+	NULL,	/* Can't write */
+	NULL,	/* Can't poll */
+	fmr_ioctl,
+	NULL,
+	NULL
+};
+
+static inline void fmr_wait( int c )
+{
+	int i;
+	
+	for( i = 0; i < c; i ++ )
+	{
+		udelay( 1400 );
+		if( current -> need_resched)
+		{
+			schedule();
+		}
+	}
+}
+
+__initfunc(int fmr_init(struct video_init *v))
+{
+	if (check_region(io, 2)) 
+	{
+		printk(KERN_ERR "fmr: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	fmr_unit.port = io;
+	fmr_unit.curvol = 0;
+	fmr_unit.tmpvol = 0;
+	fmr_unit.curfreq = RSF16_MINFREQ;
+	fmr_unit.flags = VIDEO_TUNER_LOW;
+	fmr_radio.priv = &fmr_unit;
+	
+	if(video_register_device(&fmr_radio, VFL_TYPE_RADIO)==-1)
+		return -EINVAL;
+		
+	request_region(io, 2, "fmr");
+	printk( KERN_INFO "Using SF16-FMR radio card at 0x%x\n", io );
+	printk( KERN_INFO "Copyright 2000 Wilmer van der Gaast (lintux@lintux.cx)\n" );
+	
+	// Do it twice, works better, somehow...
+	fmr_enable( io );
+	fmr_wait( 100 );
+	fmr_enable( io );
+	fmr_wait( 100 );
+	fmr_setvol( &fmr_unit );
+	fmr_setfreq( &fmr_unit );
+	return( 0 );
+}
+
+#ifdef MODULE
+
+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)");
+
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	return fmr_init(NULL);
+}
+
+void cleanup_module(void)
+{
+	video_unregister_device(&fmr_radio);
+	release_region(io,2);
+}
+
+#endif
diff -uNr linux-2.2.14/drivers/char/videodev.c linux-2.2.14-radio/drivers/char/videodev.c
--- linux-2.2.14/drivers/char/videodev.c	Fri Nov 19 18:41:31 1999
+++ linux-2.2.14-radio/drivers/char/videodev.c	Sat Apr  8 14:15:54 2000
@@ -69,6 +69,9 @@
 #ifdef CONFIG_RADIO_SF16FMI
 extern int fmi_init(struct video_init *);
 #endif
+#ifdef CONFIG_RADIO_SF16FMR
+extern int fmr_init(struct video_init *);
+#endif
 #ifdef CONFIG_RADIO_MIROPCM20
 extern int pcm20_init(struct video_init *);
 #endif
@@ -122,6 +125,9 @@
 #endif
 #ifdef CONFIG_RADIO_SF16FMI
 	{"SF16FMI", fmi_init}, 
+#endif	
+#ifdef CONFIG_RADIO_SF16FMR
+	{"SF16FMR", fmr_init}, 
 #endif	
 #ifdef CONFIG_RADIO_MIROPCM20
 	{"PCM20", pcm20_init}, 
