radio-si470x.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,789 行 · 第 1/4 页
C
1,789 行
static int si470x_vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *audio){ /* driver constants */ audio->index = 0; strcpy(audio->name, "Radio"); audio->capability = V4L2_AUDCAP_STEREO; audio->mode = 0; return 0;}/* * si470x_vidioc_g_tuner - get tuner attributes */static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner){ struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ if (radio->disconnected) { retval = -EIO; goto done; } if (tuner->index != 0) { retval = -EINVAL; goto done; } retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) goto done; /* driver constants */ strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; /* range limits */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { /* 0: 87.5 - 108 MHz (USA, Europe, default) */ default: tuner->rangelow = 87.5 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; break; /* 1: 76 - 108 MHz (Japan wide band) */ case 1 : tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; break; /* 2: 76 - 90 MHz (Japan) */ case 2 : tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 90 * FREQ_MUL; break; }; /* stereo indicator == stereo (instead of mono) */ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1) tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; else tuner->rxsubchans = V4L2_TUNER_SUB_MONO; /* mono/stereo selector */ if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 1) tuner->audmode = V4L2_TUNER_MODE_MONO; else tuner->audmode = V4L2_TUNER_MODE_STEREO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI) * 0x0101; /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": get tuner failed with %d\n", retval); return retval;}/* * si470x_vidioc_s_tuner - set tuner attributes */static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner){ struct si470x_device *radio = video_drvdata(file); int retval = -EINVAL; /* safety checks */ if (radio->disconnected) { retval = -EIO; goto done; } if (tuner->index != 0) goto done; /* mono/stereo selector */ switch (tuner->audmode) { case V4L2_TUNER_MODE_MONO: radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ break; case V4L2_TUNER_MODE_STEREO: radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ break; default: goto done; } retval = si470x_set_register(radio, POWERCFG);done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set tuner failed with %d\n", retval); return retval;}/* * si470x_vidioc_g_frequency - get tuner or modulator radio frequency */static int si470x_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq){ struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ if (radio->disconnected) { retval = -EIO; goto done; } if (freq->tuner != 0) { retval = -EINVAL; goto done; } freq->type = V4L2_TUNER_RADIO; retval = si470x_get_freq(radio, &freq->frequency);done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": get frequency failed with %d\n", retval); return retval;}/* * si470x_vidioc_s_frequency - set tuner or modulator radio frequency */static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *freq){ struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ if (radio->disconnected) { retval = -EIO; goto done; } if (freq->tuner != 0) { retval = -EINVAL; goto done; } retval = si470x_set_freq(radio, freq->frequency);done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set frequency failed with %d\n", retval); return retval;}/* * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek */static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, struct v4l2_hw_freq_seek *seek){ struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ if (radio->disconnected) { retval = -EIO; goto done; } if (seek->tuner != 0) { retval = -EINVAL; goto done; } retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set hardware frequency seek failed with %d\n", retval); return retval;}/* * si470x_ioctl_ops - video device ioctl operations */static const struct v4l2_ioctl_ops si470x_ioctl_ops = { .vidioc_querycap = si470x_vidioc_querycap, .vidioc_queryctrl = si470x_vidioc_queryctrl, .vidioc_g_ctrl = si470x_vidioc_g_ctrl, .vidioc_s_ctrl = si470x_vidioc_s_ctrl, .vidioc_g_audio = si470x_vidioc_g_audio, .vidioc_g_tuner = si470x_vidioc_g_tuner, .vidioc_s_tuner = si470x_vidioc_s_tuner, .vidioc_g_frequency = si470x_vidioc_g_frequency, .vidioc_s_frequency = si470x_vidioc_s_frequency, .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,};/* * si470x_viddev_template - video device interface */static struct video_device si470x_viddev_template = { .fops = &si470x_fops, .name = DRIVER_NAME, .release = video_device_release, .ioctl_ops = &si470x_ioctl_ops,};/************************************************************************** * USB Interface **************************************************************************//* * si470x_usb_driver_probe - probe for the device */static int si470x_usb_driver_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct si470x_device *radio; int retval = 0; /* private data allocation and initialization */ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); if (!radio) { retval = -ENOMEM; goto err_initial; } radio->users = 0; radio->disconnected = 0; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; mutex_init(&radio->disconnect_lock); mutex_init(&radio->lock); /* video device allocation and initialization */ radio->videodev = video_device_alloc(); if (!radio->videodev) { retval = -ENOMEM; goto err_radio; } memcpy(radio->videodev, &si470x_viddev_template, sizeof(si470x_viddev_template)); video_set_drvdata(radio->videodev, radio); /* show some infos about the specific device */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; goto err_all; } printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); /* check if firmware is current */ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_SW_VERSION_CURRENT) { printk(KERN_WARNING DRIVER_NAME ": This driver is known to work with " "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT); printk(KERN_WARNING DRIVER_NAME ": but the device has firmware version %hu.\n", radio->registers[CHIPID] & CHIPID_FIRMWARE); printk(KERN_WARNING DRIVER_NAME ": If you have some trouble using this driver,\n"); printk(KERN_WARNING DRIVER_NAME ": please report to V4L ML at " "video4linux-list@redhat.com\n"); } /* set initial frequency */ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ /* rds buffer allocation */ radio->buf_size = rds_buf * 3; radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); if (!radio->buffer) { retval = -EIO; goto err_all; } /* rds buffer configuration */ radio->wr_index = 0; radio->rd_index = 0; init_waitqueue_head(&radio->read_queue); /* prepare rds work function */ INIT_DELAYED_WORK(&radio->work, si470x_work); /* register video device */ retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); if (retval) { printk(KERN_WARNING DRIVER_NAME ": Could not register video device\n"); goto err_all; } usb_set_intfdata(intf, radio); return 0;err_all: video_device_release(radio->videodev); kfree(radio->buffer);err_radio: kfree(radio);err_initial: return retval;}/* * si470x_usb_driver_suspend - suspend the device */static int si470x_usb_driver_suspend(struct usb_interface *intf, pm_message_t message){ struct si470x_device *radio = usb_get_intfdata(intf); printk(KERN_INFO DRIVER_NAME ": suspending now...\n"); cancel_delayed_work_sync(&radio->work); return 0;}/* * si470x_usb_driver_resume - resume the device */static int si470x_usb_driver_resume(struct usb_interface *intf){ struct si470x_device *radio = usb_get_intfdata(intf); printk(KERN_INFO DRIVER_NAME ": resuming now...\n"); mutex_lock(&radio->lock); if (radio->users && radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) schedule_delayed_work(&radio->work, msecs_to_jiffies(rds_poll_time)); mutex_unlock(&radio->lock); return 0;}/* * si470x_usb_driver_disconnect - disconnect the device */static void si470x_usb_driver_disconnect(struct usb_interface *intf){ struct si470x_device *radio = usb_get_intfdata(intf); mutex_lock(&radio->disconnect_lock); radio->disconnected = 1; cancel_delayed_work_sync(&radio->work); usb_set_intfdata(intf, NULL); if (radio->users == 0) { video_unregister_device(radio->videodev); kfree(radio->buffer); kfree(radio); } mutex_unlock(&radio->disconnect_lock);}/* * si470x_usb_driver - usb driver interface */static struct usb_driver si470x_usb_driver = { .name = DRIVER_NAME, .probe = si470x_usb_driver_probe, .disconnect = si470x_usb_driver_disconnect, .suspend = si470x_usb_driver_suspend, .resume = si470x_usb_driver_resume, .id_table = si470x_usb_driver_id_table, .supports_autosuspend = 1,};/************************************************************************** * Module Interface **************************************************************************//* * si470x_module_init - module init */static int __init si470x_module_init(void){ printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); return usb_register(&si470x_usb_driver);}/* * si470x_module_exit - module exit */static void __exit si470x_module_exit(void){ usb_deregister(&si470x_usb_driver);}module_init(si470x_module_init);module_exit(si470x_module_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_VERSION(DRIVER_VERSION);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?