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