📄 radio-cadet.c
字号:
/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card * * by Fred Gleason <fredg@wava.com> * Version 0.3.3 * * (Loosely) based on code for the Aztech radio card by * * Russell Kroll (rkroll@exploits.org) * Quay Ly * Donald Song * Jason Lewis (jlewis@twilight.vtc.vsc.edu) * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) * William McGrath (wmcgrath@twilight.vtc.vsc.edu) * * History: * 2000-04-29 Russell Kroll <rkroll@exploits.org> * Added ISAPnP detection for Linux 2.3/2.4 * * 2001-01-10 Russell Kroll <rkroll@exploits.org> * Removed dead CONFIG_RADIO_CADET_PORT code * PnP detection on load is now default (no args necessary) * * 2002-01-17 Adam Belay <ambx1@neo.rr.com> * Updated to latest pnp code * * 2003-01-31 Alan Cox <alan@redhat.com> * Cleaned up locking, delay code, general odds and ends * * 2006-07-30 Hans J. Koch <koch@hjk-az.de> * Changed API to V4L2 */#include <linux/version.h>#include <linux/module.h> /* Modules */#include <linux/init.h> /* Initdata */#include <linux/ioport.h> /* request_region */#include <linux/delay.h> /* udelay */#include <asm/io.h> /* outb, outb_p */#include <asm/uaccess.h> /* copy to/from user */#include "compat.h"#include <linux/videodev2.h> /* V4L2 API defs */#include <media/v4l2-common.h>#include <media/v4l2-ioctl.h>#include <linux/param.h>#include <linux/pnp.h>#define RDS_BUFFER 256#define RDS_RX_FLAG 1#define MBS_RX_FLAG 2#define CADET_VERSION KERNEL_VERSION(0,3,3)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 = 0xff, .step = 1, .default_value = 0xff, .type = V4L2_CTRL_TYPE_INTEGER, }};static int io=-1; /* default to isapnp activation */static int radio_nr = -1;static int users;static int curtuner;static int tunestat;static int sigstrength;static wait_queue_head_t read_queue;static struct timer_list readtimer;static __u8 rdsin, rdsout, rdsstat;static unsigned char rdsbuf[RDS_BUFFER];static spinlock_t cadet_io_lock;static int cadet_probe(void);/* * Signal Strength Threshold Values * The V4L API spec does not define any particular unit for the signal * strength value. These values are in microvolts of RF at the tuner's input. */static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}};#if 0/*Note: cadet_getrds() is not used at the moment. It will be useful for futureextensions, e.g. an ioctl to query RDS reception quality. - Hans J. Koch*/static intcadet_getrds(void){ int rds_mbs_stat=0; spin_lock(&cadet_io_lock); outb(3,io); /* Select Decoder Control/Status */ outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ spin_unlock(&cadet_io_lock); msleep(100); spin_lock(&cadet_io_lock); outb(3,io); /* Select Decoder Control/Status */ if((inb(io+1)&0x80)!=0) { rds_mbs_stat |= RDS_RX_FLAG; } if((inb(io+1)&0x10)!=0) { rds_mbs_stat |= MBS_RX_FLAG; } spin_unlock(&cadet_io_lock); return rds_mbs_stat;}#endifstatic intcadet_getstereo(void){ int ret = V4L2_TUNER_SUB_MONO; if(curtuner != 0) /* Only FM has stereo capability! */ return V4L2_TUNER_SUB_MONO; spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ if( (inb(io+1) & 0x40) == 0) ret = V4L2_TUNER_SUB_STEREO; spin_unlock(&cadet_io_lock); return ret;}static unsignedcadet_gettune(void){ int curvol,i; unsigned fifo=0; /* * Prepare for read */ spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ curvol=inb(io+1); /* Save current volume/mute setting */ outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */ tunestat=0xffff; /* * Read the shift register */ for(i=0;i<25;i++) { fifo=(fifo<<1)|((inb(io+1)>>7)&0x01); if(i<24) { outb(0x01,io+1); tunestat&=inb(io+1); outb(0x00,io+1); } } /* * Restore volume/mute setting */ outb(curvol,io+1); spin_unlock(&cadet_io_lock); return fifo;}static unsignedcadet_getfreq(void){ int i; unsigned freq=0,test,fifo=0; /* * Read current tuning */ fifo=cadet_gettune(); /* * Convert to actual frequency */ if(curtuner==0) { /* FM */ test=12500; for(i=0;i<14;i++) { if((fifo&0x01)!=0) { freq+=test; } test=test<<1; fifo=fifo>>1; } freq-=10700000; /* IF frequency is 10.7 MHz */ freq=(freq*16)/1000000; /* Make it 1/16 MHz */ } if(curtuner==1) { /* AM */ freq=((fifo&0x7fff)-2010)*16; } return freq;}static voidcadet_settune(unsigned fifo){ int i; unsigned test; spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ /* * Write the shift register */ test=0; test=(fifo>>23)&0x02; /* Align data for SDO */ test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */ outb(7,io); /* Select tuner control */ outb(test,io+1); /* Initialize for write */ for(i=0;i<25;i++) { test|=0x01; /* Toggle SCK High */ outb(test,io+1); test&=0xfe; /* Toggle SCK Low */ outb(test,io+1); fifo=fifo<<1; /* Prepare the next bit */ test=0x1c|((fifo>>23)&0x02); outb(test,io+1); } spin_unlock(&cadet_io_lock);}static voidcadet_setfreq(unsigned freq){ unsigned fifo; int i,j,test; int curvol; /* * Formulate a fifo command */ fifo=0; if(curtuner==0) { /* FM */ test=102400; freq=(freq*1000)/16; /* Make it kHz */ freq+=10700; /* IF is 10700 kHz */ for(i=0;i<14;i++) { fifo=fifo<<1; if(freq>=test) { fifo|=0x01; freq-=test; } test=test>>1; } } if(curtuner==1) { /* AM */ fifo=(freq/16)+2010; /* Make it kHz */ fifo|=0x100000; /* Select AM Band */ } /* * Save current volume/mute setting */ spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ curvol=inb(io+1); spin_unlock(&cadet_io_lock); /* * Tune the card */ for(j=3;j>-1;j--) { cadet_settune(fifo|(j<<16)); spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ outb(curvol,io+1); spin_unlock(&cadet_io_lock); msleep(100); cadet_gettune(); if((tunestat & 0x40) == 0) { /* Tuned */ sigstrength=sigtable[curtuner][j]; return; } } sigstrength=0;}static intcadet_getvol(void){ int ret = 0; spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ if((inb(io + 1) & 0x20) != 0) ret = 0xffff; spin_unlock(&cadet_io_lock); return ret;}static voidcadet_setvol(int vol){ spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ if(vol>0) outb(0x20,io+1); else outb(0x00,io+1); spin_unlock(&cadet_io_lock);}static voidcadet_handler(unsigned long data){ /* * Service the RDS fifo */ if(spin_trylock(&cadet_io_lock)) { outb(0x3,io); /* Select RDS Decoder Control */ if((inb(io+1)&0x20)!=0) { printk(KERN_CRIT "cadet: RDS fifo overflow\n"); } outb(0x80,io); /* Select RDS fifo */ while((inb(io)&0x80)!=0) { rdsbuf[rdsin]=inb(io+1); if(rdsin==rdsout) printk(KERN_WARNING "cadet: RDS buffer overflow\n"); else rdsin++; } spin_unlock(&cadet_io_lock); } /* * Service pending read */ if( rdsin!=rdsout) wake_up_interruptible(&read_queue); /* * Clean up and exit */ init_timer(&readtimer); readtimer.function=cadet_handler; readtimer.data=(unsigned long)0; readtimer.expires=jiffies+msecs_to_jiffies(50); add_timer(&readtimer);}static ssize_tcadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos){ int i=0; unsigned char readbuf[RDS_BUFFER];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -