radio-rtrack2.c revision 7d7b4b635298fcf4c205a89486eb5f397c130f5b
1/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff 2 * 3 * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood 4 * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> 5 * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> 6 * 7 * TODO: Allow for more than one of these foolish entities :-) 8 * 9 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 10 */ 11 12#include <linux/module.h> /* Modules */ 13#include <linux/init.h> /* Initdata */ 14#include <linux/ioport.h> /* request_region */ 15#include <linux/delay.h> /* udelay */ 16#include <linux/videodev2.h> /* kernel radio structs */ 17#include <linux/mutex.h> 18#include <linux/version.h> /* for KERNEL_VERSION MACRO */ 19#include <linux/io.h> /* outb, outb_p */ 20#include <linux/uaccess.h> /* copy to/from user */ 21#include <media/v4l2-device.h> 22#include <media/v4l2-ioctl.h> 23 24MODULE_AUTHOR("Ben Pfaff"); 25MODULE_DESCRIPTION("A driver for the RadioTrack II radio card."); 26MODULE_LICENSE("GPL"); 27 28#ifndef CONFIG_RADIO_RTRACK2_PORT 29#define CONFIG_RADIO_RTRACK2_PORT -1 30#endif 31 32static int io = CONFIG_RADIO_RTRACK2_PORT; 33static int radio_nr = -1; 34 35module_param(io, int, 0); 36MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)"); 37module_param(radio_nr, int, 0); 38 39#define RADIO_VERSION KERNEL_VERSION(0, 0, 2) 40 41struct rtrack2 42{ 43 struct v4l2_device v4l2_dev; 44 struct video_device vdev; 45 int io; 46 unsigned long curfreq; 47 int muted; 48 struct mutex lock; 49}; 50 51static struct rtrack2 rtrack2_card; 52 53 54/* local things */ 55 56static void rt_mute(struct rtrack2 *dev) 57{ 58 if (dev->muted) 59 return; 60 mutex_lock(&dev->lock); 61 outb(1, dev->io); 62 mutex_unlock(&dev->lock); 63 dev->muted = 1; 64} 65 66static void rt_unmute(struct rtrack2 *dev) 67{ 68 if(dev->muted == 0) 69 return; 70 mutex_lock(&dev->lock); 71 outb(0, dev->io); 72 mutex_unlock(&dev->lock); 73 dev->muted = 0; 74} 75 76static void zero(struct rtrack2 *dev) 77{ 78 outb_p(1, dev->io); 79 outb_p(3, dev->io); 80 outb_p(1, dev->io); 81} 82 83static void one(struct rtrack2 *dev) 84{ 85 outb_p(5, dev->io); 86 outb_p(7, dev->io); 87 outb_p(5, dev->io); 88} 89 90static int rt_setfreq(struct rtrack2 *dev, unsigned long freq) 91{ 92 int i; 93 94 mutex_lock(&dev->lock); 95 dev->curfreq = freq; 96 freq = freq / 200 + 856; 97 98 outb_p(0xc8, dev->io); 99 outb_p(0xc9, dev->io); 100 outb_p(0xc9, dev->io); 101 102 for (i = 0; i < 10; i++) 103 zero(dev); 104 105 for (i = 14; i >= 0; i--) 106 if (freq & (1 << i)) 107 one(dev); 108 else 109 zero(dev); 110 111 outb_p(0xc8, dev->io); 112 if (!dev->muted) 113 outb_p(0, dev->io); 114 115 mutex_unlock(&dev->lock); 116 return 0; 117} 118 119static int vidioc_querycap(struct file *file, void *priv, 120 struct v4l2_capability *v) 121{ 122 strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver)); 123 strlcpy(v->card, "RadioTrack II", sizeof(v->card)); 124 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); 125 v->version = RADIO_VERSION; 126 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; 127 return 0; 128} 129 130static int vidioc_s_tuner(struct file *file, void *priv, 131 struct v4l2_tuner *v) 132{ 133 return v->index ? -EINVAL : 0; 134} 135 136static int rt_getsigstr(struct rtrack2 *dev) 137{ 138 int sig = 1; 139 140 mutex_lock(&dev->lock); 141 if (inb(dev->io) & 2) /* bit set = no signal present */ 142 sig = 0; 143 mutex_unlock(&dev->lock); 144 return sig; 145} 146 147static int vidioc_g_tuner(struct file *file, void *priv, 148 struct v4l2_tuner *v) 149{ 150 struct rtrack2 *rt = video_drvdata(file); 151 152 if (v->index > 0) 153 return -EINVAL; 154 155 strlcpy(v->name, "FM", sizeof(v->name)); 156 v->type = V4L2_TUNER_RADIO; 157 v->rangelow = 88 * 16000; 158 v->rangehigh = 108 * 16000; 159 v->rxsubchans = V4L2_TUNER_SUB_MONO; 160 v->capability = V4L2_TUNER_CAP_LOW; 161 v->audmode = V4L2_TUNER_MODE_MONO; 162 v->signal = 0xFFFF * rt_getsigstr(rt); 163 return 0; 164} 165 166static int vidioc_s_frequency(struct file *file, void *priv, 167 struct v4l2_frequency *f) 168{ 169 struct rtrack2 *rt = video_drvdata(file); 170 171 rt_setfreq(rt, f->frequency); 172 return 0; 173} 174 175static int vidioc_g_frequency(struct file *file, void *priv, 176 struct v4l2_frequency *f) 177{ 178 struct rtrack2 *rt = video_drvdata(file); 179 180 f->type = V4L2_TUNER_RADIO; 181 f->frequency = rt->curfreq; 182 return 0; 183} 184 185static int vidioc_queryctrl(struct file *file, void *priv, 186 struct v4l2_queryctrl *qc) 187{ 188 switch (qc->id) { 189 case V4L2_CID_AUDIO_MUTE: 190 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); 191 case V4L2_CID_AUDIO_VOLUME: 192 return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535); 193 } 194 return -EINVAL; 195} 196 197static int vidioc_g_ctrl(struct file *file, void *priv, 198 struct v4l2_control *ctrl) 199{ 200 struct rtrack2 *rt = video_drvdata(file); 201 202 switch (ctrl->id) { 203 case V4L2_CID_AUDIO_MUTE: 204 ctrl->value = rt->muted; 205 return 0; 206 case V4L2_CID_AUDIO_VOLUME: 207 if (rt->muted) 208 ctrl->value = 0; 209 else 210 ctrl->value = 65535; 211 return 0; 212 } 213 return -EINVAL; 214} 215 216static int vidioc_s_ctrl(struct file *file, void *priv, 217 struct v4l2_control *ctrl) 218{ 219 struct rtrack2 *rt = video_drvdata(file); 220 221 switch (ctrl->id) { 222 case V4L2_CID_AUDIO_MUTE: 223 if (ctrl->value) 224 rt_mute(rt); 225 else 226 rt_unmute(rt); 227 return 0; 228 case V4L2_CID_AUDIO_VOLUME: 229 if (ctrl->value) 230 rt_unmute(rt); 231 else 232 rt_mute(rt); 233 return 0; 234 } 235 return -EINVAL; 236} 237 238static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 239{ 240 *i = 0; 241 return 0; 242} 243 244static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 245{ 246 return i ? -EINVAL : 0; 247} 248 249static int vidioc_g_audio(struct file *file, void *priv, 250 struct v4l2_audio *a) 251{ 252 a->index = 0; 253 strlcpy(a->name, "Radio", sizeof(a->name)); 254 a->capability = V4L2_AUDCAP_STEREO; 255 return 0; 256} 257 258static int vidioc_s_audio(struct file *file, void *priv, 259 struct v4l2_audio *a) 260{ 261 return a->index ? -EINVAL : 0; 262} 263 264static int rtrack2_open(struct file *file) 265{ 266 return 0; 267} 268 269static int rtrack2_release(struct file *file) 270{ 271 return 0; 272} 273 274static const struct v4l2_file_operations rtrack2_fops = { 275 .owner = THIS_MODULE, 276 .open = rtrack2_open, 277 .release = rtrack2_release, 278 .ioctl = video_ioctl2, 279}; 280 281static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = { 282 .vidioc_querycap = vidioc_querycap, 283 .vidioc_g_tuner = vidioc_g_tuner, 284 .vidioc_s_tuner = vidioc_s_tuner, 285 .vidioc_g_frequency = vidioc_g_frequency, 286 .vidioc_s_frequency = vidioc_s_frequency, 287 .vidioc_queryctrl = vidioc_queryctrl, 288 .vidioc_g_ctrl = vidioc_g_ctrl, 289 .vidioc_s_ctrl = vidioc_s_ctrl, 290 .vidioc_g_audio = vidioc_g_audio, 291 .vidioc_s_audio = vidioc_s_audio, 292 .vidioc_g_input = vidioc_g_input, 293 .vidioc_s_input = vidioc_s_input, 294}; 295 296static int __init rtrack2_init(void) 297{ 298 struct rtrack2 *dev = &rtrack2_card; 299 struct v4l2_device *v4l2_dev = &dev->v4l2_dev; 300 int res; 301 302 strlcpy(v4l2_dev->name, "rtrack2", sizeof(v4l2_dev->name)); 303 dev->io = io; 304 if (dev->io == -1) { 305 v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or io=0x30c\n"); 306 return -EINVAL; 307 } 308 if (!request_region(dev->io, 4, "rtrack2")) { 309 v4l2_err(v4l2_dev, "port 0x%x already in use\n", dev->io); 310 return -EBUSY; 311 } 312 313 res = v4l2_device_register(NULL, v4l2_dev); 314 if (res < 0) { 315 release_region(dev->io, 4); 316 v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); 317 return res; 318 } 319 320 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); 321 dev->vdev.v4l2_dev = v4l2_dev; 322 dev->vdev.fops = &rtrack2_fops; 323 dev->vdev.ioctl_ops = &rtrack2_ioctl_ops; 324 dev->vdev.release = video_device_release_empty; 325 video_set_drvdata(&dev->vdev, dev); 326 327 mutex_init(&dev->lock); 328 if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { 329 v4l2_device_unregister(v4l2_dev); 330 release_region(dev->io, 4); 331 return -EINVAL; 332 } 333 334 v4l2_info(v4l2_dev, "AIMSlab Radiotrack II card driver.\n"); 335 336 /* mute card - prevents noisy bootups */ 337 outb(1, dev->io); 338 dev->muted = 1; 339 340 return 0; 341} 342 343static void __exit rtrack2_exit(void) 344{ 345 struct rtrack2 *dev = &rtrack2_card; 346 347 video_unregister_device(&dev->vdev); 348 v4l2_device_unregister(&dev->v4l2_dev); 349 release_region(dev->io, 4); 350} 351 352module_init(rtrack2_init); 353module_exit(rtrack2_exit); 354