linux-old/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 ***************************************************************************
  38 */
  39
  40#include <linux/version.h>
  41#include <linux/config.h>
  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/videodev.h>
  48#include <linux/errno.h>
  49
  50#include <asm/io.h>
  51#include <asm/uaccess.h>
  52
  53#ifndef PCI_VENDOR_ID_GEMTEK
  54#define PCI_VENDOR_ID_GEMTEK 0x5046
  55#endif
  56
  57#ifndef PCI_DEVICE_ID_GEMTEK_PR103
  58#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001
  59#endif
  60
  61#ifndef GEMTEK_PCI_RANGE_LOW
  62#define GEMTEK_PCI_RANGE_LOW (87*16000)
  63#endif
  64
  65#ifndef GEMTEK_PCI_RANGE_HIGH
  66#define GEMTEK_PCI_RANGE_HIGH (108*16000)
  67#endif
  68
  69#ifndef TRUE
  70#define TRUE (1)
  71#endif
  72
  73#ifndef FALSE 
  74#define FALSE (0)
  75#endif
  76
  77struct gemtek_pci_card {
  78        struct video_device *videodev;
  79        
  80        u32 iobase;
  81        u32 length;
  82        u8  chiprev;
  83        u16 model;
  84        
  85        u32 current_frequency;
  86        u8  mute;
  87};
  88
  89static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $";
  90
  91static int nr_radio = -1;
  92
  93static int gemtek_pci_open( struct video_device *dev, int flags)
  94{
  95        struct gemtek_pci_card *card =  dev->priv;
  96
  97/* Paranoid check */
  98        if ( !card )
  99                return -ENODEV;
 100
 101        return 0;
 102}
 103
 104static void gemtek_pci_close( struct video_device *dev )
 105{
 106/*
 107 *  The module usage is managed by 'videodev'
 108 */
 109}
 110
 111static inline u8 gemtek_pci_out( u16 value, u32 port )
 112{
 113        outw( value, port );
 114
 115        return (u8)value;
 116}
 117
 118#define _b0( v ) *((u8 *)&v)  
 119static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep )
 120{
 121        register u8 byte = *last_byte;
 122
 123        if ( !value ) {
 124                if ( !keep )
 125                        value = (u16)port;
 126                byte &= 0xfd;   
 127        } else
 128                byte |= 2;
 129
 130        _b0( value ) = byte;
 131        outw( value, port );
 132        byte |= 1;
 133        _b0( value ) = byte;
 134        outw( value, port );
 135        byte &= 0xfe;
 136        _b0( value ) = byte;
 137        outw( value, port );
 138        
 139        *last_byte = byte;
 140}
 141
 142static inline void gemtek_pci_nil( u32 port, u8 *last_byte )
 143{
 144        __gemtek_pci_cmd( 0x00, port, last_byte, FALSE );
 145}
 146
 147static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte )
 148{
 149        __gemtek_pci_cmd( cmd, port, last_byte, TRUE );
 150}
 151
 152static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency )
 153{
 154        register int i;
 155        register u32 value = frequency / 200 + 856;
 156        register u16 mask = 0x8000;
 157        u8 last_byte;
 158        u32 port = card->iobase;
 159
 160        last_byte = gemtek_pci_out( 0x06, port );
 161
 162        i = 0;
 163        do {
 164                gemtek_pci_nil( port, &last_byte );
 165                i++;
 166        } while ( i < 9 );
 167
 168        i = 0;
 169        do {
 170                gemtek_pci_cmd( value & mask, port, &last_byte );
 171                mask >>= 1;
 172                i++;
 173        } while ( i < 16 );
 174
 175        outw( 0x10, port );
 176}
 177
 178
 179static inline void gemtek_pci_mute( struct gemtek_pci_card *card )
 180{
 181        outb( 0x1f, card->iobase );
 182        card->mute = TRUE;
 183}
 184
 185static inline void gemtek_pci_unmute( struct gemtek_pci_card *card )
 186{
 187        if ( card->mute ) {
 188                gemtek_pci_setfrequency( card, card->current_frequency );
 189                card->mute = FALSE;
 190        }
 191}
 192
 193static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
 194{
 195        return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
 196}
 197
 198static int gemtek_pci_ioctl( struct video_device *dev, unsigned int cmd, void *arg)
 199{
 200        struct gemtek_pci_card *card = dev->priv;
 201
 202        switch ( cmd ) {
 203                case VIDIOCGCAP:
 204                {
 205                        struct video_capability c;
 206
 207                        c.type = VID_TYPE_TUNER;
 208                        c.channels = 1;
 209                        c.audios = 1;
 210                        c.maxwidth = 0;
 211                        c.maxheight = 0;
 212                        c.minwidth = 0;
 213                        c.minheight = 0;
 214                        strcpy( c.name, "Gemtek PCI Radio" );
 215                        if ( copy_to_user( arg, &c, sizeof( c ) ) )
 216                                return -EFAULT;
 217
 218                        return 0;
 219                } 
 220
 221                case VIDIOCGTUNER:
 222                {
 223                        struct video_tuner t;
 224                        int signal;
 225
 226                        if ( copy_from_user( &t, arg, sizeof( struct video_tuner ) ) )
 227                                return -EFAULT;
 228
 229                        if ( t.tuner ) 
 230                                return -EINVAL;
 231
 232                        signal = gemtek_pci_getsignal( card );
 233                        t.rangelow = GEMTEK_PCI_RANGE_LOW;
 234                        t.rangehigh = GEMTEK_PCI_RANGE_HIGH;
 235                        t.flags = VIDEO_TUNER_LOW | (7 << signal) ;
 236                        t.mode = VIDEO_MODE_AUTO;
 237                        t.signal = 0xFFFF * signal;
 238                        strcpy( t.name, "FM" );
 239
 240                        if ( copy_to_user( arg, &t, sizeof( struct video_tuner ) ) )
 241                                return -EFAULT;
 242
 243                        return 0;
 244                }
 245
 246                case VIDIOCSTUNER:
 247                {
 248                        struct video_tuner t;
 249
 250                        if ( copy_from_user( &t, arg, sizeof( struct video_tuner ) ) )
 251                                return -EFAULT;
 252
 253                        if ( t.tuner )
 254                                return -EINVAL;
 255
 256                        return 0;
 257                }
 258
 259                case VIDIOCGFREQ:
 260                        return put_user( card->current_frequency, (u32 *)arg );
 261
 262                case VIDIOCSFREQ:
 263                {
 264                        u32 frequency;
 265         
 266                        if ( get_user( frequency, (u32 *)arg ) )
 267                                return -EFAULT;
 268
 269                        if ( (frequency < GEMTEK_PCI_RANGE_LOW) || (frequency > GEMTEK_PCI_RANGE_HIGH) )
 270                                return -EINVAL;
 271
 272                        gemtek_pci_setfrequency( card, frequency );
 273                        card->current_frequency = frequency;
 274                        card->mute = FALSE;
 275
 276                        return 0;
 277                }
 278  
 279                case VIDIOCGAUDIO:
 280                {       
 281                        struct video_audio a;
 282
 283                        memset( &a, 0, sizeof( a ) );
 284                        a.flags |= VIDEO_AUDIO_MUTABLE;
 285                        a.volume = 1;
 286                        a.step = 65535;
 287                        a.mode = (1 << gemtek_pci_getsignal( card ));
 288                        strcpy( a.name, "Radio" );
 289
 290                        if ( copy_to_user( arg, &a, sizeof( struct video_audio ) ) )
 291                                return -EFAULT;
 292
 293                        return 0;                       
 294                }
 295
 296                case VIDIOCSAUDIO:
 297                {
 298                        struct video_audio a;
 299
 300                        if ( copy_from_user( &a, arg, sizeof( struct video_audio ) ) ) 
 301                                return -EFAULT; 
 302
 303                        if ( a.audio ) 
 304                                return -EINVAL;
 305
 306                        if ( a.flags & VIDEO_AUDIO_MUTE ) 
 307                                gemtek_pci_mute( card );
 308
 309                        else
 310                                gemtek_pci_unmute( card );
 311
 312                        return 0;
 313                }
 314
 315                default:
 316                        return -ENOIOCTLCMD;
 317        }
 318}
 319
 320enum {
 321        GEMTEK_PR103
 322};
 323
 324static char *card_names[] __devinitdata = {
 325        "GEMTEK_PR103"
 326};
 327
 328static struct pci_device_id gemtek_pci_id[] =
 329{
 330        { PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103,
 331          PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 },
 332        { 0 }
 333};
 334
 335MODULE_DEVICE_TABLE( pci, gemtek_pci_id );
 336
 337static u8 mx = 1;
 338
 339static char gemtek_pci_videodev_name[] = "Gemtek PCI Radio";
 340
 341static inline void gemtek_pci_init_struct( struct video_device *dev )
 342{
 343        memset( dev, 0, sizeof( struct video_device ) );
 344        dev->owner = THIS_MODULE;
 345        strcpy( dev->name , gemtek_pci_videodev_name );
 346        dev->type = VID_TYPE_TUNER;
 347        dev->hardware = VID_HARDWARE_GEMTEK;
 348        dev->open = gemtek_pci_open;
 349        dev->close = gemtek_pci_close;
 350        dev->ioctl = gemtek_pci_ioctl;
 351}
 352
 353static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
 354{
 355        struct gemtek_pci_card *card;
 356        struct video_device *devradio;
 357
 358        if ( (card = kmalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) {
 359                printk( KERN_ERR "gemtek_pci: out of memory\n" );
 360                return -ENOMEM;
 361        }
 362        memset( card, 0, sizeof( struct gemtek_pci_card ) );
 363
 364        if ( pci_enable_device( pci_dev ) ) 
 365                goto err_pci;
 366        
 367        card->iobase = pci_resource_start( pci_dev, 0 );
 368        card->length = pci_resource_len( pci_dev, 0 );
 369
 370        if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) {
 371                printk( KERN_ERR "gemtek_pci: i/o port already in use\n" );
 372                goto err_pci;
 373        }
 374
 375        pci_read_config_byte( pci_dev, PCI_REVISION_ID, &card->chiprev );
 376        pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model );
 377
 378        pci_set_drvdata( pci_dev, card );
 379 
 380        if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) {
 381                printk( KERN_ERR "gemtek_pci: out of memory\n" );
 382                goto err_video;
 383        }
 384        gemtek_pci_init_struct( devradio );
 385
 386        if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) {
 387                kfree( devradio );
 388                goto err_video;
 389        }
 390
 391        card->videodev = devradio;
 392        devradio->priv = card;
 393        gemtek_pci_mute( card );
 394
 395        printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n", 
 396                card->chiprev, card->iobase, card->iobase + card->length - 1 );
 397
 398        return 0;
 399
 400err_video:
 401        release_region( card->iobase, card->length );
 402
 403err_pci:
 404        kfree( card );
 405        return -ENODEV;        
 406}
 407
 408static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev )
 409{
 410        struct gemtek_pci_card *card = pci_get_drvdata( pci_dev );
 411
 412        video_unregister_device( card->videodev );
 413        kfree( card->videodev );
 414
 415        release_region( card->iobase, card->length );
 416        
 417        if ( mx )
 418                gemtek_pci_mute( card );
 419
 420        kfree( card );
 421        
 422        pci_set_drvdata( pci_dev, NULL );
 423}
 424
 425static struct pci_driver gemtek_pci_driver =
 426{
 427    name:       "gemtek_pci",
 428id_table:       gemtek_pci_id,
 429   probe:       gemtek_pci_probe,
 430  remove:       __devexit_p(gemtek_pci_remove),
 431};
 432
 433static int __init gemtek_pci_init_module( void )
 434{
 435        return pci_module_init( &gemtek_pci_driver );
 436}
 437
 438static void __exit gemtek_pci_cleanup_module( void )
 439{
 440        return pci_unregister_driver( &gemtek_pci_driver );
 441}
 442
 443MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" );
 444MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" );
 445MODULE_LICENSE("GPL");
 446
 447MODULE_PARM( mx, "b" );
 448MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" );
 449MODULE_PARM( nr_radio, "i");
 450MODULE_PARM_DESC( nr_radio, "video4linux device number to use");
 451
 452EXPORT_NO_SYMBOLS;
 453
 454module_init( gemtek_pci_init_module );
 455module_exit( gemtek_pci_cleanup_module );
 456
 457
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.