linux/drivers/media/radio/radio-rtrack2.c
<<
>>
Prefs
   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.Cox@linux.org>
   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 <asm/io.h>             /* outb, outb_p                 */
  17#include <asm/uaccess.h>        /* copy to/from user            */
  18#include <linux/videodev2.h>    /* kernel radio structs         */
  19#include <media/v4l2-common.h>
  20#include <media/v4l2-ioctl.h>
  21#include <linux/spinlock.h>
  22
  23#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
  24#define RADIO_VERSION KERNEL_VERSION(0,0,2)
  25
  26static struct v4l2_queryctrl radio_qctrl[] = {
  27        {
  28                .id            = V4L2_CID_AUDIO_MUTE,
  29                .name          = "Mute",
  30                .minimum       = 0,
  31                .maximum       = 1,
  32                .default_value = 1,
  33                .type          = V4L2_CTRL_TYPE_BOOLEAN,
  34        },{
  35                .id            = V4L2_CID_AUDIO_VOLUME,
  36                .name          = "Volume",
  37                .minimum       = 0,
  38                .maximum       = 65535,
  39                .step          = 65535,
  40                .default_value = 0xff,
  41                .type          = V4L2_CTRL_TYPE_INTEGER,
  42        }
  43};
  44
  45#ifndef CONFIG_RADIO_RTRACK2_PORT
  46#define CONFIG_RADIO_RTRACK2_PORT -1
  47#endif
  48
  49static int io = CONFIG_RADIO_RTRACK2_PORT;
  50static int radio_nr = -1;
  51static spinlock_t lock;
  52
  53struct rt_device
  54{
  55        unsigned long in_use;
  56        int port;
  57        unsigned long curfreq;
  58        int muted;
  59};
  60
  61
  62/* local things */
  63
  64static void rt_mute(struct rt_device *dev)
  65{
  66        if(dev->muted)
  67                return;
  68        spin_lock(&lock);
  69        outb(1, io);
  70        spin_unlock(&lock);
  71        dev->muted = 1;
  72}
  73
  74static void rt_unmute(struct rt_device *dev)
  75{
  76        if(dev->muted == 0)
  77                return;
  78        spin_lock(&lock);
  79        outb(0, io);
  80        spin_unlock(&lock);
  81        dev->muted = 0;
  82}
  83
  84static void zero(void)
  85{
  86        outb_p(1, io);
  87        outb_p(3, io);
  88        outb_p(1, io);
  89}
  90
  91static void one(void)
  92{
  93        outb_p(5, io);
  94        outb_p(7, io);
  95        outb_p(5, io);
  96}
  97
  98static int rt_setfreq(struct rt_device *dev, unsigned long freq)
  99{
 100        int i;
 101
 102        freq = freq / 200 + 856;
 103
 104        spin_lock(&lock);
 105
 106        outb_p(0xc8, io);
 107        outb_p(0xc9, io);
 108        outb_p(0xc9, io);
 109
 110        for (i = 0; i < 10; i++)
 111                zero ();
 112
 113        for (i = 14; i >= 0; i--)
 114                if (freq & (1 << i))
 115                        one ();
 116                else
 117                        zero ();
 118
 119        outb_p(0xc8, io);
 120        if (!dev->muted)
 121                outb_p(0, io);
 122
 123        spin_unlock(&lock);
 124        return 0;
 125}
 126
 127static int vidioc_querycap(struct file *file, void *priv,
 128                                struct v4l2_capability *v)
 129{
 130        strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
 131        strlcpy(v->card, "RadioTrack II", sizeof(v->card));
 132        sprintf(v->bus_info, "ISA");
 133        v->version = RADIO_VERSION;
 134        v->capabilities = V4L2_CAP_TUNER;
 135        return 0;
 136}
 137
 138static int vidioc_s_tuner(struct file *file, void *priv,
 139                                struct v4l2_tuner *v)
 140{
 141        if (v->index > 0)
 142                return -EINVAL;
 143
 144        return 0;
 145}
 146
 147static int rt_getsigstr(struct rt_device *dev)
 148{
 149        if (inb(io) & 2)        /* bit set = no signal present  */
 150                return 0;
 151        return 1;               /* signal present               */
 152}
 153
 154static int vidioc_g_tuner(struct file *file, void *priv,
 155                                struct v4l2_tuner *v)
 156{
 157        struct rt_device *rt = video_drvdata(file);
 158
 159        if (v->index > 0)
 160                return -EINVAL;
 161
 162        strcpy(v->name, "FM");
 163        v->type = V4L2_TUNER_RADIO;
 164        v->rangelow = (88*16000);
 165        v->rangehigh = (108*16000);
 166        v->rxsubchans = V4L2_TUNER_SUB_MONO;
 167        v->capability = V4L2_TUNER_CAP_LOW;
 168        v->audmode = V4L2_TUNER_MODE_MONO;
 169        v->signal = 0xFFFF*rt_getsigstr(rt);
 170        return 0;
 171}
 172
 173static int vidioc_s_frequency(struct file *file, void *priv,
 174                                struct v4l2_frequency *f)
 175{
 176        struct rt_device *rt = video_drvdata(file);
 177
 178        rt->curfreq = f->frequency;
 179        rt_setfreq(rt, rt->curfreq);
 180        return 0;
 181}
 182
 183static int vidioc_g_frequency(struct file *file, void *priv,
 184                                struct v4l2_frequency *f)
 185{
 186        struct rt_device *rt = video_drvdata(file);
 187
 188        f->type = V4L2_TUNER_RADIO;
 189        f->frequency = rt->curfreq;
 190        return 0;
 191}
 192
 193static int vidioc_queryctrl(struct file *file, void *priv,
 194                                struct v4l2_queryctrl *qc)
 195{
 196        int i;
 197
 198        for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
 199                if (qc->id && qc->id == radio_qctrl[i].id) {
 200                        memcpy(qc, &(radio_qctrl[i]),
 201                                                sizeof(*qc));
 202                        return 0;
 203                }
 204        }
 205        return -EINVAL;
 206}
 207
 208static int vidioc_g_ctrl(struct file *file, void *priv,
 209                                struct v4l2_control *ctrl)
 210{
 211        struct rt_device *rt = video_drvdata(file);
 212
 213        switch (ctrl->id) {
 214        case V4L2_CID_AUDIO_MUTE:
 215                ctrl->value = rt->muted;
 216                return 0;
 217        case V4L2_CID_AUDIO_VOLUME:
 218                if (rt->muted)
 219                        ctrl->value = 0;
 220                else
 221                        ctrl->value = 65535;
 222                return 0;
 223        }
 224        return -EINVAL;
 225}
 226
 227static int vidioc_s_ctrl(struct file *file, void *priv,
 228                                struct v4l2_control *ctrl)
 229{
 230        struct rt_device *rt = video_drvdata(file);
 231
 232        switch (ctrl->id) {
 233        case V4L2_CID_AUDIO_MUTE:
 234                if (ctrl->value)
 235                        rt_mute(rt);
 236                else
 237                        rt_unmute(rt);
 238                return 0;
 239        case V4L2_CID_AUDIO_VOLUME:
 240                if (ctrl->value)
 241                        rt_unmute(rt);
 242                else
 243                        rt_mute(rt);
 244                return 0;
 245        }
 246        return -EINVAL;
 247}
 248
 249static int vidioc_g_audio(struct file *file, void *priv,
 250                                struct v4l2_audio *a)
 251{
 252        if (a->index > 1)
 253                return -EINVAL;
 254
 255        strcpy(a->name, "Radio");
 256        a->capability = V4L2_AUDCAP_STEREO;
 257        return 0;
 258}
 259
 260static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
 261{
 262        *i = 0;
 263        return 0;
 264}
 265
 266static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
 267{
 268        if (i != 0)
 269                return -EINVAL;
 270        return 0;
 271}
 272
 273static int vidioc_s_audio(struct file *file, void *priv,
 274                                struct v4l2_audio *a)
 275{
 276        if (a->index != 0)
 277                return -EINVAL;
 278        return 0;
 279}
 280
 281static struct rt_device rtrack2_unit;
 282
 283static int rtrack2_exclusive_open(struct inode *inode, struct file *file)
 284{
 285        return test_and_set_bit(0, &rtrack2_unit.in_use) ? -EBUSY : 0;
 286}
 287
 288static int rtrack2_exclusive_release(struct inode *inode, struct file *file)
 289{
 290        clear_bit(0, &rtrack2_unit.in_use);
 291        return 0;
 292}
 293
 294static const struct file_operations rtrack2_fops = {
 295        .owner          = THIS_MODULE,
 296        .open           = rtrack2_exclusive_open,
 297        .release        = rtrack2_exclusive_release,
 298        .ioctl          = video_ioctl2,
 299#ifdef CONFIG_COMPAT
 300        .compat_ioctl   = v4l_compat_ioctl32,
 301#endif
 302        .llseek         = no_llseek,
 303};
 304
 305static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = {
 306        .vidioc_querycap    = vidioc_querycap,
 307        .vidioc_g_tuner     = vidioc_g_tuner,
 308        .vidioc_s_tuner     = vidioc_s_tuner,
 309        .vidioc_g_frequency = vidioc_g_frequency,
 310        .vidioc_s_frequency = vidioc_s_frequency,
 311        .vidioc_queryctrl   = vidioc_queryctrl,
 312        .vidioc_g_ctrl      = vidioc_g_ctrl,
 313        .vidioc_s_ctrl      = vidioc_s_ctrl,
 314        .vidioc_g_audio     = vidioc_g_audio,
 315        .vidioc_s_audio     = vidioc_s_audio,
 316        .vidioc_g_input     = vidioc_g_input,
 317        .vidioc_s_input     = vidioc_s_input,
 318};
 319
 320static struct video_device rtrack2_radio = {
 321        .name           = "RadioTrack II radio",
 322        .fops           = &rtrack2_fops,
 323        .ioctl_ops      = &rtrack2_ioctl_ops,
 324        .release        = video_device_release_empty,
 325};
 326
 327static int __init rtrack2_init(void)
 328{
 329        if(io==-1)
 330        {
 331                printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n");
 332                return -EINVAL;
 333        }
 334        if (!request_region(io, 4, "rtrack2"))
 335        {
 336                printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io);
 337                return -EBUSY;
 338        }
 339
 340        video_set_drvdata(&rtrack2_radio, &rtrack2_unit);
 341
 342        spin_lock_init(&lock);
 343        if (video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
 344                release_region(io, 4);
 345                return -EINVAL;
 346        }
 347
 348        printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n");
 349
 350        /* mute card - prevents noisy bootups */
 351        outb(1, io);
 352        rtrack2_unit.muted = 1;
 353
 354        return 0;
 355}
 356
 357MODULE_AUTHOR("Ben Pfaff");
 358MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
 359MODULE_LICENSE("GPL");
 360
 361module_param(io, int, 0);
 362MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
 363module_param(radio_nr, int, 0);
 364
 365static void __exit rtrack2_cleanup_module(void)
 366{
 367        video_unregister_device(&rtrack2_radio);
 368        release_region(io,4);
 369}
 370
 371module_init(rtrack2_init);
 372module_exit(rtrack2_cleanup_module);
 373
 374/*
 375  Local variables:
 376  compile-command: "mmake"
 377  End:
 378*/
 379