radio-si470x.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,789 行 · 第 1/4 页

C
1,789
字号
 * si470x_device - private data */struct si470x_device {	/* reference to USB and video device */	struct usb_device *usbdev;	struct usb_interface *intf;	struct video_device *videodev;	/* driver management */	unsigned int users;	unsigned char disconnected;	struct mutex disconnect_lock;	/* Silabs internal registers (0..15) */	unsigned short registers[RADIO_REGISTER_NUM];	/* RDS receive buffer */#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)	struct work_struct work;#else	struct delayed_work work;#endif	wait_queue_head_t read_queue;	struct mutex lock;		/* buffer locking */	unsigned char *buffer;		/* size is always multiple of three */	unsigned int buf_size;	unsigned int rd_index;	unsigned int wr_index;};/* * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, * 62.5 kHz otherwise. * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000 */#define FREQ_MUL (1000000 / 62.5)/************************************************************************** * General Driver Functions **************************************************************************//* * si470x_get_report - receive a HID report */static int si470x_get_report(struct si470x_device *radio, void *buf, int size){	unsigned char *report = (unsigned char *) buf;	int retval;	retval = usb_control_msg(radio->usbdev,		usb_rcvctrlpipe(radio->usbdev, 0),		HID_REQ_GET_REPORT,		USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,		report[0], 2,		buf, size, usb_timeout);	if (retval < 0)		printk(KERN_WARNING DRIVER_NAME			": si470x_get_report: usb_control_msg returned %d\n",			retval);	return retval;}/* * si470x_set_report - send a HID report */static int si470x_set_report(struct si470x_device *radio, void *buf, int size){	unsigned char *report = (unsigned char *) buf;	int retval;	retval = usb_control_msg(radio->usbdev,		usb_sndctrlpipe(radio->usbdev, 0),		HID_REQ_SET_REPORT,		USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,		report[0], 2,		buf, size, usb_timeout);	if (retval < 0)		printk(KERN_WARNING DRIVER_NAME			": si470x_set_report: usb_control_msg returned %d\n",			retval);	return retval;}/* * si470x_get_register - read register */static int si470x_get_register(struct si470x_device *radio, int regnr){	unsigned char buf[REGISTER_REPORT_SIZE];	int retval;	buf[0] = REGISTER_REPORT(regnr);	retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));	if (retval >= 0)		radio->registers[regnr] = get_unaligned_be16(&buf[1]);	return (retval < 0) ? -EINVAL : 0;}/* * si470x_set_register - write register */static int si470x_set_register(struct si470x_device *radio, int regnr){	unsigned char buf[REGISTER_REPORT_SIZE];	int retval;	buf[0] = REGISTER_REPORT(regnr);	put_unaligned_be16(radio->registers[regnr], &buf[1]);	retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));	return (retval < 0) ? -EINVAL : 0;}/* * si470x_get_all_registers - read entire registers */static int si470x_get_all_registers(struct si470x_device *radio){	unsigned char buf[ENTIRE_REPORT_SIZE];	int retval;	unsigned char regnr;	buf[0] = ENTIRE_REPORT;	retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));	if (retval >= 0)		for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)			radio->registers[regnr] = get_unaligned_be16(				&buf[regnr * RADIO_REGISTER_SIZE + 1]);	return (retval < 0) ? -EINVAL : 0;}/* * si470x_get_rds_registers - read rds registers */static int si470x_get_rds_registers(struct si470x_device *radio){	unsigned char buf[RDS_REPORT_SIZE];	int retval;	int size;	unsigned char regnr;	buf[0] = RDS_REPORT;	retval = usb_interrupt_msg(radio->usbdev,		usb_rcvintpipe(radio->usbdev, 1),		(void *) &buf, sizeof(buf), &size, usb_timeout);	if (size != sizeof(buf))		printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "			"return size differs: %d != %zu\n", size, sizeof(buf));	if (retval < 0)		printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "			"usb_interrupt_msg returned %d\n", retval);	if (retval >= 0)		for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)			radio->registers[STATUSRSSI + regnr] =				get_unaligned_be16(				&buf[regnr * RADIO_REGISTER_SIZE + 1]);	return (retval < 0) ? -EINVAL : 0;}/* * si470x_set_chan - set the channel */static int si470x_set_chan(struct si470x_device *radio, unsigned short chan){	int retval;	unsigned long timeout;	bool timed_out = 0;	/* start tuning */	radio->registers[CHANNEL] &= ~CHANNEL_CHAN;	radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;	retval = si470x_set_register(radio, CHANNEL);	if (retval < 0)		goto done;	/* wait till tune operation has completed */	timeout = jiffies + msecs_to_jiffies(tune_timeout);	do {		retval = si470x_get_register(radio, STATUSRSSI);		if (retval < 0)			goto stop;		timed_out = time_after(jiffies, timeout);	} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&		(!timed_out));	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)		printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n");	if (timed_out)		printk(KERN_WARNING DRIVER_NAME			": tune timed out after %u ms\n", tune_timeout);stop:	/* stop tuning */	radio->registers[CHANNEL] &= ~CHANNEL_TUNE;	retval = si470x_set_register(radio, CHANNEL);done:	return retval;}/* * si470x_get_freq - get the frequency */static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq){	unsigned int spacing, band_bottom;	unsigned short chan;	int retval;	/* Spacing (kHz) */	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {	/* 0: 200 kHz (USA, Australia) */	case 0:		spacing = 0.200 * FREQ_MUL; break;	/* 1: 100 kHz (Europe, Japan) */	case 1:		spacing = 0.100 * FREQ_MUL; break;	/* 2:  50 kHz */	default:		spacing = 0.050 * FREQ_MUL; break;	};	/* Bottom of Band (MHz) */	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {	/* 0: 87.5 - 108 MHz (USA, Europe) */	case 0:		band_bottom = 87.5 * FREQ_MUL; break;	/* 1: 76   - 108 MHz (Japan wide band) */	default:		band_bottom = 76   * FREQ_MUL; break;	/* 2: 76   -  90 MHz (Japan) */	case 2:		band_bottom = 76   * FREQ_MUL; break;	};	/* read channel */	retval = si470x_get_register(radio, READCHAN);	chan = radio->registers[READCHAN] & READCHAN_READCHAN;	/* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */	*freq = chan * spacing + band_bottom;	return retval;}/* * si470x_set_freq - set the frequency */static int si470x_set_freq(struct si470x_device *radio, unsigned int freq){	unsigned int spacing, band_bottom;	unsigned short chan;	/* Spacing (kHz) */	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {	/* 0: 200 kHz (USA, Australia) */	case 0:		spacing = 0.200 * FREQ_MUL; break;	/* 1: 100 kHz (Europe, Japan) */	case 1:		spacing = 0.100 * FREQ_MUL; break;	/* 2:  50 kHz */	default:		spacing = 0.050 * FREQ_MUL; break;	};	/* Bottom of Band (MHz) */	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {	/* 0: 87.5 - 108 MHz (USA, Europe) */	case 0:		band_bottom = 87.5 * FREQ_MUL; break;	/* 1: 76   - 108 MHz (Japan wide band) */	default:		band_bottom = 76   * FREQ_MUL; break;	/* 2: 76   -  90 MHz (Japan) */	case 2:		band_bottom = 76   * FREQ_MUL; break;	};	/* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */	chan = (freq - band_bottom) / spacing;	return si470x_set_chan(radio, chan);}/* * si470x_set_seek - set seek */static int si470x_set_seek(struct si470x_device *radio,		unsigned int wrap_around, unsigned int seek_upward){	int retval = 0;	unsigned long timeout;	bool timed_out = 0;	/* start seeking */	radio->registers[POWERCFG] |= POWERCFG_SEEK;	if (wrap_around == 1)		radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;	else		radio->registers[POWERCFG] |= POWERCFG_SKMODE;	if (seek_upward == 1)		radio->registers[POWERCFG] |= POWERCFG_SEEKUP;	else		radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;	retval = si470x_set_register(radio, POWERCFG);	if (retval < 0)		goto done;	/* wait till seek operation has completed */	timeout = jiffies + msecs_to_jiffies(seek_timeout);	do {		retval = si470x_get_register(radio, STATUSRSSI);		if (retval < 0)			goto stop;		timed_out = time_after(jiffies, timeout);	} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&		(!timed_out));	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)		printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n");	if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)		printk(KERN_WARNING DRIVER_NAME			": seek failed / band limit reached\n");	if (timed_out)		printk(KERN_WARNING DRIVER_NAME			": seek timed out after %u ms\n", seek_timeout);stop:	/* stop seeking */	radio->registers[POWERCFG] &= ~POWERCFG_SEEK;	retval = si470x_set_register(radio, POWERCFG);done:	/* try again, if timed out */	if ((retval == 0) && timed_out)		retval = -EAGAIN;	return retval;}/* * si470x_start - switch on radio */static int si470x_start(struct si470x_device *radio){	int retval;	/* powercfg */	radio->registers[POWERCFG] =		POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;	retval = si470x_set_register(radio, POWERCFG);	if (retval < 0)		goto done;	/* sysconfig 1 */	radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;	retval = si470x_set_register(radio, SYSCONFIG1);	if (retval < 0)		goto done;	/* sysconfig 2 */	radio->registers[SYSCONFIG2] =		(0x3f  << 8) |				/* SEEKTH */		((band  << 6) & SYSCONFIG2_BAND)  |	/* BAND */		((space << 4) & SYSCONFIG2_SPACE) |	/* SPACE */		15;					/* VOLUME (max) */	retval = si470x_set_register(radio, SYSCONFIG2);	if (retval < 0)		goto done;	/* reset last channel */	retval = si470x_set_chan(radio,		radio->registers[CHANNEL] & CHANNEL_CHAN);done:	return retval;}/* * si470x_stop - switch off radio */static int si470x_stop(struct si470x_device *radio){	int retval;	/* sysconfig 1 */	radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;	retval = si470x_set_register(radio, SYSCONFIG1);	if (retval < 0)		goto done;	/* powercfg */	radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;	/* POWERCFG_ENABLE has to automatically go low */	radio->registers[POWERCFG] |= POWERCFG_ENABLE |	POWERCFG_DISABLE;	retval = si470x_set_register(radio, POWERCFG);done:	return retval;}/* * si470x_rds_on - switch on rds reception */static int si470x_rds_on(struct si470x_device *radio){	int retval;	/* sysconfig 1 */	mutex_lock(&radio->lock);	radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;	retval = si470x_set_register(radio, SYSCONFIG1);	if (retval < 0)		radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;	mutex_unlock(&radio->lock);	return retval;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?