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 + -
显示快捷键?