📄 af9005.c
字号:
/* DVB USB compliant Linux driver for the Afatech 9005 * USB1.1 DVB-T receiver. * * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) * * Thanks to Afatech who kindly provided information. * * 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. * * see Documentation/dvb/REDME.dvb-usb for more information */#include "af9005.h"/* debug */int dvb_usb_af9005_debug;module_param_named(debug, dvb_usb_af9005_debug, int, 0644);MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4,reg=8,i2c=16,fw=32 (or-able))." DVB_USB_DEBUG_STATUS);/* enable obnoxious led */int dvb_usb_af9005_led = 1;module_param_named(led, dvb_usb_af9005_led, bool, 0644);MODULE_PARM_DESC(led, "enable led (default: 1).");/* eeprom dump */static int dvb_usb_af9005_dump_eeprom;module_param_named(dump_eeprom, dvb_usb_af9005_dump_eeprom, int, 0);MODULE_PARM_DESC(dump_eeprom, "dump contents of the eeprom.");DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);/* remote control decoder */static int (*rc_decode) (struct dvb_usb_device *d, u8 *data, int len, u32 *event, int *state);static void *rc_keys;static int *rc_keys_size;u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };struct af9005_device_state { u8 sequence; int led_state;};static int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen, int delay_ms){ int actlen, ret = -ENOMEM; if (wbuf == NULL || wlen == 0) return -EINVAL; if ((ret = mutex_lock_interruptible(&d->usb_mutex))) return ret; deb_xfer(">>> "); debug_dump(wbuf, wlen, deb_xfer); ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, 2), wbuf, wlen, &actlen, 2000); if (ret) err("bulk message failed: %d (%d/%d)", ret, wlen, actlen); else ret = actlen != wlen ? -1 : 0; /* an answer is expected, and no error before */ if (!ret && rbuf && rlen) { if (delay_ms) msleep(delay_ms); ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, 0x01), rbuf, rlen, &actlen, 2000); if (ret) err("recv bulk message failed: %d", ret); else { deb_xfer("<<< "); debug_dump(rbuf, actlen, deb_xfer); } } mutex_unlock(&d->usb_mutex); return ret;}static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, int readwrite, int type, u8 * values, int len){ struct af9005_device_state *st = d->priv; u8 obuf[16] = { 0 }; u8 ibuf[17] = { 0 }; u8 command; int i; int ret; if (len < 1) { err("generic read/write, less than 1 byte. Makes no sense."); return -EINVAL; } if (len > 8) { err("generic read/write, more than 8 bytes. Not supported."); return -EINVAL; } obuf[0] = 14; /* rest of buffer length low */ obuf[1] = 0; /* rest of buffer length high */ obuf[2] = AF9005_REGISTER_RW; /* register operation */ obuf[3] = 12; /* rest of buffer length */ obuf[4] = st->sequence++; /* sequence number */ obuf[5] = (u8) (reg >> 8); /* register address */ obuf[6] = (u8) (reg & 0xff); if (type == AF9005_OFDM_REG) { command = AF9005_CMD_OFDM_REG; } else { command = AF9005_CMD_TUNER; } if (len > 1) command |= AF9005_CMD_BURST | AF9005_CMD_AUTOINC | (len - 1) << 3; command |= readwrite; if (readwrite == AF9005_CMD_WRITE) for (i = 0; i < len; i++) obuf[8 + i] = values[i]; else if (type == AF9005_TUNER_REG) /* read command for tuner, the first byte contains the i2c address */ obuf[8] = values[0]; obuf[7] = command; ret = af9005_usb_generic_rw(d, obuf, 16, ibuf, 17, 0); if (ret) return ret; /* sanity check */ if (ibuf[2] != AF9005_REGISTER_RW_ACK) { err("generic read/write, wrong reply code."); return -EIO; } if (ibuf[3] != 0x0d) { err("generic read/write, wrong length in reply."); return -EIO; } if (ibuf[4] != obuf[4]) { err("generic read/write, wrong sequence in reply."); return -EIO; } /* Windows driver doesn't check these fields, in fact sometimes the register in the reply is different that what has been sent if (ibuf[5] != obuf[5] || ibuf[6] != obuf[6]) { err("generic read/write, wrong register in reply."); return -EIO; } if (ibuf[7] != command) { err("generic read/write wrong command in reply."); return -EIO; } */ if (ibuf[16] != 0x01) { err("generic read/write wrong status code in reply."); return -EIO; } if (readwrite == AF9005_CMD_READ) for (i = 0; i < len; i++) values[i] = ibuf[8 + i]; return 0;}int af9005_read_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 * value){ int ret; deb_reg("read register %x ", reg); ret = af9005_generic_read_write(d, reg, AF9005_CMD_READ, AF9005_OFDM_REG, value, 1); if (ret) deb_reg("failed\n"); else deb_reg("value %x\n", *value); return ret;}int af9005_read_ofdm_registers(struct dvb_usb_device *d, u16 reg, u8 * values, int len){ int ret; deb_reg("read %d registers %x ", len, reg); ret = af9005_generic_read_write(d, reg, AF9005_CMD_READ, AF9005_OFDM_REG, values, len); if (ret) deb_reg("failed\n"); else debug_dump(values, len, deb_reg); return ret;}int af9005_write_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 value){ int ret; u8 temp = value; deb_reg("write register %x value %x ", reg, value); ret = af9005_generic_read_write(d, reg, AF9005_CMD_WRITE, AF9005_OFDM_REG, &temp, 1); if (ret) deb_reg("failed\n"); else deb_reg("ok\n"); return ret;}int af9005_write_ofdm_registers(struct dvb_usb_device *d, u16 reg, u8 * values, int len){ int ret; deb_reg("write %d registers %x values ", len, reg); debug_dump(values, len, deb_reg); ret = af9005_generic_read_write(d, reg, AF9005_CMD_WRITE, AF9005_OFDM_REG, values, len); if (ret) deb_reg("failed\n"); else deb_reg("ok\n"); return ret;}int af9005_read_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, u8 len, u8 * value){ u8 temp; int ret; deb_reg("read bits %x %x %x", reg, pos, len); ret = af9005_read_ofdm_register(d, reg, &temp); if (ret) { deb_reg(" failed\n"); return ret; } *value = (temp >> pos) & regmask[len - 1]; deb_reg(" value %x\n", *value); return 0;}int af9005_write_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, u8 len, u8 value){ u8 temp, mask; int ret; deb_reg("write bits %x %x %x value %x\n", reg, pos, len, value); if (pos == 0 && len == 8) return af9005_write_ofdm_register(d, reg, value); ret = af9005_read_ofdm_register(d, reg, &temp); if (ret) return ret; mask = regmask[len - 1] << pos; temp = (temp & ~mask) | ((value << pos) & mask); return af9005_write_ofdm_register(d, reg, temp);}static int af9005_usb_read_tuner_registers(struct dvb_usb_device *d, u16 reg, u8 * values, int len){ return af9005_generic_read_write(d, reg, AF9005_CMD_READ, AF9005_TUNER_REG, values, len);}static int af9005_usb_write_tuner_registers(struct dvb_usb_device *d, u16 reg, u8 * values, int len){ return af9005_generic_read_write(d, reg, AF9005_CMD_WRITE, AF9005_TUNER_REG, values, len);}int af9005_write_tuner_registers(struct dvb_usb_device *d, u16 reg, u8 * values, int len){ /* don't let the name of this function mislead you: it's just used as an interface from the firmware to the i2c bus. The actual i2c addresses are contained in the data */ int ret, i, done = 0, fail = 0; u8 temp; ret = af9005_usb_write_tuner_registers(d, reg, values, len); if (ret) return ret; if (reg != 0xffff) { /* check if write done (0xa40d bit 1) or fail (0xa40d bit 2) */ for (i = 0; i < 200; i++) { ret = af9005_read_ofdm_register(d, xd_I2C_i2c_m_status_wdat_done, &temp); if (ret) return ret; done = temp & (regmask[i2c_m_status_wdat_done_len - 1] << i2c_m_status_wdat_done_pos); if (done) break; fail = temp & (regmask[i2c_m_status_wdat_fail_len - 1] << i2c_m_status_wdat_fail_pos); if (fail) break; msleep(50); } if (i == 200) return -ETIMEDOUT; if (fail) { /* clear write fail bit */ af9005_write_register_bits(d, xd_I2C_i2c_m_status_wdat_fail, i2c_m_status_wdat_fail_pos, i2c_m_status_wdat_fail_len, 1); return -EIO; } /* clear write done bit */ ret = af9005_write_register_bits(d, xd_I2C_i2c_m_status_wdat_fail, i2c_m_status_wdat_done_pos, i2c_m_status_wdat_done_len, 1); if (ret) return ret; } return 0;}int af9005_read_tuner_registers(struct dvb_usb_device *d, u16 reg, u8 addr, u8 * values, int len){ /* don't let the name of this function mislead you: it's just used as an interface from the firmware to the i2c bus. The actual i2c addresses are contained in the data */ int ret, i; u8 temp, buf[2]; buf[0] = addr; /* tuner i2c address */ buf[1] = values[0]; /* tuner register */ values[0] = addr + 0x01; /* i2c read address */ if (reg == APO_REG_I2C_RW_SILICON_TUNER) { /* write tuner i2c address to tuner, 0c00c0 undocumented, found by sniffing */ ret = af9005_write_tuner_registers(d, 0x00c0, buf, 2); if (ret) return ret; } /* send read command to ofsm */ ret = af9005_usb_read_tuner_registers(d, reg, values, 1); if (ret) return ret; /* check if read done */ for (i = 0; i < 200; i++) { ret = af9005_read_ofdm_register(d, 0xa408, &temp); if (ret) return ret; if (temp & 0x01) break; msleep(50); } if (i == 200) return -ETIMEDOUT; /* clear read done bit (by writing 1) */ ret = af9005_write_ofdm_register(d, xd_I2C_i2c_m_data8, 1); if (ret) return ret; /* get read data (available from 0xa400) */ for (i = 0; i < len; i++) { ret = af9005_read_ofdm_register(d, 0xa400 + i, &temp); if (ret) return ret; values[i] = temp; } return 0;}static int af9005_i2c_write(struct dvb_usb_device *d, u8 i2caddr, u8 reg, u8 * data, int len){ int ret, i; u8 buf[3]; deb_i2c("i2c_write i2caddr %x, reg %x, len %d data ", i2caddr, reg, len); debug_dump(data, len, deb_i2c); for (i = 0; i < len; i++) { buf[0] = i2caddr; buf[1] = reg + (u8) i; buf[2] = data[i]; ret = af9005_write_tuner_registers(d, APO_REG_I2C_RW_SILICON_TUNER, buf, 3); if (ret) { deb_i2c("i2c_write failed\n"); return ret; } } deb_i2c("i2c_write ok\n"); return 0;}static int af9005_i2c_read(struct dvb_usb_device *d, u8 i2caddr, u8 reg, u8 * data, int len){ int ret, i; u8 temp; deb_i2c("i2c_read i2caddr %x, reg %x, len %d\n ", i2caddr, reg, len); for (i = 0; i < len; i++) { temp = reg + i; ret = af9005_read_tuner_registers(d, APO_REG_I2C_RW_SILICON_TUNER, i2caddr, &temp, 1); if (ret) { deb_i2c("i2c_read failed\n"); return ret; } data[i] = temp; } deb_i2c("i2c data read: "); debug_dump(data, len, deb_i2c); return 0;}static int af9005_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num){ /* only implements what the mt2060 module does, don't know how to make it really generic */ struct dvb_usb_device *d = i2c_get_adapdata(adap); int ret; u8 reg, addr; u8 *value; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; if (num > 2) warn("more than 2 i2c messages at a time is not handled yet. TODO."); if (num == 2) { /* reads a single register */ reg = *msg[0].buf; addr = msg[0].addr; value = msg[1].buf; ret = af9005_i2c_read(d, addr, reg, value, 1); if (ret == 0) ret = 2; } else { /* write one or more registers */ reg = msg[0].buf[0]; addr = msg[0].addr; value = &msg[0].buf[1]; ret = af9005_i2c_write(d, addr, reg, value, msg[0].len - 1); if (ret == 0) ret = 1; } mutex_unlock(&d->i2c_mutex); return ret;}static u32 af9005_i2c_func(struct i2c_adapter *adapter){ return I2C_FUNC_I2C;}static struct i2c_algorithm af9005_i2c_algo = { .master_xfer = af9005_i2c_xfer, .functionality = af9005_i2c_func,};int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, int wlen, u8 * rbuf, int rlen){ struct af9005_device_state *st = d->priv; int ret, i, packet_len; u8 buf[64]; u8 ibuf[64]; if (wlen < 0) { err("send command, wlen less than 0 bytes. Makes no sense."); return -EINVAL; } if (wlen > 54) { err("send command, wlen more than 54 bytes. Not supported."); return -EINVAL; } if (rlen > 54) { err("send command, rlen more than 54 bytes. Not supported."); return -EINVAL; } packet_len = wlen + 5; buf[0] = (u8) (packet_len & 0xff); buf[1] = (u8) ((packet_len & 0xff00) >> 8); buf[2] = 0x26; /* packet type */ buf[3] = wlen + 3; buf[4] = st->sequence++; buf[5] = command; buf[6] = wlen; for (i = 0; i < wlen; i++) buf[7 + i] = wbuf[i]; ret = af9005_usb_generic_rw(d, buf, wlen + 7, ibuf, rlen + 7, 0); if (ret) return ret; if (ibuf[2] != 0x27) { err("send command, wrong reply code."); return -EIO; } if (ibuf[4] != buf[4]) { err("send command, wrong sequence in reply."); return -EIO; } if (ibuf[5] != 0x01) { err("send command, wrong status code in reply."); return -EIO; } if (ibuf[6] != rlen) { err("send command, invalid data length in reply."); return -EIO; } for (i = 0; i < rlen; i++) rbuf[i] = ibuf[i + 7]; return 0;}int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, int len){ struct af9005_device_state *st = d->priv; u8 obuf[16], ibuf[14]; int ret, i; memset(obuf, 0, sizeof(obuf)); memset(ibuf, 0, sizeof(ibuf)); obuf[0] = 14; /* length of rest of packet low */ obuf[1] = 0; /* length of rest of packer high */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -