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 gemtek_do_ioctl(struct inode *inode, struct file *file, 165 unsigned int cmd, void *arg) 166 { 167 struct video_device *dev = video_devdata(file); 168 struct gemtek_device *rt=dev->priv; 169 170 switch(cmd) 171 { 172 case VIDIOC_QUERYCAP: 173 { 174 struct v4l2_capability *v = arg; 175 memset(v,0,sizeof(*v)); 176 strlcpy(v->driver, "radio-gemtek", sizeof (v->driver)); 177 strlcpy(v->card, "GemTek", sizeof (v->card)); 178 sprintf(v->bus_info,"ISA"); 179 v->version = RADIO_VERSION; 180 v->capabilities = V4L2_CAP_TUNER; 181 182 return 0; 183 } 184 case VIDIOC_G_TUNER: 185 { 186 struct v4l2_tuner *v = arg; 187 188 if (v->index > 0) 189 return -EINVAL; 190 191 memset(v,0,sizeof(*v)); 192 strcpy(v->name, "FM"); 193 v->type = V4L2_TUNER_RADIO; 194 195 v->rangelow=(87*16000); 196 v->rangehigh=(108*16000); 197 v->rxsubchans =V4L2_TUNER_SUB_MONO; 198 v->capability=V4L2_TUNER_CAP_LOW; 199 v->audmode = V4L2_TUNER_MODE_MONO; 200 v->signal=0xFFFF*gemtek_getsigstr(rt); 201 202 return 0; 203 } 204 case VIDIOC_S_TUNER: 205 { 206 struct v4l2_tuner *v = arg; 207 208 if (v->index > 0) 209 return -EINVAL; 210 211 return 0; 212 } 213 case VIDIOC_S_FREQUENCY: 214 { 215 struct v4l2_frequency *f = arg; 216 217 rt->curfreq = f->frequency; 218 /* needs to be called twice in order for getsigstr to work */ 219 gemtek_setfreq(rt, rt->curfreq); 220 gemtek_setfreq(rt, rt->curfreq); 221 return 0; 222 } 223 case VIDIOC_G_FREQUENCY: 224 { 225 struct v4l2_frequency *f = arg; 226 227 f->type = V4L2_TUNER_RADIO; 228 f->frequency = rt->curfreq; 229 230 return 0; 231 } 232 case VIDIOC_QUERYCTRL: 233 { 234 struct v4l2_queryctrl *qc = arg; 235 int i; 236 237 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 238 if (qc->id && qc->id == radio_qctrl[i].id) { 239 memcpy(qc, &(radio_qctrl[i]), 240 sizeof(*qc)); 241 return (0); 242 } 243 } 244 return -EINVAL; 245 } 246 case VIDIOC_G_CTRL: 247 { 248 struct v4l2_control *ctrl= arg; 249 250 switch (ctrl->id) { 251 case V4L2_CID_AUDIO_MUTE: 252 ctrl->value=rt->muted; 253 return (0); 254 case V4L2_CID_AUDIO_VOLUME: 255 if (rt->muted) 256 ctrl->value=0; 257 else 258 ctrl->value=65535; 259 return (0); 260 } 261 return -EINVAL; 262 } 263 case VIDIOC_S_CTRL: 264 { 265 struct v4l2_control *ctrl= arg; 266 267 switch (ctrl->id) { 268 case V4L2_CID_AUDIO_MUTE: 269 if (ctrl->value) { 270 gemtek_mute(rt); 271 } else { 272 gemtek_unmute(rt); 273 } 274 return (0); 275 case V4L2_CID_AUDIO_VOLUME: 276 if (ctrl->value) { 277 gemtek_unmute(rt); 278 } else { 279 gemtek_mute(rt); 280 } 281 return (0); 282 } 283 return -EINVAL; 284 } 285 default: 286 return v4l_compat_translate_ioctl(inode,file,cmd,arg, 287 gemtek_do_ioctl); 288 } 289 } 290 291 static int gemtek_ioctl(struct inode *inode, struct file *file, 292 unsigned int cmd, unsigned long arg) 293 { 294 return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl); 295 } 296 297 static struct gemtek_device gemtek_unit; 298 299 static const struct file_operations gemtek_fops = { 300 .owner = THIS_MODULE, 301 .open = video_exclusive_open, 302 .release = video_exclusive_release, 303 .ioctl = gemtek_ioctl, 304 .compat_ioctl = v4l_compat_ioctl32, 305 .llseek = no_llseek, 306 }; 307 308 static struct video_device gemtek_radio= 309 { 310 .owner = THIS_MODULE, 311 .name = "GemTek radio", 312 .type = VID_TYPE_TUNER, 313 .hardware = 0, 314 .fops = &gemtek_fops, 315 }; 316 317 static int __init gemtek_init(void) 318 { 319 if(io==-1) 320 { 321 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"); 322 return -EINVAL; 323 } 324 325 if (!request_region(io, 4, "gemtek")) 326 { 327 printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); 328 return -EBUSY; 329 } 330 331 gemtek_radio.priv=&gemtek_unit; 332 333 if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1) 334 { 335 release_region(io, 4); 336 return -EINVAL; 337 } 338 printk(KERN_INFO "GemTek Radio Card driver.\n"); 339 340 spin_lock_init(&lock); 341 342 /* this is _maybe_ unnecessary */ 343 outb(0x01, io); 344 345 /* mute card - prevents noisy bootups */ 346 gemtek_unit.muted = 0; 347 gemtek_mute(&gemtek_unit); 348 349 return 0; 350 } 351 352 MODULE_AUTHOR("Jonas Munsin"); 353 MODULE_DESCRIPTION("A driver for the GemTek Radio Card"); 354 MODULE_LICENSE("GPL"); 355 356 module_param(io, int, 0); 357 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))."); 358 module_param(radio_nr, int, 0); 359 360 static void __exit gemtek_cleanup(void) 361 { 362 video_unregister_device(&gemtek_radio); 363 release_region(io,4); 364 } 365 366 module_init(gemtek_init); 367 module_exit(gemtek_cleanup); 368 369 /* 370 Local variables: 371 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" 372 End: 373 */ 374