📄 alps_tdlb7.c
字号:
/* Driver for Alps TDLB7 Frontend Copyright (C) 1999 Juergen Peitz <peitz@snafu.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*//* Wrote this code mainly to get my own card running. It's working for me, but I hope somebody who knows more about linux programming and the DVB driver can improve it. Reused a lot from the existing driver and tuner code. Thanks to everybody who worked on it! This driver needs a copy of the microcode file 'Sc_main.mc' from the Haupauge windows driver in the 'usr/lib/DVB/driver/frontends' directory. You can also pass the complete file name with the module parameter 'mcfile'. The code only needs to be loaded once after a power on. Because loading the microcode to the card takes some time, you can use the 'loadcode=0' module parameter, if you only want to reload the dvb driver. Juergen Peitz */ #define __KERNEL_SYSCALLS__#include <linux/module.h>#include <linux/init.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/unistd.h>#include "compat.h"#include "dvb_frontend.h"static int debug = 0;static int loadcode = 1;static char * mcfile = "/usr/lib/DVB/driver/frontends/Sc_main.mc";#define dprintk if (debug) printk/* microcode size for sp8870 */#define SP8870_CODE_SIZE 16382/* starting point for microcode in file 'Sc_main.mc' */#define SP8870_CODE_OFFSET 0x0Astatic int errno;staticstruct dvb_frontend_info tdlb7_info = { name: "Alps TDLB7", type: FE_OFDM, frequency_min: 470000000, frequency_max: 860000000, frequency_stepsize: 166666,#if 0 frequency_tolerance: ???, symbol_rate_min: ???, symbol_rate_max: ???, symbol_rate_tolerance: ???, notifier_delay: 0,#endif caps: FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_RECOVER};staticint sp8870_writereg (struct dvb_i2c_bus *i2c, u16 reg, u16 data){ u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; struct i2c_msg msg = { addr: 0x71, flags: 0, buf: buf, len: 4 }; int err; if ((err = i2c->xfer (i2c, &msg, 1)) != 1) { dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data); return -EREMOTEIO; } return 0;}staticu16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg){ int ret; u8 b0 [] = { reg >> 8 , reg & 0xff }; u8 b1 [] = { 0, 0 }; struct i2c_msg msg [] = { { addr: 0x71, flags: 0, buf: b0, len: 2 }, { addr: 0x71, flags: I2C_M_RD, buf: b1, len: 2 } }; ret = i2c->xfer (i2c, msg, 2); if (ret != 2) dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); return (b1[0] << 8 | b1[1]);}staticint sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4]){ int ret; struct i2c_msg msg = { addr: 0x60, flags: 0, buf: data, len: 4 }; ret = i2c->xfer (i2c, &msg, 1); if (ret != 1) printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret); return (ret != 1) ? -1 : 0;}staticint sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq){ u32 div = (freq + 36200000) / 166666; u8 buf [4]; int pwr; if (freq <= 782000000) pwr = 1; else pwr = 2; buf[0] = (div >> 8) & 0x7f; buf[1] = div & 0xff; buf[2] = 0x85; buf[3] = pwr << 6; return sp5659_write (i2c, buf);}staticint sp8870_read_code(const char *fn, char **fp){ int fd; loff_t l; char *dp; fd = open(fn, 0, 0); if (fd == -1) { printk(KERN_INFO "%s: Unable to load '%s'.\n", __FUNCTION__, fn); return -1; } l = lseek(fd, 0L, 2); if (l <= 0 || l < SP8870_CODE_OFFSET+SP8870_CODE_SIZE) { printk(KERN_INFO "%s: code file too small '%s'\n", __FUNCTION__, fn); sys_close(fd); return -1; } lseek(fd, SP8870_CODE_OFFSET, 0); *fp= dp = vmalloc(SP8870_CODE_SIZE); if (dp == NULL) { printk(KERN_INFO "%s: Out of memory loading '%s'.\n", __FUNCTION__, fn); sys_close(fd); return -1; } if (read(fd, dp, SP8870_CODE_SIZE) != SP8870_CODE_SIZE) { printk(KERN_INFO "%s: Failed to read '%s'.\n",__FUNCTION__, fn); vfree(dp); sys_close(fd); return -1; } sys_close(fd); *fp = dp; return 0;}static int sp8870_load_code(struct dvb_i2c_bus *i2c){ /* this takes a long time. is there a way to do it faster? */ char *lcode; struct i2c_msg msg; unsigned char buf[255]; int err; int p=0; int c; mm_segment_t fs = get_fs(); // system controller stop sp8870_writereg(i2c,0x0F00,0x0000); // instruction RAM register hiword sp8870_writereg(i2c,0x8F08,((SP8870_CODE_SIZE/2) & 0xFFFF)); // instruction RAM MWR sp8870_writereg(i2c,0x8F0A,((SP8870_CODE_SIZE/2) >> 16)); set_fs(get_ds()); if (sp8870_read_code(mcfile,(char**) &lcode)<0) return -1; set_fs(fs); while (p<SP8870_CODE_SIZE){ c = (p<=SP8870_CODE_SIZE-252) ? 252 : SP8870_CODE_SIZE-p; buf[0]=0xCF; buf[1]=0x0A; memcpy(&buf[2],lcode+p,c); c+=2; msg.addr=0x71; msg.flags=0; msg.buf=buf; msg.len=c; if ((err = i2c->xfer (i2c, &msg, 1)) != 1) { dprintk ("%s: i2c error (err == %i)\n", __FUNCTION__, err); vfree(lcode); return -EREMOTEIO; } p+=252; } vfree(lcode); return 0;};staticint sp8870_init (struct dvb_i2c_bus *i2c){ dprintk ("%s\n", __FUNCTION__); // system controller stop sp8870_writereg(i2c,0x0F00,0x0000); // ADC mode: 2 for MT8872, 3 for MT8870/8871 sp8870_writereg(i2c,0x0301,0x0003); // Reed Solomon parity bytes passed to output sp8870_writereg(i2c,0x0C13,0x0001); // MPEG clock is suppressed if no valid data sp8870_writereg(i2c,0x0C14,0x0001); // sample rate correction bit [23..17] sp8870_writereg(i2c,0x0319,0x000A); // sample rate correction bit [16..0] sp8870_writereg(i2c,0x031A,0x0AAB); // integer carrier offset sp8870_writereg(i2c,0x0309,0x0400); // fractional carrier offset sp8870_writereg(i2c,0x030A,0x0000); // filter for 8 Mhz channel sp8870_writereg(i2c,0x0311,0x0000); // scan order: 2k first = 0x0000, 8k first = 0x0001 sp8870_writereg(i2c,0x0338,0x0000); return 0;}staticint tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg){ struct dvb_i2c_bus *i2c = fe->i2c; switch (cmd) { case FE_GET_INFO: memcpy (arg, &tdlb7_info, sizeof(struct dvb_frontend_info)); break; case FE_READ_STATUS: { fe_status_t *status = arg; int sync = sp8870_readreg (i2c, 0x0200); int signal = 0xff-sp8870_readreg (i2c, 0x303); *status=0; if (signal>10) // FIXME: is 10 the right value ? *status |= FE_HAS_SIGNAL; if (sync&0x04) // FIXME: find criteria *status |= FE_HAS_CARRIER; if (sync&0x04) // FIXME *status |= FE_HAS_VITERBI; if (sync&0x08) // FIXME *status |= FE_HAS_SYNC; if (sync&0x04) *status |= FE_HAS_LOCK; break; } case FE_READ_BER: { u32 *ber=(u32 *) arg; // bit error rate before Viterbi *ber=sp8870_readreg(i2c,0x0C07); break; } case FE_READ_SIGNAL_STRENGTH: // FIXME: correct registers ? { *((u16*) arg) = 0xffff-((sp8870_readreg (i2c, 0x306) << 8) | sp8870_readreg (i2c, 0x303)); break; } case FE_READ_SNR: // not supported by hardware? { s32 *snr=(s32 *) arg; *snr=0; return -EOPNOTSUPP; } case FE_READ_UNCORRECTED_BLOCKS: // not supported by hardware? { u32 *ublocks=(u32 *) arg; *ublocks=0; return -EOPNOTSUPP; } case FE_SET_FRONTEND: { struct dvb_frontend_parameters *p = arg; // system controller stop sp8870_writereg(i2c,0x0F00,0x0000); sp5659_set_tv_freq (i2c, p->frequency); // read status reg in order to clear pending irqs sp8870_readreg(i2c, 0x200); // sample rate correction bit [23..17] sp8870_writereg(i2c,0x0319,0x000A); // sample rate correction bit [16..0] sp8870_writereg(i2c,0x031A,0x0AAB); // integer carrier offset sp8870_writereg(i2c,0x0309,0x0400); // fractional carrier offset sp8870_writereg(i2c,0x030A,0x0000); // filter for 6/7/8 Mhz channel if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) sp8870_writereg(i2c,0x0311,0x0002); else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) sp8870_writereg(i2c,0x0311,0x0001); else sp8870_writereg(i2c,0x0311,0x0000); // scan order: 2k first = 0x0000, 8k first = 0x0001 if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K) sp8870_writereg(i2c,0x0338,0x0000); else sp8870_writereg(i2c,0x0338,0x0001); // instruction RAM register loword sp8870_writereg(i2c,0x0F09,0x0000); // instruction RAM register hiword sp8870_writereg(i2c,0x0F08,0x0000); // system controller start sp8870_writereg(i2c,0x0F00,0x0001); break; } case FE_RESET: // system controller stop sp8870_writereg(i2c,0x0F00,0x0000); // read status reg in order to clear pending irqs sp8870_readreg(i2c, 0x200); // system controller start sp8870_writereg(i2c,0x0F00,0x0001); break; case FE_GET_FRONTEND: // FIXME: read known values back from Hardware... { break; } case FE_SLEEP: // is this supported by hardware? return -EOPNOTSUPP; case FE_INIT: return sp8870_init (i2c); default: return -EOPNOTSUPP; }; return 0;}staticint tdlb7_attach (struct dvb_i2c_bus *i2c){ struct i2c_msg msg = { addr: 0x71, flags: 0, buf: NULL, len: 0 }; dprintk ("%s\n", __FUNCTION__); if (i2c->xfer (i2c, &msg, 1) != 1) return -ENODEV; if (loadcode) { dprintk("%s: loading mcfile '%s' !\n", __FUNCTION__, mcfile); if (sp8870_load_code(i2c)==0) dprintk("%s: microcode loaded!\n", __FUNCTION__); }else{ dprintk("%s: without loading mcfile!\n", __FUNCTION__); } dvb_register_frontend (tdlb7_ioctl, i2c, NULL, &tdlb7_info); return 0;}staticvoid tdlb7_detach (struct dvb_i2c_bus *i2c){ dprintk ("%s\n", __FUNCTION__); dvb_unregister_frontend (tdlb7_ioctl, i2c);}staticint __init init_tdlb7 (void){ dprintk ("%s\n", __FUNCTION__); return dvb_register_i2c_device (THIS_MODULE, tdlb7_attach, tdlb7_detach);}staticvoid __exit exit_tdlb7 (void){ dprintk ("%s\n", __FUNCTION__); dvb_unregister_i2c_device (tdlb7_attach);}module_init(init_tdlb7);module_exit(exit_tdlb7);MODULE_PARM(debug,"i");MODULE_PARM_DESC(debug, "enable verbose debug messages");MODULE_PARM(loadcode,"i");MODULE_PARM_DESC(loadcode, "load tuner microcode");MODULE_PARM(mcfile,"s");MODULE_PARM_DESC(mcfile, "where to find the microcode file");MODULE_DESCRIPTION("TDLB7 DVB-T Frontend");MODULE_AUTHOR("Juergen Peitz");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -