2005-04-16 15:20:36 -07:00
/* radiotrack (radioreveal) driver for Linux radio support
* ( c ) 1997 M . Kirkwood
2006-08-08 09:10:01 -03:00
* Converted to V4L2 API by Mauro Carvalho Chehab < mchehab @ infradead . org >
2008-10-27 15:13:47 -03:00
* Converted to new API by Alan Cox < alan @ lxorguk . ukuu . org . uk >
2005-04-16 15:20:36 -07:00
* Various bugfixes and enhancements by Russell Kroll < rkroll @ exploits . org >
*
* History :
* 1999 - 02 - 24 Russell Kroll < rkroll @ exploits . org >
* Fine tuning / VIDEO_TUNER_LOW
* Frequency range expanded to start at 87 MHz
*
* TODO : Allow for more than one of these foolish entities : - )
*
* Notes on the hardware ( reverse engineered from other peoples '
* reverse engineering of AIMS ' code : - )
*
* Frequency control is done digitally - - ie out ( port , encodefreq ( 95.8 ) ) ;
*
* The signal strength query is unsurprisingly inaccurate . And it seems
* to indicate that ( on my card , at least ) the frequency setting isn ' t
* too great . ( I have to tune up .025 MHz from what the freq should be
* to get a report that the thing is tuned . )
*
* Volume control is ( ugh ) analogue :
* out ( port , start_increasing_volume ) ;
* wait ( a_wee_while ) ;
* out ( port , stop_changing_the_volume ) ;
2006-04-08 16:06:16 -03:00
*
2005-04-16 15:20:36 -07:00
*/
# include <linux/module.h> /* Modules */
# include <linux/init.h> /* Initdata */
2005-09-13 01:25:15 -07:00
# include <linux/ioport.h> /* request_region */
2005-04-16 15:20:36 -07:00
# include <linux/delay.h> /* udelay */
2006-08-08 09:10:01 -03:00
# include <linux/videodev2.h> /* kernel radio structs */
2009-03-06 13:45:27 -03:00
# include <linux/version.h> /* for KERNEL_VERSION MACRO */
# include <linux/io.h> /* outb, outb_p */
# include <media/v4l2-device.h>
2008-07-20 08:12:02 -03:00
# include <media/v4l2-ioctl.h>
2005-04-16 15:20:36 -07:00
2009-03-06 13:45:27 -03:00
MODULE_AUTHOR ( " M.Kirkwood " ) ;
MODULE_DESCRIPTION ( " A driver for the RadioTrack/RadioReveal radio card. " ) ;
MODULE_LICENSE ( " GPL " ) ;
2006-08-08 09:10:01 -03:00
2005-04-16 15:20:36 -07:00
# ifndef CONFIG_RADIO_RTRACK_PORT
# define CONFIG_RADIO_RTRACK_PORT -1
# endif
2006-04-08 16:06:16 -03:00
static int io = CONFIG_RADIO_RTRACK_PORT ;
2005-04-16 15:20:36 -07:00
static int radio_nr = - 1 ;
2009-03-06 13:45:27 -03:00
module_param ( io , int , 0 ) ;
MODULE_PARM_DESC ( io , " I/O address of the RadioTrack card (0x20f or 0x30f) " ) ;
module_param ( radio_nr , int , 0 ) ;
# define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
struct rtrack
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
struct v4l2_device v4l2_dev ;
struct video_device vdev ;
2005-04-16 15:20:36 -07:00
int port ;
int curvol ;
unsigned long curfreq ;
int muted ;
2009-03-06 13:45:27 -03:00
int io ;
struct mutex lock ;
2005-04-16 15:20:36 -07:00
} ;
2009-03-06 13:45:27 -03:00
static struct rtrack rtrack_card ;
2005-04-16 15:20:36 -07:00
/* local things */
static void sleep_delay ( long n )
{
/* Sleep nicely for 'n' uS */
2009-03-06 13:45:27 -03:00
int d = n / msecs_to_jiffies ( 1000 ) ;
if ( ! d )
2005-04-16 15:20:36 -07:00
udelay ( n ) ;
else
msleep ( jiffies_to_msecs ( d ) ) ;
}
2009-03-06 13:45:27 -03:00
static void rt_decvol ( struct rtrack * rt )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
outb ( 0x58 , rt - > io ) ; /* volume down + sigstr + on */
2005-04-16 15:20:36 -07:00
sleep_delay ( 100000 ) ;
2009-03-06 13:45:27 -03:00
outb ( 0xd8 , rt - > io ) ; /* volume steady + sigstr + on */
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:45:27 -03:00
static void rt_incvol ( struct rtrack * rt )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
outb ( 0x98 , rt - > io ) ; /* volume up + sigstr + on */
2005-04-16 15:20:36 -07:00
sleep_delay ( 100000 ) ;
2009-03-06 13:45:27 -03:00
outb ( 0xd8 , rt - > io ) ; /* volume steady + sigstr + on */
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:45:27 -03:00
static void rt_mute ( struct rtrack * rt )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
rt - > muted = 1 ;
mutex_lock ( & rt - > lock ) ;
outb ( 0xd0 , rt - > io ) ; /* volume steady, off */
mutex_unlock ( & rt - > lock ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:45:27 -03:00
static int rt_setvol ( struct rtrack * rt , int vol )
2005-04-16 15:20:36 -07:00
{
int i ;
2009-03-06 13:45:27 -03:00
mutex_lock ( & rt - > lock ) ;
2006-04-08 16:06:16 -03:00
2009-03-06 13:45:27 -03:00
if ( vol = = rt - > curvol ) { /* requested volume = current */
if ( rt - > muted ) { /* user is unmuting the card */
rt - > muted = 0 ;
outb ( 0xd8 , rt - > io ) ; /* enable card */
2006-04-08 16:06:16 -03:00
}
2009-03-06 13:45:27 -03:00
mutex_unlock ( & rt - > lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-03-06 13:45:27 -03:00
if ( vol = = 0 ) { /* volume = 0 means mute the card */
outb ( 0x48 , rt - > io ) ; /* volume down but still "on" */
2005-04-16 15:20:36 -07:00
sleep_delay ( 2000000 ) ; /* make sure it's totally down */
2009-03-06 13:45:27 -03:00
outb ( 0xd0 , rt - > io ) ; /* volume steady, off */
rt - > curvol = 0 ; /* track the volume state! */
mutex_unlock ( & rt - > lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-03-06 13:45:27 -03:00
rt - > muted = 0 ;
if ( vol > rt - > curvol )
for ( i = rt - > curvol ; i < vol ; i + + )
rt_incvol ( rt ) ;
2005-04-16 15:20:36 -07:00
else
2009-03-06 13:45:27 -03:00
for ( i = rt - > curvol ; i > vol ; i - - )
rt_decvol ( rt ) ;
2005-04-16 15:20:36 -07:00
2009-03-06 13:45:27 -03:00
rt - > curvol = vol ;
mutex_unlock ( & rt - > lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2006-04-08 16:06:16 -03:00
/* the 128+64 on these outb's is to keep the volume stable while tuning
2005-04-16 15:20:36 -07:00
* without them , the volume _will_ creep up with each frequency change
* and bit 4 ( + 16 ) is to keep the signal strength meter enabled
*/
2009-03-06 13:45:27 -03:00
static void send_0_byte ( struct rtrack * rt )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
if ( rt - > curvol = = 0 | | rt - > muted ) {
outb_p ( 128 + 64 + 16 + 1 , rt - > io ) ; /* wr-enable + data low */
outb_p ( 128 + 64 + 16 + 2 + 1 , rt - > io ) ; /* clock */
2005-04-16 15:20:36 -07:00
}
else {
2009-03-06 13:45:27 -03:00
outb_p ( 128 + 64 + 16 + 8 + 1 , rt - > io ) ; /* on + wr-enable + data low */
outb_p ( 128 + 64 + 16 + 8 + 2 + 1 , rt - > io ) ; /* clock */
2005-04-16 15:20:36 -07:00
}
2006-04-08 16:06:16 -03:00
sleep_delay ( 1000 ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:45:27 -03:00
static void send_1_byte ( struct rtrack * rt )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
if ( rt - > curvol = = 0 | | rt - > muted ) {
outb_p ( 128 + 64 + 16 + 4 + 1 , rt - > io ) ; /* wr-enable+data high */
outb_p ( 128 + 64 + 16 + 4 + 2 + 1 , rt - > io ) ; /* clock */
2006-04-08 16:06:16 -03:00
}
2005-04-16 15:20:36 -07:00
else {
2009-03-06 13:45:27 -03:00
outb_p ( 128 + 64 + 16 + 8 + 4 + 1 , rt - > io ) ; /* on+wr-enable+data high */
outb_p ( 128 + 64 + 16 + 8 + 4 + 2 + 1 , rt - > io ) ; /* clock */
2005-04-16 15:20:36 -07:00
}
2006-04-08 16:06:16 -03:00
sleep_delay ( 1000 ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:45:27 -03:00
static int rt_setfreq ( struct rtrack * rt , unsigned long freq )
2005-04-16 15:20:36 -07:00
{
int i ;
2009-03-06 13:45:27 -03:00
mutex_lock ( & rt - > lock ) ; /* Stop other ops interfering */
rt - > curfreq = freq ;
2005-04-16 15:20:36 -07:00
/* now uses VIDEO_TUNER_LOW for fine tuning */
freq + = 171200 ; /* Add 10.7 MHz IF */
freq / = 800 ; /* Convert to 50 kHz units */
2006-04-08 16:06:16 -03:00
2009-03-06 13:45:27 -03:00
send_0_byte ( rt ) ; /* 0: LSB of frequency */
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < 13 ; i + + ) /* : frequency bits (1-13) */
if ( freq & ( 1 < < i ) )
2009-03-06 13:45:27 -03:00
send_1_byte ( rt ) ;
2005-04-16 15:20:36 -07:00
else
2009-03-06 13:45:27 -03:00
send_0_byte ( rt ) ;
2005-04-16 15:20:36 -07:00
2009-03-06 13:45:27 -03:00
send_0_byte ( rt ) ; /* 14: test bit - always 0 */
send_0_byte ( rt ) ; /* 15: test bit - always 0 */
2005-04-16 15:20:36 -07:00
2009-03-06 13:45:27 -03:00
send_0_byte ( rt ) ; /* 16: band data 0 - always 0 */
send_0_byte ( rt ) ; /* 17: band data 1 - always 0 */
send_0_byte ( rt ) ; /* 18: band data 2 - always 0 */
send_0_byte ( rt ) ; /* 19: time base - always 0 */
2005-04-16 15:20:36 -07:00
2009-03-06 13:45:27 -03:00
send_0_byte ( rt ) ; /* 20: spacing (0 = 25 kHz) */
send_1_byte ( rt ) ; /* 21: spacing (1 = 25 kHz) */
send_0_byte ( rt ) ; /* 22: spacing (0 = 25 kHz) */
send_1_byte ( rt ) ; /* 23: AM/FM (FM = 1, always) */
2005-04-16 15:20:36 -07:00
2009-03-06 13:45:27 -03:00
if ( rt - > curvol = = 0 | | rt - > muted )
outb ( 0xd0 , rt - > io ) ; /* volume steady + sigstr */
2005-04-16 15:20:36 -07:00
else
2009-03-06 13:45:27 -03:00
outb ( 0xd8 , rt - > io ) ; /* volume steady + sigstr + on */
2006-04-08 16:06:16 -03:00
2009-03-06 13:45:27 -03:00
mutex_unlock ( & rt - > lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-03-06 13:45:27 -03:00
static int rt_getsigstr ( struct rtrack * rt )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
int sig = 1 ;
2005-04-16 15:20:36 -07:00
2009-03-06 13:45:27 -03:00
mutex_lock ( & rt - > lock ) ;
if ( inb ( rt - > io ) & 2 ) /* bit set = no signal present */
sig = 0 ;
mutex_unlock ( & rt - > lock ) ;
return sig ;
}
2006-08-08 09:10:01 -03:00
2007-04-25 00:14:36 -03:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
{
strlcpy ( v - > driver , " radio-aimslab " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " RadioTrack " , sizeof ( v - > card ) ) ;
2009-03-06 13:45:27 -03:00
strlcpy ( v - > bus_info , " ISA " , sizeof ( v - > bus_info ) ) ;
2007-04-25 00:14:36 -03:00
v - > version = RADIO_VERSION ;
2009-03-06 13:45:27 -03:00
v - > capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO ;
2007-04-25 00:14:36 -03:00
return 0 ;
}
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
struct rtrack * rt = video_drvdata ( file ) ;
2006-04-08 16:06:16 -03:00
2007-04-25 00:14:36 -03:00
if ( v - > index > 0 )
return - EINVAL ;
2006-08-08 09:10:01 -03:00
2009-03-06 13:45:27 -03:00
strlcpy ( v - > name , " FM " , sizeof ( v - > name ) ) ;
2007-04-25 00:14:36 -03:00
v - > type = V4L2_TUNER_RADIO ;
2009-03-06 13:45:27 -03:00
v - > rangelow = 87 * 16000 ;
v - > rangehigh = 108 * 16000 ;
2007-04-25 00:14:36 -03:00
v - > rxsubchans = V4L2_TUNER_SUB_MONO ;
v - > capability = V4L2_TUNER_CAP_LOW ;
v - > audmode = V4L2_TUNER_MODE_MONO ;
2009-03-06 13:45:27 -03:00
v - > signal = 0xffff * rt_getsigstr ( rt ) ;
2007-04-25 00:14:36 -03:00
return 0 ;
}
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-03-06 13:45:27 -03:00
return v - > index ? - EINVAL : 0 ;
2007-04-25 00:14:36 -03:00
}
2006-08-08 09:10:01 -03:00
2007-04-25 00:14:36 -03:00
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 13:45:27 -03:00
struct rtrack * rt = video_drvdata ( file ) ;
2006-08-08 09:10:01 -03:00
2009-11-27 04:33:25 -03:00
if ( f - > tuner ! = 0 | | f - > type ! = V4L2_TUNER_RADIO )
return - EINVAL ;
2009-03-06 13:45:27 -03:00
rt_setfreq ( rt , f - > frequency ) ;
2007-04-25 00:14:36 -03:00
return 0 ;
}
2006-08-08 09:10:01 -03:00
2007-04-25 00:14:36 -03:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 13:45:27 -03:00
struct rtrack * rt = video_drvdata ( file ) ;
2006-08-08 09:10:01 -03:00
2009-11-27 04:33:25 -03:00
if ( f - > tuner ! = 0 )
return - EINVAL ;
2007-04-25 00:14:36 -03:00
f - > type = V4L2_TUNER_RADIO ;
f - > frequency = rt - > curfreq ;
return 0 ;
}
2006-08-08 09:10:01 -03:00
2007-04-25 00:14:36 -03:00
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
2009-03-06 13:45:27 -03:00
switch ( qc - > id ) {
case V4L2_CID_AUDIO_MUTE :
return v4l2_ctrl_query_fill ( qc , 0 , 1 , 1 , 1 ) ;
case V4L2_CID_AUDIO_VOLUME :
return v4l2_ctrl_query_fill ( qc , 0 , 0xff , 1 , 0xff ) ;
2007-04-25 00:14:36 -03:00
}
return - EINVAL ;
}
2006-08-08 09:10:01 -03:00
2007-04-25 00:14:36 -03:00
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-03-06 13:45:27 -03:00
struct rtrack * rt = video_drvdata ( file ) ;
2006-08-08 09:10:01 -03:00
2007-04-25 00:14:36 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = rt - > muted ;
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
2009-03-06 13:45:27 -03:00
ctrl - > value = rt - > curvol ;
2007-04-25 00:14:36 -03:00
return 0 ;
}
return - EINVAL ;
}
2006-08-08 09:10:01 -03:00
2007-04-25 00:14:36 -03:00
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-03-06 13:45:27 -03:00
struct rtrack * rt = video_drvdata ( file ) ;
2006-08-08 09:10:01 -03:00
2007-04-25 00:14:36 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( ctrl - > value )
rt_mute ( rt ) ;
else
2009-03-06 13:45:27 -03:00
rt_setvol ( rt , rt - > curvol ) ;
2007-04-25 00:14:36 -03:00
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
2009-03-06 13:45:27 -03:00
rt_setvol ( rt , ctrl - > value ) ;
2007-04-25 00:14:36 -03:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-04-25 00:14:36 -03:00
return - EINVAL ;
}
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 )
{
2009-03-06 13:45:27 -03:00
return i ? - EINVAL : 0 ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:45:27 -03:00
static int vidioc_g_audio ( struct file * file , void * priv ,
2007-04-25 00:14:36 -03:00
struct v4l2_audio * a )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
a - > index = 0 ;
strlcpy ( a - > name , " Radio " , sizeof ( a - > name ) ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
2007-04-25 00:14:36 -03:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-03-06 13:45:27 -03:00
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
return a - > index ? - EINVAL : 0 ;
}
2005-04-16 15:20:36 -07:00
2008-12-30 06:58:20 -03:00
static const struct v4l2_file_operations rtrack_fops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
2007-04-25 00:14:36 -03:00
. ioctl = video_ioctl2 ,
2005-04-16 15:20:36 -07:00
} ;
2008-07-21 02:57:38 -03:00
static const struct v4l2_ioctl_ops rtrack_ioctl_ops = {
2007-04-25 00:14:36 -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 __init rtrack_init ( void )
{
2009-03-06 13:45:27 -03:00
struct rtrack * rt = & rtrack_card ;
struct v4l2_device * v4l2_dev = & rt - > v4l2_dev ;
int res ;
strlcpy ( v4l2_dev - > name , " rtrack " , sizeof ( v4l2_dev - > name ) ) ;
rt - > io = io ;
if ( rt - > io = = - 1 ) {
2009-03-09 08:11:21 -03:00
v4l2_err ( v4l2_dev , " you must set an I/O address with io=0x20f or 0x30f \n " ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
2009-03-06 13:45:27 -03:00
if ( ! request_region ( rt - > io , 2 , " rtrack " ) ) {
v4l2_err ( v4l2_dev , " port 0x%x already in use \n " , rt - > io ) ;
2005-04-16 15:20:36 -07:00
return - EBUSY ;
}
2009-03-06 13:45:27 -03:00
res = v4l2_device_register ( NULL , v4l2_dev ) ;
if ( res < 0 ) {
release_region ( rt - > io , 2 ) ;
v4l2_err ( v4l2_dev , " could not register v4l2_device \n " ) ;
return res ;
}
2006-04-08 16:06:16 -03:00
2009-03-06 13:45:27 -03:00
strlcpy ( rt - > vdev . name , v4l2_dev - > name , sizeof ( rt - > vdev . name ) ) ;
rt - > vdev . v4l2_dev = v4l2_dev ;
rt - > vdev . fops = & rtrack_fops ;
rt - > vdev . ioctl_ops = & rtrack_ioctl_ops ;
rt - > vdev . release = video_device_release_empty ;
video_set_drvdata ( & rt - > vdev , rt ) ;
if ( video_register_device ( & rt - > vdev , VFL_TYPE_RADIO , radio_nr ) < 0 ) {
v4l2_device_unregister ( & rt - > v4l2_dev ) ;
release_region ( rt - > io , 2 ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
2009-03-06 13:45:27 -03:00
v4l2_info ( v4l2_dev , " AIMSlab RadioTrack/RadioReveal card driver. \n " ) ;
2005-04-16 15:20:36 -07:00
/* Set up the I/O locking */
2006-04-08 16:06:16 -03:00
2009-03-06 13:45:27 -03:00
mutex_init ( & rt - > lock ) ;
2006-04-08 16:06:16 -03:00
/* mute card - prevents noisy bootups */
2005-04-16 15:20:36 -07:00
/* this ensures that the volume is all the way down */
2009-03-06 13:45:27 -03:00
outb ( 0x48 , rt - > io ) ; /* volume down but still "on" */
2005-04-16 15:20:36 -07:00
sleep_delay ( 2000000 ) ; /* make sure it's totally down */
2009-03-06 13:45:27 -03:00
outb ( 0xc0 , rt - > io ) ; /* steady volume, mute card */
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-03-06 13:45:27 -03:00
static void __exit rtrack_exit ( void )
2005-04-16 15:20:36 -07:00
{
2009-03-06 13:45:27 -03:00
struct rtrack * rt = & rtrack_card ;
video_unregister_device ( & rt - > vdev ) ;
v4l2_device_unregister ( & rt - > v4l2_dev ) ;
release_region ( rt - > io , 2 ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( rtrack_init ) ;
2009-03-06 13:45:27 -03:00
module_exit ( rtrack_exit ) ;
2005-04-16 15:20:36 -07:00