1/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi> 2 * 3 * GemTek hasn't released any specs on the card, so the protocol had to 4 * be reverse engineered with dosemu. 5 * 6 * Besides the protocol changes, this is mostly a copy of: 7 * 8 * RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff 9 * 10 * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood 11 * Coverted to new API by Alan Cox <Alan.Cox@linux.org> 12 * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> 13 * 14 * TODO: Allow for more than one of these foolish entities :-) 15 * 16 */ 17 18#include <linux/module.h> /* Modules */ 19#include <linux/init.h> /* Initdata */ 20#include <linux/ioport.h> /* check_region, request_region */ 21#include <linux/delay.h> /* udelay */ 22#include <asm/io.h> /* outb, outb_p */ 23#include <asm/uaccess.h> /* copy to/from user */ 24#include <linux/videodev.h> /* kernel radio structs */ 25#include <linux/config.h> /* CONFIG_RADIO_GEMTEK_PORT */ 26#include <linux/spinlock.h> 27 28#ifndef CONFIG_RADIO_GEMTEK_PORT 29#define CONFIG_RADIO_GEMTEK_PORT -1 30#endif 31 32static int io = CONFIG_RADIO_GEMTEK_PORT; 33static int radio_nr = -1; 34static int users = 0; 35static spinlock_t lock; 36 37struct gemtek_device 38{ 39 int port; 40 unsigned long curfreq; 41 int muted; 42}; 43 44 45/* local things */ 46 47/* the correct way to mute the gemtek may be to write the last written 48 * frequency || 0x10, but just writing 0x10 once seems to do it as well 49 */ 50static void gemtek_mute(struct gemtek_device *dev) 51{ 52 if(dev->muted) 53 return; 54 spin_lock(&lock); 55 outb(0x10, io); 56 spin_unlock(&lock); 57 dev->muted = 1; 58} 59 60static void gemtek_unmute(struct gemtek_device *dev) 61{ 62 if(dev->muted == 0) 63 return; 64 spin_lock(&lock); 65 outb(0x20, io); 66 spin_unlock(&lock); 67 dev->muted = 0; 68} 69 70static void zero(void) 71{ 72 outb_p(0x04, io); 73 udelay(5); 74 outb_p(0x05, io); 75 udelay(5); 76} 77 78static void one(void) 79{ 80 outb_p(0x06, io); 81 udelay(5); 82 outb_p(0x07, io); 83 udelay(5); 84} 85 86static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) 87{ 88 int i; 89 90/* freq = 78.25*((float)freq/16000.0 + 10.52); */ 91 92 freq /= 16; 93 freq += 10520; 94 freq *= 7825; 95 freq /= 100000; 96 97 spin_lock(&lock); 98 99 /* 2 start bits */ 100 outb_p(0x03, io); 101 udelay(5); 102 outb_p(0x07, io); 103 udelay(5); 104 105 /* 28 frequency bits (lsb first) */ 106 for (i = 0; i < 14; i++) 107 if (freq & (1 << i)) 108 one(); 109 else 110 zero(); 111 /* 36 unknown bits */ 112 for (i = 0; i < 11; i++) 113 zero(); 114 one(); 115 for (i = 0; i < 4; i++) 116 zero(); 117 one(); 118 zero(); 119 120 /* 2 end bits */ 121 outb_p(0x03, io); 122 udelay(5); 123 outb_p(0x07, io); 124 udelay(5); 125 126 spin_unlock(&lock); 127 128 return 0; 129} 130 131int gemtek_getsigstr(struct gemtek_device *dev) 132{ 133 spin_lock(&lock); 134 inb(io); 135 udelay(5); 136 spin_unlock(&lock); 137 if (inb(io) & 8) /* bit set = no signal present */ 138 return 0; 139 return 1; /* signal present */ 140} 141 142static int gemtek_ioctl(struct video_device *dev, unsigned int cmd, void *arg) 143{ 144 struct gemtek_device *rt=dev->priv; 145 146 switch(cmd) 147 { 148 case VIDIOCGCAP: 149 { 150 struct video_capability v; 151 v.type=VID_TYPE_TUNER; 152 v.channels=1; 153 v.audios=1; 154 /* No we don't do pictures */ 155 v.maxwidth=0; 156 v.maxheight=0; 157 v.minwidth=0; 158 v.minheight=0; 159 strcpy(v.name, "GemTek"); 160 if(copy_to_user(arg,&v,sizeof(v))) 161 return -EFAULT; 162 return 0; 163 } 164 case VIDIOCGTUNER: 165 { 166 struct video_tuner v; 167 if(copy_from_user(&v, arg,sizeof(v))!=0) 168 return -EFAULT; 169 if(v.tuner) /* Only 1 tuner */ 170 return -EINVAL; 171 v.rangelow=87*16000; 172 v.rangehigh=108*16000; 173 v.flags=VIDEO_TUNER_LOW; 174 v.mode=VIDEO_MODE_AUTO; 175 v.signal=0xFFFF*gemtek_getsigstr(rt); 176 strcpy(v.name, "FM"); 177 if(copy_to_user(arg,&v, sizeof(v))) 178 return -EFAULT; 179 return 0; 180 } 181 case VIDIOCSTUNER: 182 { 183 struct video_tuner v; 184 if(copy_from_user(&v, arg, sizeof(v))) 185 return -EFAULT; 186 if(v.tuner!=0) 187 return -EINVAL; 188 /* Only 1 tuner so no setting needed ! */ 189 return 0; 190 } 191 case VIDIOCGFREQ: 192 if(copy_to_user(arg, &rt->curfreq, sizeof(rt->curfreq))) 193 return -EFAULT; 194 return 0; 195 case VIDIOCSFREQ: 196 if(copy_from_user(&rt->curfreq, arg,sizeof(rt->curfreq))) 197 return -EFAULT; 198 /* needs to be called twice in order for getsigstr to work */ 199 gemtek_setfreq(rt, rt->curfreq); 200 gemtek_setfreq(rt, rt->curfreq); 201 return 0; 202 case VIDIOCGAUDIO: 203 { 204 struct video_audio v; 205 memset(&v,0, sizeof(v)); 206 v.flags|=VIDEO_AUDIO_MUTABLE; 207 v.volume=1; 208 v.step=65535; 209 strcpy(v.name, "Radio"); 210 if(copy_to_user(arg,&v, sizeof(v))) 211 return -EFAULT; 212 return 0; 213 } 214 case VIDIOCSAUDIO: 215 { 216 struct video_audio v; 217 if(copy_from_user(&v, arg, sizeof(v))) 218 return -EFAULT; 219 if(v.audio) 220 return -EINVAL; 221 222 if(v.flags&VIDEO_AUDIO_MUTE) 223 gemtek_mute(rt); 224 else 225 gemtek_unmute(rt); 226 227 return 0; 228 } 229 default: 230 return -ENOIOCTLCMD; 231 } 232} 233 234static int gemtek_open(struct video_device *dev, int flags) 235{ 236 if(users) 237 return -EBUSY; 238 users++; 239 return 0; 240} 241 242static void gemtek_close(struct video_device *dev) 243{ 244 users--; 245} 246 247static struct gemtek_device gemtek_unit; 248 249static struct video_device gemtek_radio= 250{ 251 owner: THIS_MODULE, 252 name: "GemTek radio", 253 type: VID_TYPE_TUNER, 254 hardware: VID_HARDWARE_GEMTEK, 255 open: gemtek_open, 256 close: gemtek_close, 257 ioctl: gemtek_ioctl, 258}; 259 260static int __init gemtek_init(void) 261{ 262 if(io==-1) 263 { 264 printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n"); 265 return -EINVAL; 266 } 267 268 if (!request_region(io, 4, "gemtek")) 269 { 270 printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); 271 return -EBUSY; 272 } 273 274 gemtek_radio.priv=&gemtek_unit; 275 276 if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1) 277 { 278 release_region(io, 4); 279 return -EINVAL; 280 } 281 printk(KERN_INFO "GemTek Radio Card driver.\n"); 282 283 spin_lock_init(&lock); 284 285 /* this is _maybe_ unnecessary */ 286 outb(0x01, io); 287 288 /* mute card - prevents noisy bootups */ 289 gemtek_unit.muted = 0; 290 gemtek_mute(&gemtek_unit); 291 292 return 0; 293} 294 295MODULE_AUTHOR("Jonas Munsin"); 296MODULE_DESCRIPTION("A driver for the GemTek Radio Card"); 297MODULE_LICENSE("GPL"); 298 299MODULE_PARM(io, "i"); 300MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard))."); 301MODULE_PARM(radio_nr, "i"); 302 303EXPORT_NO_SYMBOLS; 304 305static void __exit gemtek_cleanup(void) 306{ 307 video_unregister_device(&gemtek_radio); 308 release_region(io,4); 309} 310 311module_init(gemtek_init); 312module_exit(gemtek_cleanup); 313 314/* 315 Local variables: 316 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" 317 End: 318*/ 319