linux/drivers/media/radio/radio-gemtek-pci.c
<<
>>
Prefs
   1/*
   2 ***************************************************************************
   3 *
   4 *     radio-gemtek-pci.c - Gemtek PCI Radio driver
   5 *     (C) 2001 Vladimir Shebordaev <vshebordaev@mail.ru>
   6 *
   7 ***************************************************************************
   8 *
   9 *     This program is free software; you can redistribute it and/or
  10 *     modify it under the terms of the GNU General Public License as
  11 *     published by the Free Software Foundation; either version 2 of
  12 *     the License, or (at your option) any later version.
  13 *
  14 *     This program is distributed in the hope that it will be useful,
  15 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 *     GNU General Public License for more details.
  18 *
  19 *     You should have received a copy of the GNU General Public
  20 *     License along with this program; if not, write to the Free
  21 *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
  22 *     USA.
  23 *
  24 ***************************************************************************
  25 *
  26 *     Gemtek Corp still silently refuses to release any specifications
  27 *     of their multimedia devices, so the protocol still has to be
  28 *     reverse engineered.
  29 *
  30 *     The v4l code was inspired by Jonas Munsin's  Gemtek serial line
  31 *     radio device driver.
  32 *
  33 *     Please, let me know if this piece of code was useful :)
  34 *
  35 *     TODO: multiple device support and portability were not tested
  36 *
  37 *     Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
  38 *
  39 ***************************************************************************
  40 */
  41
  42#include <linux/types.h>
  43#include <linux/list.h>
  44#include <linux/module.h>
  45#include <linux/init.h>
  46#include <linux/pci.h>
  47#include <linux/videodev2.h>
  48#include <media/v4l2-common.h>
  49#include <media/v4l2-ioctl.h>
  50#include <linux/errno.h>
  51
  52#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
  53#define RADIO_VERSION KERNEL_VERSION(0,0,2)
  54
  55static struct v4l2_queryctrl radio_qctrl[] = {
  56        {
  57                .id            = V4L2_CID_AUDIO_MUTE,
  58                .name          = "Mute",
  59                .minimum       = 0,
  60                .maximum       = 1,
  61                .default_value = 1,
  62                .type          = V4L2_CTRL_TYPE_BOOLEAN,
  63        },{
  64                .id            = V4L2_CID_AUDIO_VOLUME,
  65                .name          = "Volume",
  66                .minimum       = 0,
  67                .maximum       = 65535,
  68                .step          = 65535,
  69                .default_value = 0xff,
  70                .type          = V4L2_CTRL_TYPE_INTEGER,
  71        }
  72};
  73
  74#include <asm/io.h>
  75#include <asm/uaccess.h>
  76
  77#ifndef PCI_VENDOR_ID_GEMTEK
  78#define PCI_VENDOR_ID_GEMTEK 0x5046
  79#endif
  80
  81#ifndef PCI_DEVICE_ID_GEMTEK_PR103
  82#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001
  83#endif
  84
  85#ifndef GEMTEK_PCI_RANGE_LOW
  86#define GEMTEK_PCI_RANGE_LOW (87*16000)
  87#endif
  88
  89#ifndef GEMTEK_PCI_RANGE_HIGH
  90#define GEMTEK_PCI_RANGE_HIGH (108*16000)
  91#endif
  92
  93struct gemtek_pci_card {
  94        struct video_device *videodev;
  95
  96        u32 iobase;
  97        u32 length;
  98
  99        u32 current_frequency;
 100        u8  mute;
 101};
 102
 103static int nr_radio = -1;
 104static unsigned long in_use;
 105
 106static inline u8 gemtek_pci_out( u16 value, u32 port )
 107{
 108        outw( value, port );
 109
 110        return (u8)value;
 111}
 112
 113#define _b0( v ) *((u8 *)&v)
 114static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep )
 115{
 116        register u8 byte = *last_byte;
 117
 118        if ( !value ) {
 119                if ( !keep )
 120                        value = (u16)port;
 121                byte &= 0xfd;
 122        } else
 123                byte |= 2;
 124
 125        _b0( value ) = byte;
 126        outw( value, port );
 127        byte |= 1;
 128        _b0( value ) = byte;
 129        outw( value, port );
 130        byte &= 0xfe;
 131        _b0( value ) = byte;
 132        outw( value, port );
 133
 134        *last_byte = byte;
 135}
 136
 137static inline void gemtek_pci_nil( u32 port, u8 *last_byte )
 138{
 139        __gemtek_pci_cmd( 0x00, port, last_byte, false );
 140}
 141
 142static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte )
 143{
 144        __gemtek_pci_cmd( cmd, port, last_byte, true );
 145}
 146
 147static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency )
 148{
 149        register int i;
 150        register u32 value = frequency / 200 + 856;
 151        register u16 mask = 0x8000;
 152        u8 last_byte;
 153        u32 port = card->iobase;
 154
 155        last_byte = gemtek_pci_out( 0x06, port );
 156
 157        i = 0;
 158        do {
 159                gemtek_pci_nil( port, &last_byte );
 160                i++;
 161        } while ( i < 9 );
 162
 163        i = 0;
 164        do {
 165                gemtek_pci_cmd( value & mask, port, &last_byte );
 166                mask >>= 1;
 167                i++;
 168        } while ( i < 16 );
 169
 170        outw( 0x10, port );
 171}
 172
 173
 174static inline void gemtek_pci_mute( struct gemtek_pci_card *card )
 175{
 176        outb( 0x1f, card->iobase );
 177        card->mute = true;
 178}
 179
 180static inline void gemtek_pci_unmute( struct gemtek_pci_card *card )
 181{
 182        if ( card->mute ) {
 183                gemtek_pci_setfrequency( card, card->current_frequency );
 184                card->mute = false;
 185        }
 186}
 187
 188static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
 189{
 190        return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
 191}
 192
 193static int vidioc_querycap(struct file *file, void *priv,
 194                                        struct v4l2_capability *v)
 195{
 196        strlcpy(v->driver, "radio-gemtek-pci", sizeof(v->driver));
 197        strlcpy(v->card, "GemTek PCI Radio", sizeof(v->card));
 198        sprintf(v->bus_info, "ISA");
 199        v->version = RADIO_VERSION;
 200        v->capabilities = V4L2_CAP_TUNER;
 201        return 0;
 202}
 203
 204static int vidioc_g_tuner(struct file *file, void *priv,
 205                                        struct v4l2_tuner *v)
 206{
 207        struct gemtek_pci_card *card = video_drvdata(file);
 208
 209        if (v->index > 0)
 210                return -EINVAL;
 211
 212        strcpy(v->name, "FM");
 213        v->type = V4L2_TUNER_RADIO;
 214        v->rangelow = GEMTEK_PCI_RANGE_LOW;
 215        v->rangehigh = GEMTEK_PCI_RANGE_HIGH;
 216        v->rxsubchans = V4L2_TUNER_SUB_MONO;
 217        v->capability = V4L2_TUNER_CAP_LOW;
 218        v->audmode = V4L2_TUNER_MODE_MONO;
 219        v->signal = 0xffff * gemtek_pci_getsignal(card);
 220        return 0;
 221}
 222
 223static int vidioc_s_tuner(struct file *file, void *priv,
 224                                        struct v4l2_tuner *v)
 225{
 226        if (v->index > 0)
 227                return -EINVAL;
 228        return 0;
 229}
 230
 231static int vidioc_s_frequency(struct file *file, void *priv,
 232                                        struct v4l2_frequency *f)
 233{
 234        struct gemtek_pci_card *card = video_drvdata(file);
 235
 236        if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
 237                        (f->frequency > GEMTEK_PCI_RANGE_HIGH) )
 238                return -EINVAL;
 239        gemtek_pci_setfrequency(card, f->frequency);
 240        card->current_frequency = f->frequency;
 241        card->mute = false;
 242        return 0;
 243}
 244
 245static int vidioc_g_frequency(struct file *file, void *priv,
 246                                        struct v4l2_frequency *f)
 247{
 248        struct gemtek_pci_card *card = video_drvdata(file);
 249
 250        f->type = V4L2_TUNER_RADIO;
 251        f->frequency = card->current_frequency;
 252        return 0;
 253}
 254
 255static int vidioc_queryctrl(struct file *file, void *priv,
 256                                        struct v4l2_queryctrl *qc)
 257{
 258        int i;
 259        for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
 260                if (qc->id && qc->id == radio_qctrl[i].id) {
 261                        memcpy(qc, &(radio_qctrl[i]),
 262                                                sizeof(*qc));
 263                        return 0;
 264                }
 265        }
 266        return -EINVAL;
 267}
 268
 269static int vidioc_g_ctrl(struct file *file, void *priv,
 270                                        struct v4l2_control *ctrl)
 271{
 272        struct gemtek_pci_card *card = video_drvdata(file);
 273
 274        switch (ctrl->id) {
 275        case V4L2_CID_AUDIO_MUTE:
 276                ctrl->value = card->mute;
 277                return 0;
 278        case V4L2_CID_AUDIO_VOLUME:
 279                if (card->mute)
 280                        ctrl->value = 0;
 281                else
 282                        ctrl->value = 65535;
 283                return 0;
 284        }
 285        return -EINVAL;
 286}
 287
 288static int vidioc_s_ctrl(struct file *file, void *priv,
 289                                        struct v4l2_control *ctrl)
 290{
 291        struct gemtek_pci_card *card = video_drvdata(file);
 292
 293        switch (ctrl->id) {
 294        case V4L2_CID_AUDIO_MUTE:
 295                if (ctrl->value)
 296                        gemtek_pci_mute(card);
 297                else
 298                        gemtek_pci_unmute(card);
 299                return 0;
 300        case V4L2_CID_AUDIO_VOLUME:
 301                if (ctrl->value)
 302                        gemtek_pci_unmute(card);
 303                else
 304                        gemtek_pci_mute(card);
 305                return 0;
 306        }
 307        return -EINVAL;
 308}
 309
 310static int vidioc_g_audio(struct file *file, void *priv,
 311                                        struct v4l2_audio *a)
 312{
 313        if (a->index > 1)
 314                return -EINVAL;
 315
 316        strcpy(a->name, "Radio");
 317        a->capability = V4L2_AUDCAP_STEREO;
 318        return 0;
 319}
 320
 321static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
 322{
 323        *i = 0;
 324        return 0;
 325}
 326
 327static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
 328{
 329        if (i != 0)
 330                return -EINVAL;
 331        return 0;
 332}
 333
 334static int vidioc_s_audio(struct file *file, void *priv,
 335                                        struct v4l2_audio *a)
 336{
 337        if (a->index != 0)
 338                return -EINVAL;
 339        return 0;
 340}
 341
 342enum {
 343        GEMTEK_PR103
 344};
 345
 346static char *card_names[] __devinitdata = {
 347        "GEMTEK_PR103"
 348};
 349
 350static struct pci_device_id gemtek_pci_id[] =
 351{
 352        { PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103,
 353          PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 },
 354        { 0 }
 355};
 356
 357MODULE_DEVICE_TABLE( pci, gemtek_pci_id );
 358
 359static int mx = 1;
 360
 361static int gemtek_pci_exclusive_open(struct inode *inode, struct file *file)
 362{
 363        return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
 364}
 365
 366static int gemtek_pci_exclusive_release(struct inode *inode, struct file *file)
 367{
 368        clear_bit(0, &in_use);
 369        return 0;
 370}
 371
 372static const struct file_operations gemtek_pci_fops = {
 373        .owner          = THIS_MODULE,
 374        .open           = gemtek_pci_exclusive_open,
 375        .release        = gemtek_pci_exclusive_release,
 376        .ioctl          = video_ioctl2,
 377#ifdef CONFIG_COMPAT
 378        .compat_ioctl   = v4l_compat_ioctl32,
 379#endif
 380        .llseek         = no_llseek,
 381};
 382
 383static const struct v4l2_ioctl_ops gemtek_pci_ioctl_ops = {
 384        .vidioc_querycap    = vidioc_querycap,
 385        .vidioc_g_tuner     = vidioc_g_tuner,
 386        .vidioc_s_tuner     = vidioc_s_tuner,
 387        .vidioc_g_audio     = vidioc_g_audio,
 388        .vidioc_s_audio     = vidioc_s_audio,
 389        .vidioc_g_input     = vidioc_g_input,
 390        .vidioc_s_input     = vidioc_s_input,
 391        .vidioc_g_frequency = vidioc_g_frequency,
 392        .vidioc_s_frequency = vidioc_s_frequency,
 393        .vidioc_queryctrl   = vidioc_queryctrl,
 394        .vidioc_g_ctrl      = vidioc_g_ctrl,
 395        .vidioc_s_ctrl      = vidioc_s_ctrl,
 396};
 397
 398static struct video_device vdev_template = {
 399        .name           = "Gemtek PCI Radio",
 400        .fops           = &gemtek_pci_fops,
 401        .ioctl_ops      = &gemtek_pci_ioctl_ops,
 402        .release        = video_device_release_empty,
 403};
 404
 405static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
 406{
 407        struct gemtek_pci_card *card;
 408        struct video_device *devradio;
 409
 410        if ( (card = kzalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) {
 411                printk( KERN_ERR "gemtek_pci: out of memory\n" );
 412                return -ENOMEM;
 413        }
 414
 415        if ( pci_enable_device( pci_dev ) )
 416                goto err_pci;
 417
 418        card->iobase = pci_resource_start( pci_dev, 0 );
 419        card->length = pci_resource_len( pci_dev, 0 );
 420
 421        if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) {
 422                printk( KERN_ERR "gemtek_pci: i/o port already in use\n" );
 423                goto err_pci;
 424        }
 425
 426        pci_set_drvdata( pci_dev, card );
 427
 428        if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) {
 429                printk( KERN_ERR "gemtek_pci: out of memory\n" );
 430                goto err_video;
 431        }
 432        *devradio = vdev_template;
 433
 434        if (video_register_device(devradio, VFL_TYPE_RADIO, nr_radio) < 0) {
 435                kfree( devradio );
 436                goto err_video;
 437        }
 438
 439        card->videodev = devradio;
 440        video_set_drvdata(devradio, card);
 441        gemtek_pci_mute( card );
 442
 443        printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n",
 444                pci_dev->revision, card->iobase, card->iobase + card->length - 1 );
 445
 446        return 0;
 447
 448err_video:
 449        release_region( card->iobase, card->length );
 450
 451err_pci:
 452        kfree( card );
 453        return -ENODEV;
 454}
 455
 456static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev )
 457{
 458        struct gemtek_pci_card *card = pci_get_drvdata( pci_dev );
 459
 460        video_unregister_device( card->videodev );
 461        kfree( card->videodev );
 462
 463        release_region( card->iobase, card->length );
 464
 465        if ( mx )
 466                gemtek_pci_mute( card );
 467
 468        kfree( card );
 469
 470        pci_set_drvdata( pci_dev, NULL );
 471}
 472
 473static struct pci_driver gemtek_pci_driver =
 474{
 475        .name           = "gemtek_pci",
 476        .id_table       = gemtek_pci_id,
 477        .probe          = gemtek_pci_probe,
 478        .remove         = __devexit_p(gemtek_pci_remove),
 479};
 480
 481static int __init gemtek_pci_init_module( void )
 482{
 483        return pci_register_driver( &gemtek_pci_driver );
 484}
 485
 486static void __exit gemtek_pci_cleanup_module( void )
 487{
 488        pci_unregister_driver(&gemtek_pci_driver);
 489}
 490
 491MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" );
 492MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" );
 493MODULE_LICENSE("GPL");
 494
 495module_param(mx, bool, 0);
 496MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" );
 497module_param(nr_radio, int, 0);
 498MODULE_PARM_DESC( nr_radio, "video4linux device number to use");
 499
 500module_init( gemtek_pci_init_module );
 501module_exit( gemtek_pci_cleanup_module );
 502
 503