[PATCH] support for gemtek's radio card

Jonas Munsin (jmunsin@iki.fi)
Mon, 14 Dec 1998 20:37:19 +0200


Hello.

Here is a patch that adds support for the GemTek Radio Card.

(last-minute note: GemTek also makes a combined radio-soundcard, this patch has
only been tested with the radiocard-only version)

-- 
Jonas Munsin          Physics student at Åbo Akademi | A social life?
email: jmunsin@iki.NOSPAM.fi  ph: +358-(0)40-5257809 | Where can I
IRC:EvenFlow/effe  Remove the NOSPAM part in my email| download THAT?!?

diff -u --new-file --recursive linux/Documentation/Configure.help linux.gemtek/Documentation/Configure.help --- linux/Documentation/Configure.help Mon Dec 14 20:21:16 1998 +++ linux.gemtek/Documentation/Configure.help Mon Dec 14 19:48:31 1998 @@ -10278,6 +10278,28 @@ say M here and read Documentation/modules.txt. The module will be called radio-miropcm20.o +GemTek Radio Card +CONFIG_RADIO_GEMTEK + Choose Y here if you have this FM radio card, 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-gemtek.o. + +GemTek i/o port +CONFIG_RADIO_GEMTEK_PORT + Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is + 0x34c, if you haven't changed the jumper setting on the card. + BT848 Video For Linux CONFIG_VIDEO_BT848 Support for BT848 based frame grabber/overlay boards. This includes diff -u --new-file --recursive linux/drivers/char/Config.in linux.gemtek/drivers/char/Config.in --- linux/drivers/char/Config.in Mon Dec 14 20:21:20 1998 +++ linux.gemtek/drivers/char/Config.in Mon Dec 14 19:48:31 1998 @@ -110,6 +110,10 @@ hex ' Aztech/Packard Bell I/O port (0x350 or 0x358)' CONFIG_RADIO_AZTECH_PORT 350 fi dep_tristate 'Miro PCM20 Radio' CONFIG_RADIO_MIROPCM20 $CONFIG_VIDEO_DEV + dep_tristate 'GemTek Radio Card support' CONFIG_RADIO_GEMTEK $CONFIG_VIDEO_DEV + if [ "$CONFIG_RADIO_GEMTEK" = "y" ]; then + hex ' GemTek i/o port (0x20c, 0x30c, 0x24c or 0x34c)' CONFIG_RADIO_GEMTEK_PORT 34c + fi if [ "$CONFIG_PCI" != "n" ]; then dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV fi diff -u --new-file --recursive linux/drivers/char/Makefile linux.gemtek/drivers/char/Makefile --- linux/drivers/char/Makefile Mon Dec 14 20:21:20 1998 +++ linux.gemtek/drivers/char/Makefile Mon Dec 14 19:48:31 1998 @@ -380,6 +380,14 @@ endif endif +ifeq ($(CONFIG_RADIO_GEMTEK),y) +L_OBJS += radio-gemtek.o +else + ifeq ($(CONFIG_RADIO_GEMTEK),m) + M_OBJS += radio-gemtek.o + endif +endif + ifeq ($(CONFIG_QIC02_TAPE),y) L_OBJS += tpqic02.o else diff -u --new-file --recursive linux/drivers/char/radio-gemtek.c linux.gemtek/drivers/char/radio-gemtek.c --- linux/drivers/char/radio-gemtek.c Thu Jan 1 02:00:00 1970 +++ linux.gemtek/drivers/char/radio-gemtek.c Mon Dec 14 19:48:54 1998 @@ -0,0 +1,310 @@ +/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi> + * + * GemTek hasn't released any specs on the card, so the protocol had to + * be reverse engineered with dosemu. + * + * Besides the protocol changes, this is mostly a copy of: + * + * RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff + * + * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood + * Coverted to new API by Alan Cox <Alan.Cox@linux.org> + * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> + * + * TODO: Allow for more than one of these foolish entities :-) + * + */ + +#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_GEMTEK_PORT */ + +#ifndef CONFIG_RADIO_GEMTEK_PORT +#define CONFIG_RADIO_GEMTEK_PORT -1 +#endif + +static int io = CONFIG_RADIO_GEMTEK_PORT; +static int users = 0; + +struct gemtek_device +{ + int port; + unsigned long curfreq; + int muted; +}; + + +/* local things */ + +/* the correct way to mute the gemtek may be to write the last written + * frequency || 0x10, but just writing 0x10 once seems to do it as well + */ +static void gemtek_mute(struct gemtek_device *dev) +{ + if(dev->muted) + return; + outb(0x10, io); + dev->muted = 1; +} + +static void gemtek_unmute(struct gemtek_device *dev) +{ + if(dev->muted == 0) + return; + outb(0x20, io); + dev->muted = 0; +} + +static void zero(void) +{ + outb_p(0x04, io); + udelay(5); + outb_p(0x05, io); + udelay(5); +} + +static void one(void) +{ + outb_p(0x06, io); + udelay(5); + outb_p(0x07, io); + udelay(5); +} + +static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) +{ + int i; + +/* freq = 78.25*((float)freq/16000.0 + 10.52); */ + + freq /= 16; + freq += 10520; + freq *= 7825; + freq /= 100000; + + /* 2 start bits */ + outb_p(0x03, io); + udelay(5); + outb_p(0x07, io); + udelay(5); + + /* 28 frequency bits (lsb first) */ + for (i = 0; i < 14; i++) + if (freq & (1 << i)) + one(); + else + zero(); + /* 36 unknown bits */ + for (i = 0; i < 11; i++) + zero(); + one(); + for (i = 0; i < 4; i++) + zero(); + one(); + zero(); + + /* 2 end bits */ + outb_p(0x03, io); + udelay(5); + outb_p(0x07, io); + udelay(5); + + return 0; +} + +int gemtek_getsigstr(struct gemtek_device *dev) +{ + inb(io); + udelay(5); + if (inb(io) & 8) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int gemtek_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct gemtek_device *rt=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability v; + 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; + strcpy(v.name, "GemTek"); + 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; + v.rangelow=87*16000; + v.rangehigh=108*16000; + v.flags=VIDEO_TUNER_LOW; + v.mode=VIDEO_MODE_AUTO; + v.signal=0xFFFF*gemtek_getsigstr(rt); + 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; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if(copy_to_user(arg, &rt->curfreq, sizeof(rt->curfreq))) + return -EFAULT; + return 0; + case VIDIOCSFREQ: + if(copy_from_user(&rt->curfreq, arg,sizeof(rt->curfreq))) + return -EFAULT; + /* needs to be called twice in order for getsigstr to work */ + gemtek_setfreq(rt, rt->curfreq); + gemtek_setfreq(rt, rt->curfreq); + return 0; + case VIDIOCGAUDIO: + { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags|=VIDEO_AUDIO_MUTABLE; + v.volume=1; + v.step=65535; + strcpy(v.name, "Radio"); + 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; + + if(v.flags&VIDEO_AUDIO_MUTE) + gemtek_mute(rt); + else + gemtek_unmute(rt); + + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int gemtek_open(struct video_device *dev, int flags) +{ + if(users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void gemtek_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + +static struct gemtek_device gemtek_unit; + +static struct video_device gemtek_radio= +{ + "GemTek radio", + VID_TYPE_TUNER, + VID_HARDWARE_GEMTEK, + gemtek_open, + gemtek_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* Can't poll */ + gemtek_ioctl, + NULL, + NULL +}; + +__initfunc(int gemtek_init(struct video_init *v)) +{ + if (check_region(io, 4)) + { + printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); + return -EBUSY; + } + + gemtek_radio.priv=&gemtek_unit; + + if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO)==-1) + return -EINVAL; + + request_region(io, 4, "gemtek"); + printk(KERN_INFO "GemTek Radio Card driver.\n"); + + /* mute card - prevents noisy bootups */ + outb(0x10, io); + udelay(5); + gemtek_unit.muted = 1; + + /* this is _maybe_ unnecessary */ + outb(0x01, io); + + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("Jonas Munsin"); +MODULE_DESCRIPTION("A driver for the GemTek Radio Card"); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c)"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c\n"); + return -EINVAL; + } + return gemtek_init(NULL); +} + +void cleanup_module(void) +{ + video_unregister_device(&gemtek_radio); + release_region(io,4); +} + +#endif + +/* + Local variables: + compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c" + End: +*/ diff -u --new-file --recursive linux/drivers/char/videodev.c linux.gemtek/drivers/char/videodev.c --- linux/drivers/char/videodev.c Mon Nov 9 00:36:46 1998 +++ linux.gemtek/drivers/char/videodev.c Mon Dec 14 19:48:31 1998 @@ -68,6 +68,9 @@ #ifdef CONFIG_RADIO_MIROPCM20 extern int pcm20_init(struct video_init *); #endif +#ifdef CONFIG_RADIO_GEMTEK +extern int gemtek_init(struct video_init *); +#endif #ifdef CONFIG_VIDEO_PMS extern int init_pms_cards(struct video_init *); #endif @@ -103,6 +106,9 @@ #endif #ifdef CONFIG_RADIO_MIROPCM20 {"PCM20", pcm20_init}, +#endif +#ifdef CONFIG_RADIO_GEMTEK + {"GemTek", gemtek_init}, #endif {"end", NULL} }; diff -u --new-file --recursive linux/include/linux/videodev.h linux.gemtek/include/linux/videodev.h --- linux/include/linux/videodev.h Mon Dec 14 20:21:45 1998 +++ linux.gemtek/include/linux/videodev.h Mon Dec 14 20:01:55 1998 @@ -261,6 +261,7 @@ #define VID_HARDWARE_PERMEDIA2 14 /* Reserved for Permedia2 */ #define VID_HARDWARE_RIVA128 15 /* Reserved for RIVA 128 */ #define VID_HARDWARE_PLANB 16 /* PowerMac motherboard video-in */ +#define VID_HARDWARE_GEMTEK 17 /* * Initialiser list

- 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/