2005-04-16 15:20:36 -07:00
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2006-04-08 16:06:16 -03:00
*
2005-04-16 15:20:36 -07:00
* radio - gemtek - pci . c - Gemtek PCI Radio driver
* ( C ) 2001 Vladimir Shebordaev < vshebordaev @ mail . ru >
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the Free
* Software Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 ,
* USA .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Gemtek Corp still silently refuses to release any specifications
* of their multimedia devices , so the protocol still has to be
* reverse engineered .
*
* The v4l code was inspired by Jonas Munsin ' s Gemtek serial line
* radio device driver .
*
* Please , let me know if this piece of code was useful : )
2006-04-08 16:06:16 -03:00
*
2005-04-16 15:20:36 -07:00
* TODO : multiple device support and portability were not tested
*
2006-08-08 09:10:01 -03:00
* Converted to V4L2 API by Mauro Carvalho Chehab < mchehab @ infradead . org >
*
2005-04-16 15:20:36 -07:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
# include <linux/types.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pci.h>
2006-08-08 09:10:01 -03:00
# include <linux/videodev2.h>
2006-06-05 10:26:32 -03:00
# include <media/v4l2-common.h>
2005-04-16 15:20:36 -07:00
# include <linux/errno.h>
2006-08-08 09:10:01 -03:00
# include <linux/version.h> /* for KERNEL_VERSION MACRO */
# define RADIO_VERSION KERNEL_VERSION(0,0,2)
static struct v4l2_queryctrl radio_qctrl [ ] = {
{
. id = V4L2_CID_AUDIO_MUTE ,
. name = " Mute " ,
. minimum = 0 ,
. maximum = 1 ,
. default_value = 1 ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
} , {
. id = V4L2_CID_AUDIO_VOLUME ,
. name = " Volume " ,
. minimum = 0 ,
. maximum = 65535 ,
. step = 65535 ,
. default_value = 0xff ,
. type = V4L2_CTRL_TYPE_INTEGER ,
}
} ;
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <asm/uaccess.h>
# ifndef PCI_VENDOR_ID_GEMTEK
# define PCI_VENDOR_ID_GEMTEK 0x5046
# endif
# ifndef PCI_DEVICE_ID_GEMTEK_PR103
# define PCI_DEVICE_ID_GEMTEK_PR103 0x1001
# endif
# ifndef GEMTEK_PCI_RANGE_LOW
# define GEMTEK_PCI_RANGE_LOW (87*16000)
# endif
# ifndef GEMTEK_PCI_RANGE_HIGH
# define GEMTEK_PCI_RANGE_HIGH (108*16000)
# endif
struct gemtek_pci_card {
struct video_device * videodev ;
2006-04-08 16:06:16 -03:00
2005-04-16 15:20:36 -07:00
u32 iobase ;
u32 length ;
2006-04-08 16:06:16 -03:00
2005-04-16 15:20:36 -07:00
u32 current_frequency ;
u8 mute ;
} ;
static const char rcsid [ ] = " $Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $ " ;
static int nr_radio = - 1 ;
static inline u8 gemtek_pci_out ( u16 value , u32 port )
{
outw ( value , port ) ;
return ( u8 ) value ;
}
2006-04-08 16:06:16 -03:00
# define _b0( v ) *((u8 *)&v)
2005-04-16 15:20:36 -07:00
static void __gemtek_pci_cmd ( u16 value , u32 port , u8 * last_byte , int keep )
{
register u8 byte = * last_byte ;
if ( ! value ) {
if ( ! keep )
value = ( u16 ) port ;
2006-04-08 16:06:16 -03:00
byte & = 0xfd ;
2005-04-16 15:20:36 -07:00
} else
byte | = 2 ;
_b0 ( value ) = byte ;
outw ( value , port ) ;
byte | = 1 ;
_b0 ( value ) = byte ;
outw ( value , port ) ;
byte & = 0xfe ;
_b0 ( value ) = byte ;
outw ( value , port ) ;
2006-04-08 16:06:16 -03:00
2005-04-16 15:20:36 -07:00
* last_byte = byte ;
}
static inline void gemtek_pci_nil ( u32 port , u8 * last_byte )
{
2007-02-06 21:55:07 -03:00
__gemtek_pci_cmd ( 0x00 , port , last_byte , false ) ;
2005-04-16 15:20:36 -07:00
}
static inline void gemtek_pci_cmd ( u16 cmd , u32 port , u8 * last_byte )
{
2007-02-06 21:55:07 -03:00
__gemtek_pci_cmd ( cmd , port , last_byte , true ) ;
2005-04-16 15:20:36 -07:00
}
static void gemtek_pci_setfrequency ( struct gemtek_pci_card * card , unsigned long frequency )
{
register int i ;
register u32 value = frequency / 200 + 856 ;
register u16 mask = 0x8000 ;
u8 last_byte ;
u32 port = card - > iobase ;
last_byte = gemtek_pci_out ( 0x06 , port ) ;
i = 0 ;
do {
gemtek_pci_nil ( port , & last_byte ) ;
i + + ;
} while ( i < 9 ) ;
i = 0 ;
do {
gemtek_pci_cmd ( value & mask , port , & last_byte ) ;
mask > > = 1 ;
i + + ;
} while ( i < 16 ) ;
outw ( 0x10 , port ) ;
}
static inline void gemtek_pci_mute ( struct gemtek_pci_card * card )
{
outb ( 0x1f , card - > iobase ) ;
2007-02-06 21:55:07 -03:00
card - > mute = true ;
2005-04-16 15:20:36 -07:00
}
static inline void gemtek_pci_unmute ( struct gemtek_pci_card * card )
{
if ( card - > mute ) {
gemtek_pci_setfrequency ( card , card - > current_frequency ) ;
2007-02-06 21:55:07 -03:00
card - > mute = false ;
2005-04-16 15:20:36 -07:00
}
}
static inline unsigned int gemtek_pci_getsignal ( struct gemtek_pci_card * card )
{
return ( inb ( card - > iobase ) & 0x08 ) ? 0 : 1 ;
}
2007-04-26 10:42:12 -03:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
{
strlcpy ( v - > driver , " radio-gemtek-pci " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " GemTek PCI Radio " , sizeof ( v - > card ) ) ;
sprintf ( v - > bus_info , " ISA " ) ;
v - > version = RADIO_VERSION ;
v - > capabilities = V4L2_CAP_TUNER ;
return 0 ;
}
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
2005-04-16 15:20:36 -07:00
{
struct video_device * dev = video_devdata ( file ) ;
struct gemtek_pci_card * card = dev - > priv ;
2007-04-26 10:42:12 -03:00
if ( v - > index > 0 )
return - EINVAL ;
strcpy ( v - > name , " FM " ) ;
v - > type = V4L2_TUNER_RADIO ;
v - > rangelow = GEMTEK_PCI_RANGE_LOW ;
v - > rangehigh = GEMTEK_PCI_RANGE_HIGH ;
v - > rxsubchans = V4L2_TUNER_SUB_MONO ;
v - > capability = V4L2_TUNER_CAP_LOW ;
v - > audmode = V4L2_TUNER_MODE_MONO ;
v - > signal = 0xffff * gemtek_pci_getsignal ( card ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
2007-04-26 10:42:12 -03:00
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
if ( v - > index > 0 )
return - EINVAL ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
2007-04-26 10:42:12 -03:00
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
struct video_device * dev = video_devdata ( file ) ;
struct gemtek_pci_card * card = dev - > priv ;
2006-08-08 09:10:01 -03:00
2007-04-26 10:42:12 -03:00
if ( ( f - > frequency < GEMTEK_PCI_RANGE_LOW ) | |
( f - > frequency > GEMTEK_PCI_RANGE_HIGH ) )
return - EINVAL ;
gemtek_pci_setfrequency ( card , f - > frequency ) ;
card - > current_frequency = f - > frequency ;
card - > mute = false ;
return 0 ;
}
2006-08-08 09:10:01 -03:00
2007-04-26 10:42:12 -03:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
struct video_device * dev = video_devdata ( file ) ;
struct gemtek_pci_card * card = dev - > priv ;
2006-08-08 09:10:01 -03:00
2007-04-26 10:42:12 -03:00
f - > type = V4L2_TUNER_RADIO ;
f - > frequency = card - > current_frequency ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
2007-04-26 10:42:12 -03:00
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( radio_qctrl ) ; i + + ) {
if ( qc - > id & & qc - > id = = radio_qctrl [ i ] . id ) {
memcpy ( qc , & ( radio_qctrl [ i ] ) ,
sizeof ( * qc ) ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2007-04-26 10:42:12 -03:00
}
return - EINVAL ;
}
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
struct video_device * dev = video_devdata ( file ) ;
struct gemtek_pci_card * card = dev - > priv ;
2006-04-08 16:06:16 -03:00
2007-04-26 10:42:12 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = card - > mute ;
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
if ( card - > mute )
ctrl - > value = 0 ;
else
ctrl - > value = 65535 ;
return 0 ;
}
return - EINVAL ;
}
2005-04-16 15:20:36 -07:00
2007-04-26 10:42:12 -03:00
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
struct video_device * dev = video_devdata ( file ) ;
struct gemtek_pci_card * card = dev - > priv ;
2005-04-16 15:20:36 -07:00
2007-04-26 10:42:12 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( ctrl - > value )
gemtek_pci_mute ( card ) ;
else
gemtek_pci_unmute ( card ) ;
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
if ( ctrl - > value )
gemtek_pci_unmute ( card ) ;
else
gemtek_pci_mute ( card ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-04-26 10:42:12 -03:00
return - EINVAL ;
}
static int vidioc_g_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
if ( a - > index > 1 )
return - EINVAL ;
strcpy ( a - > name , " Radio " ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
return 0 ;
}
static int vidioc_g_input ( struct file * filp , void * priv , unsigned int * i )
{
* i = 0 ;
return 0 ;
}
static int vidioc_s_input ( struct file * filp , void * priv , unsigned int i )
{
if ( i ! = 0 )
return - EINVAL ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-04-26 10:42:12 -03:00
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
2005-04-16 15:20:36 -07:00
{
2007-04-26 10:42:12 -03:00
if ( a - > index ! = 0 )
return - EINVAL ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
enum {
GEMTEK_PR103
} ;
static char * card_names [ ] __devinitdata = {
" GEMTEK_PR103 "
} ;
static struct pci_device_id gemtek_pci_id [ ] =
{
{ PCI_VENDOR_ID_GEMTEK , PCI_DEVICE_ID_GEMTEK_PR103 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , GEMTEK_PR103 } ,
{ 0 }
} ;
MODULE_DEVICE_TABLE ( pci , gemtek_pci_id ) ;
static int mx = 1 ;
2007-02-12 00:55:33 -08:00
static const struct file_operations gemtek_pci_fops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
. open = video_exclusive_open ,
. release = video_exclusive_release ,
2007-04-26 10:42:12 -03:00
. ioctl = video_ioctl2 ,
2008-04-22 14:46:11 -03:00
# ifdef CONFIG_COMPAT
2006-01-09 15:24:57 -02:00
. compat_ioctl = v4l_compat_ioctl32 ,
2008-04-22 14:46:11 -03:00
# endif
2005-04-16 15:20:36 -07:00
. llseek = no_llseek ,
} ;
static struct video_device vdev_template = {
. owner = THIS_MODULE ,
. name = " Gemtek PCI Radio " ,
. type = VID_TYPE_TUNER ,
. fops = & gemtek_pci_fops ,
2007-04-26 10:42:12 -03:00
. vidioc_querycap = vidioc_querycap ,
. vidioc_g_tuner = vidioc_g_tuner ,
. vidioc_s_tuner = vidioc_s_tuner ,
. vidioc_g_audio = vidioc_g_audio ,
. vidioc_s_audio = vidioc_s_audio ,
. vidioc_g_input = vidioc_g_input ,
. vidioc_s_input = vidioc_s_input ,
. vidioc_g_frequency = vidioc_g_frequency ,
. vidioc_s_frequency = vidioc_s_frequency ,
. vidioc_queryctrl = vidioc_queryctrl ,
. vidioc_g_ctrl = vidioc_g_ctrl ,
. vidioc_s_ctrl = vidioc_s_ctrl ,
2005-04-16 15:20:36 -07:00
} ;
static int __devinit gemtek_pci_probe ( struct pci_dev * pci_dev , const struct pci_device_id * pci_id )
{
struct gemtek_pci_card * card ;
struct video_device * devradio ;
2006-01-11 19:40:56 -02:00
if ( ( card = kzalloc ( sizeof ( struct gemtek_pci_card ) , GFP_KERNEL ) ) = = NULL ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR " gemtek_pci: out of memory \n " ) ;
return - ENOMEM ;
}
2006-04-08 16:06:16 -03:00
if ( pci_enable_device ( pci_dev ) )
2005-04-16 15:20:36 -07:00
goto err_pci ;
2006-04-08 16:06:16 -03:00
2005-04-16 15:20:36 -07:00
card - > iobase = pci_resource_start ( pci_dev , 0 ) ;
card - > length = pci_resource_len ( pci_dev , 0 ) ;
if ( request_region ( card - > iobase , card - > length , card_names [ pci_id - > driver_data ] ) = = NULL ) {
printk ( KERN_ERR " gemtek_pci: i/o port already in use \n " ) ;
goto err_pci ;
}
pci_set_drvdata ( pci_dev , card ) ;
2006-04-08 16:06:16 -03:00
2005-04-16 15:20:36 -07:00
if ( ( devradio = kmalloc ( sizeof ( struct video_device ) , GFP_KERNEL ) ) = = NULL ) {
printk ( KERN_ERR " gemtek_pci: out of memory \n " ) ;
goto err_video ;
}
* devradio = vdev_template ;
if ( video_register_device ( devradio , VFL_TYPE_RADIO , nr_radio ) = = - 1 ) {
kfree ( devradio ) ;
goto err_video ;
}
card - > videodev = devradio ;
devradio - > priv = card ;
gemtek_pci_mute ( card ) ;
2006-04-08 16:06:16 -03:00
printk ( KERN_INFO " Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x. \n " ,
2007-06-08 15:46:36 -07:00
pci_dev - > revision , card - > iobase , card - > iobase + card - > length - 1 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
err_video :
release_region ( card - > iobase , card - > length ) ;
err_pci :
kfree ( card ) ;
2006-04-08 16:06:16 -03:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
static void __devexit gemtek_pci_remove ( struct pci_dev * pci_dev )
{
struct gemtek_pci_card * card = pci_get_drvdata ( pci_dev ) ;
video_unregister_device ( card - > videodev ) ;
kfree ( card - > videodev ) ;
release_region ( card - > iobase , card - > length ) ;
2006-04-08 16:06:16 -03:00
2005-04-16 15:20:36 -07:00
if ( mx )
gemtek_pci_mute ( card ) ;
kfree ( card ) ;
2006-04-08 16:06:16 -03:00
2005-04-16 15:20:36 -07:00
pci_set_drvdata ( pci_dev , NULL ) ;
}
static struct pci_driver gemtek_pci_driver =
{
. name = " gemtek_pci " ,
. id_table = gemtek_pci_id ,
. probe = gemtek_pci_probe ,
. remove = __devexit_p ( gemtek_pci_remove ) ,
} ;
static int __init gemtek_pci_init_module ( void )
{
2005-11-30 00:59:34 +01:00
return pci_register_driver ( & gemtek_pci_driver ) ;
2005-04-16 15:20:36 -07:00
}
static void __exit gemtek_pci_cleanup_module ( void )
{
2006-10-04 08:09:10 -03:00
pci_unregister_driver ( & gemtek_pci_driver ) ;
2005-04-16 15:20:36 -07:00
}
MODULE_AUTHOR ( " Vladimir Shebordaev <vshebordaev@mail.ru> " ) ;
MODULE_DESCRIPTION ( " The video4linux driver for the Gemtek PCI Radio Card " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( mx , bool , 0 ) ;
MODULE_PARM_DESC ( mx , " single digit: 1 - turn off the turner upon module exit (default), 0 - do not " ) ;
module_param ( nr_radio , int , 0 ) ;
MODULE_PARM_DESC ( nr_radio , " video4linux device number to use " ) ;
module_init ( gemtek_pci_init_module ) ;
module_exit ( gemtek_pci_cleanup_module ) ;