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 * Converted 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 32 static int io = CONFIG_RADIO_GEMTEK_PORT; 33 static int radio_nr = -1; 34 static spinlock_t lock; 35 36 struct gemtek_device 37 { 38 int port; 39 unsigned long curfreq; 40 int muted; 41 }; 42 43 44 /* local things */ 45 46 /* the correct way to mute the gemtek may be to write the last written 47 * frequency || 0x10, but just writing 0x10 once seems to do it as well 48 */ 49 static void gemtek_mute(struct gemtek_device *dev) 50 { 51 if(dev->muted) 52 return; 53 spin_lock(&lock); 54 outb(0x10, io); 55 spin_unlock(&lock); 56 dev->muted = 1; 57 } 58 59 static void gemtek_unmute(struct gemtek_device *dev) 60 { 61 if(dev->muted == 0) 62 return; 63 spin_lock(&lock); 64 outb(0x20, io); 65 spin_unlock(&lock); 66 dev->muted = 0; 67 } 68 69 static void zero(void) 70 { 71 outb_p(0x04, io); 72 udelay(5); 73 outb_p(0x05, io); 74 udelay(5); 75 } 76 77 static void one(void) 78 { 79 outb_p(0x06, io); 80 udelay(5); 81 outb_p(0x07, io); 82 udelay(5); 83 } 84 85 static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) 86 { 87 int i; 88 89 /* freq = 78.25*((float)freq/16000.0 + 10.52); */ 90 91 freq /= 16; 92 freq += 10520; 93 freq *= 7825; 94 freq /= 100000; 95 96 spin_lock(&lock); 97 98 /* 2 start bits */ 99 outb_p(0x03, io); 100 udelay(5); 101 outb_p(0x07, io); 102 udelay(5); 103 104 /* 28 frequency bits (lsb first) */ 105 for (i = 0; i < 14; i++) 106 if (freq & (1 << i)) 107 one(); 108 else 109 zero(); 110 /* 36 unknown bits */ 111 for (i = 0; i < 11; i++) 112 zero(); 113 one(); 114 for (i = 0; i < 4; i++) 115 zero(); 116 one(); 117 zero(); 118 119 /* 2 end bits */ 120 outb_p(0x03, io); 121 udelay(5); 122 outb_p(0x07, io); 123 udelay(5); 124 125 spin_unlock(&lock); 126 127 return 0; 128 } 129 130 static int gemtek_getsigstr(struct gemtek_device *dev) 131 { 132 spin_lock(&lock); 133 inb(io); 134 udelay(5); 135 spin_unlock(&lock); 136 if (inb(io) & 8) /* bit set = no signal present */ 137 return 0; 138 return 1; /* signal present */ 139 } 140 141 static int gemtek_do_ioctl(struct inode *inode, struct file *file, 142 unsigned int cmd, void *arg) 143 { 144 struct video_device *dev = video_devdata(file); 145 struct gemtek_device *rt=dev->priv; 146 147 switch(cmd) 148 { 149 case VIDIOCGCAP: 150 { 151 struct video_capability *v = arg; 152 memset(v,0,sizeof(*v)); 153 v->type=VID_TYPE_TUNER; 154 v->channels=1; 155 v->audios=1; 156 strcpy(v->name, "GemTek"); 157 return 0; 158 } 159 case VIDIOCGTUNER: 160 { 161 struct video_tuner *v = arg; 162 if(v->tuner) /* Only 1 tuner */ 163 return -EINVAL; 164 v->rangelow=87*16000; 165 v->rangehigh=108*16000; 166 v->flags=VIDEO_TUNER_LOW; 167 v->mode=VIDEO_MODE_AUTO; 168 v->signal=0xFFFF*gemtek_getsigstr(rt); 169 strcpy(v->name, "FM"); 170 return 0; 171 } 172 case VIDIOCSTUNER: 173 { 174 struct video_tuner *v = arg; 175 if(v->tuner!=0) 176 return -EINVAL; 177 /* Only 1 tuner so no setting needed ! */ 178 return 0; 179 } 180 case VIDIOCGFREQ: 181 { 182 unsigned long *freq = arg; 183 *freq = rt->curfreq; 184 return 0; 185 } 186 case VIDIOCSFREQ: 187 { 188 unsigned long *freq = arg; 189 rt->curfreq = *freq; 190 /* needs to be called twice in order for getsigstr to work */ 191 gemtek_setfreq(rt, rt->curfreq); 192 gemtek_setfreq(rt, rt->curfreq); 193 return 0; 194 } 195 case VIDIOCGAUDIO: 196 { 197 struct video_audio *v = arg; 198 memset(v,0, sizeof(*v)); 199 v->flags|=VIDEO_AUDIO_MUTABLE; 200 v->volume=1; 201 v->step=65535; 202 strcpy(v->name, "Radio"); 203 return 0; 204 } 205 case VIDIOCSAUDIO: 206 { 207 struct video_audio *v = arg; 208 if(v->audio) 209 return -EINVAL; 210 211 if(v->flags&VIDEO_AUDIO_MUTE) 212 gemtek_mute(rt); 213 else 214 gemtek_unmute(rt); 215 216 return 0; 217 } 218 default: 219 return -ENOIOCTLCMD; 220 } 221 } 222 223 static int gemtek_ioctl(struct inode *inode, struct file *file, 224 unsigned int cmd, unsigned long arg) 225 { 226 return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl); 227 } 228 229 static struct gemtek_device gemtek_unit; 230 231 static struct file_operations gemtek_fops = { 232 .owner = THIS_MODULE, 233 .open = video_exclusive_open, 234 .release = video_exclusive_release, 235 .ioctl = gemtek_ioctl, 236 .llseek = no_llseek, 237 }; 238 239 static struct video_device gemtek_radio= 240 { 241 .owner = THIS_MODULE, 242 .name = "GemTek radio", 243 .type = VID_TYPE_TUNER, 244 .hardware = VID_HARDWARE_GEMTEK, 245 .fops = &gemtek_fops, 246 }; 247 248 static int __init gemtek_init(void) 249 { 250 if(io==-1) 251 { 252 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"); 253 return -EINVAL; 254 } 255 256 if (!request_region(io, 4, "gemtek")) 257 { 258 printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); 259 return -EBUSY; 260 } 261 262 gemtek_radio.priv=&gemtek_unit; 263 264 if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1) 265 { 266 release_region(io, 4); 267 return -EINVAL; 268 } 269 printk(KERN_INFO "GemTek Radio Card driver.\n"); 270 271 spin_lock_init(&lock); 272 273 /* this is _maybe_ unnecessary */ 274 outb(0x01, io); 275 276 /* mute card - prevents noisy bootups */ 277 gemtek_unit.muted = 0; 278 gemtek_mute(&gemtek_unit); 279 280 return 0; 281 } 282 283 MODULE_AUTHOR("Jonas Munsin"); 284 MODULE_DESCRIPTION("A driver for the GemTek Radio Card"); 285 MODULE_LICENSE("GPL"); 286 287 module_param(io, int, 0); 288 MODULE_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))."); 289 module_param(radio_nr, int, 0); 290 291 static void __exit gemtek_cleanup(void) 292 { 293 video_unregister_device(&gemtek_radio); 294 release_region(io,4); 295 } 296 297 module_init(gemtek_init); 298 module_exit(gemtek_cleanup); 299 300 /* 301 Local variables: 302 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" 303 End: 304 */ 305