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 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 17 */ 18 19 #include <linux/module.h> /* Modules */ 20 #include <linux/init.h> /* Initdata */ 21 #include <linux/ioport.h> /* request_region */ 22 #include <linux/delay.h> /* udelay */ 23 #include <asm/io.h> /* outb, outb_p */ 24 #include <asm/uaccess.h> /* copy to/from user */ 25 #include <linux/videodev2.h> /* kernel radio structs */ 26 #include <media/v4l2-common.h> 27 #include <linux/spinlock.h> 28 29 #include <linux/version.h> /* for KERNEL_VERSION MACRO */ 30 #define RADIO_VERSION KERNEL_VERSION(0,0,2) 31 32 static struct v4l2_queryctrl radio_qctrl[] = { 33 { 34 .id = V4L2_CID_AUDIO_MUTE, 35 .name = "Mute", 36 .minimum = 0, 37 .maximum = 1, 38 .default_value = 1, 39 .type = V4L2_CTRL_TYPE_BOOLEAN, 40 },{ 41 .id = V4L2_CID_AUDIO_VOLUME, 42 .name = "Volume", 43 .minimum = 0, 44 .maximum = 65535, 45 .step = 65535, 46 .default_value = 0xff, 47 .type = V4L2_CTRL_TYPE_INTEGER, 48 } 49 }; 50 51 #ifndef CONFIG_RADIO_GEMTEK_PORT 52 #define CONFIG_RADIO_GEMTEK_PORT -1 53 #endif 54 55 static int io = CONFIG_RADIO_GEMTEK_PORT; 56 static int radio_nr = -1; 57 static spinlock_t lock; 58 59 struct gemtek_device 60 { 61 int port; 62 unsigned long curfreq; 63 int muted; 64 }; 65 66 67 /* local things */ 68 69 /* the correct way to mute the gemtek may be to write the last written 70 * frequency || 0x10, but just writing 0x10 once seems to do it as well 71 */ 72 static void gemtek_mute(struct gemtek_device *dev) 73 { 74 if(dev->muted) 75 return; 76 spin_lock(&lock); 77 outb(0x10, io); 78 spin_unlock(&lock); 79 dev->muted = 1; 80 } 81 82 static void gemtek_unmute(struct gemtek_device *dev) 83 { 84 if(dev->muted == 0) 85 return; 86 spin_lock(&lock); 87 outb(0x20, io); 88 spin_unlock(&lock); 89 dev->muted = 0; 90 } 91 92 static void zero(void) 93 { 94 outb_p(0x04, io); 95 udelay(5); 96 outb_p(0x05, io); 97 udelay(5); 98 } 99 100 static void one(void) 101 { 102 outb_p(0x06, io); 103 udelay(5); 104 outb_p(0x07, io); 105 udelay(5); 106 } 107 108 static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) 109 { 110 int i; 111 112 /* freq = 78.25*((float)freq/16000.0 + 10.52); */ 113 114 freq /= 16; 115 freq += 10520; 116 freq *= 7825; 117 freq /= 100000; 118 119 spin_lock(&lock); 120 121 /* 2 start bits */ 122 outb_p(0x03, io); 123 udelay(5); 124 outb_p(0x07, io); 125 udelay(5); 126 127 /* 28 frequency bits (lsb first) */ 128 for (i = 0; i < 14; i++) 129 if (freq & (1 << i)) 130 one(); 131 else 132 zero(); 133 /* 36 unknown bits */ 134 for (i = 0; i < 11; i++) 135 zero(); 136 one(); 137 for (i = 0; i < 4; i++) 138 zero(); 139 one(); 140 zero(); 141 142 /* 2 end bits */ 143 outb_p(0x03, io); 144 udelay(5); 145 outb_p(0x07, io); 146 udelay(5); 147 148 spin_unlock(&lock); 149 150 return 0; 151 } 152 153 static int gemtek_getsigstr(struct gemtek_device *dev) 154 { 155 spin_lock(&lock); 156 inb(io); 157 udelay(5); 158 spin_unlock(&lock); 159 if (inb(io) & 8) /* bit set = no signal present */ 160 return 0; 161 return 1; /* signal present */ 162 } 163 164 static int vidioc_querycap(struct file *file, void *priv, 165 struct v4l2_capability *v) 166 { 167 strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); 168 strlcpy(v->card, "GemTek", sizeof(v->card)); 169 sprintf(v->bus_info, "ISA"); 170 v->version = RADIO_VERSION; 171 v->capabilities = V4L2_CAP_TUNER; 172 return 0; 173 } 174 175 static int vidioc_g_tuner(struct file *file, void *priv, 176 struct v4l2_tuner *v) 177 { 178 struct video_device *dev = video_devdata(file); 179 struct gemtek_device *rt = dev->priv; 180 181 if (v->index > 0) 182 return -EINVAL; 183 184 strcpy(v->name, "FM"); 185 v->type = V4L2_TUNER_RADIO; 186 v->rangelow = (87*16000); 187 v->rangehigh = (108*16000); 188 v->rxsubchans = V4L2_TUNER_SUB_MONO; 189 v->capability = V4L2_TUNER_CAP_LOW; 190 v->audmode = V4L2_TUNER_MODE_MONO; 191 v->signal = 0xffff*gemtek_getsigstr(rt); 192 return 0; 193 } 194 195 static int vidioc_s_tuner(struct file *file, void *priv, 196 struct v4l2_tuner *v) 197 { 198 if (v->index > 0) 199 return -EINVAL; 200 return 0; 201 } 202 203 static int vidioc_s_frequency(struct file *file, void *priv, 204 struct v4l2_frequency *f) 205 { 206 struct video_device *dev = video_devdata(file); 207 struct gemtek_device *rt = dev->priv; 208 209 rt->curfreq = f->frequency; 210 /* needs to be called twice in order for getsigstr to work */ 211 gemtek_setfreq(rt, rt->curfreq); 212 gemtek_setfreq(rt, rt->curfreq); 213 return 0; 214 } 215 216 static int vidioc_g_frequency(struct file *file, void *priv, 217 struct v4l2_frequency *f) 218 { 219 struct video_device *dev = video_devdata(file); 220 struct gemtek_device *rt = dev->priv; 221 222 f->type = V4L2_TUNER_RADIO; 223 f->frequency = rt->curfreq; 224 return 0; 225 } 226 227 static int vidioc_queryctrl(struct file *file, void *priv, 228 struct v4l2_queryctrl *qc) 229 { 230 int i; 231 232 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 233 if (qc->id && qc->id == radio_qctrl[i].id) { 234 memcpy(qc, &(radio_qctrl[i]), 235 sizeof(*qc)); 236 return 0; 237 } 238 } 239 return -EINVAL; 240 } 241 242 static int vidioc_g_ctrl(struct file *file, void *priv, 243 struct v4l2_control *ctrl) 244 { 245 struct video_device *dev = video_devdata(file); 246 struct gemtek_device *rt = dev->priv; 247 248 switch (ctrl->id) { 249 case V4L2_CID_AUDIO_MUTE: 250 ctrl->value = rt->muted; 251 return 0; 252 case V4L2_CID_AUDIO_VOLUME: 253 if (rt->muted) 254 ctrl->value = 0; 255 else 256 ctrl->value = 65535; 257 return 0; 258 } 259 return -EINVAL; 260 } 261 262 static int vidioc_s_ctrl(struct file *file, void *priv, 263 struct v4l2_control *ctrl) 264 { 265 struct video_device *dev = video_devdata(file); 266 struct gemtek_device *rt = dev->priv; 267 268 switch (ctrl->id) { 269 case V4L2_CID_AUDIO_MUTE: 270 if (ctrl->value) 271 gemtek_mute(rt); 272 else 273 gemtek_unmute(rt); 274 return 0; 275 case V4L2_CID_AUDIO_VOLUME: 276 if (ctrl->value) 277 gemtek_unmute(rt); 278 else 279 gemtek_mute(rt); 280 return 0; 281 } 282 return -EINVAL; 283 } 284 285 static int vidioc_g_audio (struct file *file, void *priv, 286 struct v4l2_audio *a) 287 { 288 if (a->index > 1) 289 return -EINVAL; 290 291 strcpy(a->name, "Radio"); 292 a->capability = V4L2_AUDCAP_STEREO; 293 return 0; 294 } 295 296 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 297 { 298 *i = 0; 299 return 0; 300 } 301 302 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 303 { 304 if (i != 0) 305 return -EINVAL; 306 return 0; 307 } 308 309 static int vidioc_s_audio(struct file *file, void *priv, 310 struct v4l2_audio *a) 311 { 312 if (a->index != 0) 313 return -EINVAL; 314 return 0; 315 } 316 317 static struct gemtek_device gemtek_unit; 318 319 static const struct file_operations gemtek_fops = { 320 .owner = THIS_MODULE, 321 .open = video_exclusive_open, 322 .release = video_exclusive_release, 323 .ioctl = video_ioctl2, 324 .compat_ioctl = v4l_compat_ioctl32, 325 .llseek = no_llseek, 326 }; 327 328 static struct video_device gemtek_radio= 329 { 330 .owner = THIS_MODULE, 331 .name = "GemTek radio", 332 .type = VID_TYPE_TUNER, 333 .hardware = 0, 334 .fops = &gemtek_fops, 335 .vidioc_querycap = vidioc_querycap, 336 .vidioc_g_tuner = vidioc_g_tuner, 337 .vidioc_s_tuner = vidioc_s_tuner, 338 .vidioc_g_audio = vidioc_g_audio, 339 .vidioc_s_audio = vidioc_s_audio, 340 .vidioc_g_input = vidioc_g_input, 341 .vidioc_s_input = vidioc_s_input, 342 .vidioc_g_frequency = vidioc_g_frequency, 343 .vidioc_s_frequency = vidioc_s_frequency, 344 .vidioc_queryctrl = vidioc_queryctrl, 345 .vidioc_g_ctrl = vidioc_g_ctrl, 346 .vidioc_s_ctrl = vidioc_s_ctrl, 347 }; 348 349 static int __init gemtek_init(void) 350 { 351 if(io==-1) 352 { 353 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"); 354 return -EINVAL; 355 } 356 357 if (!request_region(io, 4, "gemtek")) 358 { 359 printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); 360 return -EBUSY; 361 } 362 363 gemtek_radio.priv=&gemtek_unit; 364 365 if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1) 366 { 367 release_region(io, 4); 368 return -EINVAL; 369 } 370 printk(KERN_INFO "GemTek Radio Card driver.\n"); 371 372 spin_lock_init(&lock); 373 374 /* this is _maybe_ unnecessary */ 375 outb(0x01, io); 376 377 /* mute card - prevents noisy bootups */ 378 gemtek_unit.muted = 0; 379 gemtek_mute(&gemtek_unit); 380 381 return 0; 382 } 383 384 MODULE_AUTHOR("Jonas Munsin"); 385 MODULE_DESCRIPTION("A driver for the GemTek Radio Card"); 386 MODULE_LICENSE("GPL"); 387 388 module_param(io, int, 0); 389 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))."); 390 module_param(radio_nr, int, 0); 391 392 static void __exit gemtek_cleanup(void) 393 { 394 video_unregister_device(&gemtek_radio); 395 release_region(io,4); 396 } 397 398 module_init(gemtek_init); 399 module_exit(gemtek_cleanup); 400 401 /* 402 Local variables: 403 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" 404 End: 405 */ 406