radio-si4703.c
资源名称:fmradio.zip [点击查看]
上传用户:ledjyj
上传日期:2014-08-27
资源大小:2639k
文件大小:14k
源码类别:
驱动编程
开发平台:
Unix_Linux
- /* SI4703 Radio Card driver for radio support
- * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de>
- *
- * Card manufacturer:
- * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e
- *
- * Notes on the hardware
- *
- * This card has two output sockets, one for speakers and one for line.
- * The speaker output has volume control, but only in four discrete
- * steps. The line output has neither volume control nor mute.
- *
- * The card has auto-stereo according to its manual, although it all
- * sounds mono to me (even with the Win/DOS drivers). Maybe it's my
- * antenna - I really don't know for sure.
- *
- * Frequency control is done digitally.
- *
- * Volume control is done digitally, but there are only four different
- * possible values. So you should better always turn the volume up and
- * use line control. I got the best results by connecting line output
- * to the sound card microphone input. For such a configuration the
- * volume control has no effect, since volume control only influences
- * the speaker output.
- *
- * There is no explicit mute/unmute. So I set the radio frequency to a
- * value where I do expect just noise and turn the speaker volume down.
- * The frequency change is necessary since the card never seems to be
- * completely silent.
- */
- #include <linux/module.h> /* Modules */
- #include <linux/init.h> /* Initdata */
- #include <linux/ioport.h> /* request_region */
- #include <linux/proc_fs.h> /* radio card status report */
- #include <asm/io.h> /* outb, outb_p */
- #include <asm/uaccess.h> /* copy to/from user */
- #include <linux/videodev.h> /* kernel radio structs */
- #include <linux/config.h> /* CONFIG_RADIO_SI4703_* */
- #include <linux/types.h>
- #include <linux/interrupt.h>
- #include <linux/workqueue.h>
- #include "SI4703API.h"
- #define BANNER "SI4703 Radio Card driver v0.1n"
- #ifndef CONFIG_RADIO_SI4703_MUTEFREQ
- #define CONFIG_RADIO_SI4703_MUTEFREQ 0
- #endif
- #ifndef CONFIG_PROC_FS
- #undef CONFIG_RADIO_SI4703_PROC_FS
- #endif
- #ifndef TRUE
- #define TRUE 1
- #endif
- #ifndef FALSE
- #define FALSE 0
- #endif
- struct SI4703_device {
- int users;
- int curvol;
- int muted;
- unsigned long curfreq;
- unsigned long mutefreq;
- struct mutex lock;
- __u32 audmode;
- struct RDS_DATA irq_data;
- spinlock_t irq_lock;
- wait_queue_head_t irq_queue;
- struct workqueue_struct *radio_irq_workq;
- struct work_struct fm_event_work;
- struct fasync_struct *async_queue;
- };
- static struct SI4703_device SI4703_unit =
- {
- .curfreq = CONFIG_RADIO_SI4703_MUTEFREQ,
- .mutefreq = CONFIG_RADIO_SI4703_MUTEFREQ,
- };
- static int SI4703_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
- static int SI4703_Read(struct file *file, char __user *arg, size_t count, loff_t *offset);
- #ifdef CONFIG_RADIO_SI4703_PROC_FS
- static int SI4703_get_info(char *buf, char **start, off_t offset, int len);
- #endif
- static void query_radio_ctrl(struct v4l2_queryctrl *param);
- static void get_radio_ctrl(struct v4l2_control *param);
- static void set_radio_ctrl(struct v4l2_control *param);
- static int SI4703_init_irq(void);
- static int SI4703_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
- {
- struct video_device *dev = video_devdata(file);
- struct SI4703_device *SI4703 = dev->priv;
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- strcpy(v->driver, "SI4703");
- return 0;
- }
- case VIDIOC_RADIO_ON:
- {
- printk(KERN_ERR "radio-SI4703: ioctl radio onn");
- FM_SI47XX_PowerUp();
- return 0;
- }
- case VIDIOC_RADIO_OFF:
- {
- printk(KERN_ERR "radio-SI4703: ioctl radio offn");
- FM_SI47XX_PowerDown();
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
- // if (v->tuner) /* Only 1 tuner */
- // return -EINVAL;
- printk(KERN_ERR "radio-SI4703: ioctl get tunern");
- v->rangelow = 8750;
- v->rangehigh = 10800;
- v->type = V4L2_TUNER_RADIO;
- v->capability = V4L2_TUNER_CAP_STEREO;
- v->signal = FM_SI47XX_ReadRSSI(); /* get the signal strength */
- strcpy(v->name, "SI4703");
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
- // if (v->tuner != 0)
- // return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
- printk(KERN_ERR "radio-SI4703: ioctl set tunern");
- SI4703->audmode = v->audmode;
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *freq = arg;
- printk(KERN_ERR "radio-SI4703: ioctl get freqn");
- freq->frequency = SI4703->curfreq;
- freq->type = V4L2_TUNER_RADIO;
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *freq = arg;
- printk(KERN_ERR "radio-SI4703: ioctl set freqn");
- SI4703->curfreq = freq->frequency;
- FM_SI47XX_Tune(SI4703->curfreq);
- return 0;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *v = arg;
- query_radio_ctrl(v);
- return 0;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control * v = arg;
- get_radio_ctrl(v);
- return 0;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control * v = arg;
- printk(KERN_ERR "radio-SI4703: ioctl set ctrln");
- set_radio_ctrl(v);
- return 0;
- }
- case RADIO_SCAN_NEXT:
- {
- struct radio_scan *v = arg;
- if(SEEKUP == v->direction) {
- printk(KERN_ERR "radio-SI4703: ioctl seek upn");
- FM_SI47XX_Seek(SEEKUP,&v->channel, &v->scanok);
- }
- if(SEEKDOWN == v->direction) {
- printk(KERN_ERR "radio-SI4703: ioctl seek downn");
- FM_SI47XX_Seek(SEEKDOWN, &v->channel,&v->scanok);
- }
- return 0;
- }
- default:
- return -ENOIOCTLCMD;
- }
- }
- static void query_radio_ctrl(struct v4l2_queryctrl *param)
- {
- struct v4l2_queryctrl *v = param;
- switch (v->id) {
- case V4L2_CID_AUDIO_VOLUME:
- {
- v->type = V4L2_CTRL_TYPE_INTEGER;
- v->minimum = 0;
- v->maximum = 100;
- v->step =20;
- return;
- }
- case V4L2_CID_AUDIO_MUTE:
- {
- v->type = V4L2_CTRL_TYPE_BOOLEAN;
- return;
- }
- default:
- {
- v->flags = V4L2_CTRL_FLAG_DISABLED;
- return;
- }
- }
- }
- static void get_radio_ctrl(struct v4l2_control *param)
- {
- struct v4l2_control *v = param;
- switch (v->id) {
- case V4L2_CID_AUDIO_VOLUME:
- {
- v->value = SI4703_unit.curvol;
- return;
- }
- case V4L2_CID_AUDIO_MUTE:
- {
- v->value = SI4703_unit.muted ;
- return;
- }
- default:
- {
- v->value = -1;
- return;
- }
- }
- }
- static void set_radio_ctrl(struct v4l2_control *param)
- {
- struct v4l2_control *v = param;
- switch (v->id) {
- case V4L2_CID_AUDIO_VOLUME:
- {
- FM_SI47XX_Volume(v->value);
- SI4703_unit.curvol = v->value;
- return;
- }
- case V4L2_CID_AUDIO_MUTE:
- {
- if (1 == v->value) {
- FM_SI47XX_MuteVolume();
- SI4703_unit.muted =1;
- }
- else {
- FM_SI47XX_DMuteVolume();
- SI4703_unit.muted =0;
- }
- return;
- }
- default:
- {
- printk(KERN_ERR "radio-SI4703: error controln");
- return;
- }
- }
- }
- static int SI4703_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
- {
- printk(KERN_ERR "radio-SI4703: enter ioctln");
- return video_usercopy(inode, file, cmd, arg, SI4703_do_ioctl);
- }
- static irqreturn_t si4703_radio_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
- {
- struct SI4703_device *radio = &SI4703_unit;
- printk(KERN_ERR "radio-SI4703: interruptn");
- //spin_lock(&radio->irq_lock);
- // radio->irq_data.RDSA = ReadRDS_BLOCK(RDSA);
- // radio->irq_data.RDSB = ReadRDS_BLOCK(RDSB);
- // radio->irq_data.RDSC = ReadRDS_BLOCK(RDSC);
- // radio->irq_data.RDSD= ReadRDS_BLOCK(RDSD);
- // radio->irq_data.RDSA = 100;
- //spin_unlock(&radio->irq_lock);
- disable_irq(IRQ_GPIO(113));
- queue_work(radio->radio_irq_workq, &radio->fm_event_work);
- return IRQ_HANDLED;
- }
- static int SI4703_Read(struct file *file, char __user *arg, size_t count, loff_t *offset)
- {
- struct SI4703_device *radio = &SI4703_unit;
- DECLARE_WAITQUEUE(wait, current);
- struct RDS_DATA data;
- ssize_t ret;
- //if (count != sizeof(unsigned int) && count < sizeof(unsigned long))
- //return -EINVAL;
- add_wait_queue(&radio->irq_queue, &wait);
- do {
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_lock_irq(&radio->irq_lock);
- data = radio->irq_data;
- radio->irq_data.RDSA = 0;
- spin_unlock_irq(&radio->irq_lock);
- if (data.RDSA != 0) {
- ret = 0;
- break;
- }
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- schedule();
- } while (1);
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&radio->irq_queue, &wait);
- /*if (ret == 0) {
- if (radio->read_callback)
- data = radio->read_callback(rtc->class_dev.dev,
- data);}*/
- return copy_to_user(arg,&data,sizeof(data))? -EFAULT : sizeof(data);
- }
- static unsigned int SI4703_dev_poll(struct file *file, poll_table *wait)
- {
- struct SI4703_device *radio = &SI4703_unit;
- struct RDS_DATA data;
- poll_wait(file, &radio->irq_queue, wait);
- data = radio->irq_data;
- return (data.RDSA != 0) ? (POLLIN | POLLRDNORM) : 0;
- }
- static int SI4703_dev_fasync(int fd, struct file *file, int on)
- {
- struct SI4703_device *radio = &SI4703_unit;
- return fasync_helper(fd, file, on, &radio->async_queue);
- }
- static void radio_fm_irq_worker(void *ptr)
- {
- //spin_lock(&radio->irq_lock);
- struct SI4703_device *radio = ptr;
- radio->irq_data.RDSA = ReadRDS_BLOCK(RDSA);
- radio->irq_data.RDSB = ReadRDS_BLOCK(RDSB);
- radio->irq_data.RDSC = ReadRDS_BLOCK(RDSC);
- radio->irq_data.RDSD= ReadRDS_BLOCK(RDSD);
- radio->irq_data.RDSA = 100;
- wake_up_interruptible(&radio->irq_queue);
- kill_fasync(&radio->async_queue, SIGIO, POLL_IN);
- enable_irq(IRQ_GPIO(113));
- //spin_unlock(&radio->irq_lock);
- }
- static int SI4703_init_irq(void)
- {
- struct SI4703_device *radio = &SI4703_unit;
- INIT_WORK(&radio->fm_event_work, radio_fm_irq_worker, radio);
- if ((radio->radio_irq_workq =
- create_singlethread_workqueue("si4703radio")) == NULL) {
- printk(KERN_ERR"could not create radio irq work queuen");
- return -EINVAL;
- }
- if(request_irq (IRQ_GPIO(113), si4703_radio_interrupt, SA_INTERRUPT, "si4703_radio", NULL))
- {
- printk(KERN_ERR "radio-SI4703: error request irqn");
- destroy_workqueue(radio->radio_irq_workq);
- free_irq(IRQ_GPIO(113),NULL);
- return -EINVAL;
- }
- return 0;
- }
- static struct file_operations SI4703_fops = {
- .owner = THIS_MODULE,
- .open = video_exclusive_open,
- .read = SI4703_Read,
- .release = video_exclusive_release,
- .ioctl = SI4703_ioctl,
- .poll = SI4703_dev_poll,
- .fasync = SI4703_dev_fasync,
- .compat_ioctl = v4l_compat_ioctl32,
- .llseek = no_llseek,
- };
- static struct video_device SI4703_radio =
- {
- .owner = THIS_MODULE,
- .name = "SI4703 Radio",
- .type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_SI4703,
- .fops = &SI4703_fops,
- };
- #ifdef CONFIG_RADIO_SI4703_PROC_FS
- static int SI4703_get_info(char *buf, char **start, off_t offset, int len)
- {
- char *out = buf;
- #ifdef MODULE
- #define MODULEPROCSTRING "Driver loaded as a module"
- #else
- #define MODULEPROCSTRING "Driver compiled into kernel"
- #endif
- /* output must be kept under PAGE_SIZE */
- out += sprintf(out, BANNER);
- out += sprintf(out, "Load type: " MODULEPROCSTRING "nn");
- out += sprintf(out, "frequency = %lu kHzn",
- SI4703_unit.curfreq >> 4);
- out += sprintf(out, "volume = %dn", SI4703_unit.curvol);
- out += sprintf(out, "mute = %sn", SI4703_unit.muted ?
- "on" : "off");
- out += sprintf(out, "mute frequency = %lu kHzn",
- SI4703_unit.mutefreq >> 4);
- return out - buf;
- }
- #endif /* CONFIG_RADIO_SI4703_PROC_FS */
- MODULE_AUTHOR("Beck He");
- MODULE_DESCRIPTION("A driver for the SI4703 radio card .");
- MODULE_LICENSE("GPL");
- static int radio_nr = -1;
- module_param(radio_nr, int, 0);
- #ifdef MODULE
- static unsigned long mutefreq = 0;
- module_param(mutefreq, ulong, 0);
- MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
- #endif
- static int __init SI4703_init(void)
- {
- struct SI4703_device *radio = &SI4703_unit;
- #ifdef MODULE
- if (mutefreq < 87000 || mutefreq > 108500) {
- printk(KERN_ERR "radio-SI4703: You must set a frequency (in kHz) used when muting the card,n");
- printk(KERN_ERR "radio-SI4703: e.g. with "mutefreq=87500" (87000 <= mutefreq <= 108500)n");
- return -EINVAL;
- }
- SI4703_unit.mutefreq = mutefreq;
- #endif /* MODULE */
- printk(KERN_INFO BANNER);
- mutex_init(&radio->lock);
- spin_lock_init(&radio->irq_lock);
- init_waitqueue_head(&radio->irq_queue);
- SI4703_radio.priv = &SI4703_unit;
- if (video_register_device(&SI4703_radio, VFL_TYPE_RADIO, radio_nr) == -1)
- {
- return -EINVAL;
- }
- FM_SI47XX_Init();
- SI4703_init_irq();
- //printk(KERN_INFO "radio-SI4703: mute frequency is %lu kHz.n",
- // SI4703_unit.mutefreq);
- //SI4703_unit.mutefreq <<= 4;
- /* mute card - prevents noisy bootups */
- // SI4703_mute(&SI4703_unit);
- #ifdef CONFIG_RADIO_SI4703_PROC_FS
- if (!create_proc_info_entry("driver/radio-SI4703", 0, NULL,
- SI4703_get_info))
- printk(KERN_ERR "radio-SI4703: registering /proc/driver/radio-SI4703 failedn");
- #endif
- return 0;
- }
- static void __exit SI4703_cleanup_module(void)
- {
- #ifdef CONFIG_RADIO_SI4703_PROC_FS
- remove_proc_entry("driver/radio-SI4703", NULL);
- #endif
- free_irq(IRQ_GPIO(113),NULL);
- video_unregister_device(&SI4703_radio);
- }
- module_init(SI4703_init);
- module_exit(SI4703_cleanup_module);