162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* DVB USB compliant Linux driver for the Afatech 9005 362306a36Sopenharmony_ci * USB1.1 DVB-T receiver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Thanks to Afatech who kindly provided information. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include "af9005.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* debug */ 1462306a36Sopenharmony_ciint dvb_usb_af9005_debug; 1562306a36Sopenharmony_cimodule_param_named(debug, dvb_usb_af9005_debug, int, 0644); 1662306a36Sopenharmony_ciMODULE_PARM_DESC(debug, 1762306a36Sopenharmony_ci "set debugging level (1=info,xfer=2,rc=4,reg=8,i2c=16,fw=32 (or-able))." 1862306a36Sopenharmony_ci DVB_USB_DEBUG_STATUS); 1962306a36Sopenharmony_ci/* enable obnoxious led */ 2062306a36Sopenharmony_cibool dvb_usb_af9005_led = true; 2162306a36Sopenharmony_cimodule_param_named(led, dvb_usb_af9005_led, bool, 0644); 2262306a36Sopenharmony_ciMODULE_PARM_DESC(led, "enable led (default: 1)."); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* eeprom dump */ 2562306a36Sopenharmony_cistatic int dvb_usb_af9005_dump_eeprom; 2662306a36Sopenharmony_cimodule_param_named(dump_eeprom, dvb_usb_af9005_dump_eeprom, int, 0); 2762306a36Sopenharmony_ciMODULE_PARM_DESC(dump_eeprom, "dump contents of the eeprom."); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* remote control decoder */ 3262306a36Sopenharmony_cistatic int (*rc_decode) (struct dvb_usb_device *d, u8 *data, int len, 3362306a36Sopenharmony_ci u32 *event, int *state); 3462306a36Sopenharmony_cistatic void *rc_keys; 3562306a36Sopenharmony_cistatic int *rc_keys_size; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciu8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct af9005_device_state { 4062306a36Sopenharmony_ci u8 sequence; 4162306a36Sopenharmony_ci int led_state; 4262306a36Sopenharmony_ci unsigned char data[256]; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, 4662306a36Sopenharmony_ci int readwrite, int type, u8 * values, int len) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct af9005_device_state *st = d->priv; 4962306a36Sopenharmony_ci u8 command, seq; 5062306a36Sopenharmony_ci int i, ret; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (len < 1) { 5362306a36Sopenharmony_ci err("generic read/write, less than 1 byte. Makes no sense."); 5462306a36Sopenharmony_ci return -EINVAL; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci if (len > 8) { 5762306a36Sopenharmony_ci err("generic read/write, more than 8 bytes. Not supported."); 5862306a36Sopenharmony_ci return -EINVAL; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci mutex_lock(&d->data_mutex); 6262306a36Sopenharmony_ci st->data[0] = 14; /* rest of buffer length low */ 6362306a36Sopenharmony_ci st->data[1] = 0; /* rest of buffer length high */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci st->data[2] = AF9005_REGISTER_RW; /* register operation */ 6662306a36Sopenharmony_ci st->data[3] = 12; /* rest of buffer length */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci st->data[4] = seq = st->sequence++; /* sequence number */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci st->data[5] = (u8) (reg >> 8); /* register address */ 7162306a36Sopenharmony_ci st->data[6] = (u8) (reg & 0xff); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (type == AF9005_OFDM_REG) { 7462306a36Sopenharmony_ci command = AF9005_CMD_OFDM_REG; 7562306a36Sopenharmony_ci } else { 7662306a36Sopenharmony_ci command = AF9005_CMD_TUNER; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (len > 1) 8062306a36Sopenharmony_ci command |= 8162306a36Sopenharmony_ci AF9005_CMD_BURST | AF9005_CMD_AUTOINC | (len - 1) << 3; 8262306a36Sopenharmony_ci command |= readwrite; 8362306a36Sopenharmony_ci if (readwrite == AF9005_CMD_WRITE) 8462306a36Sopenharmony_ci for (i = 0; i < len; i++) 8562306a36Sopenharmony_ci st->data[8 + i] = values[i]; 8662306a36Sopenharmony_ci else if (type == AF9005_TUNER_REG) 8762306a36Sopenharmony_ci /* read command for tuner, the first byte contains the i2c address */ 8862306a36Sopenharmony_ci st->data[8] = values[0]; 8962306a36Sopenharmony_ci st->data[7] = command; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ret = dvb_usb_generic_rw(d, st->data, 16, st->data, 17, 0); 9262306a36Sopenharmony_ci if (ret) 9362306a36Sopenharmony_ci goto ret; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* sanity check */ 9662306a36Sopenharmony_ci if (st->data[2] != AF9005_REGISTER_RW_ACK) { 9762306a36Sopenharmony_ci err("generic read/write, wrong reply code."); 9862306a36Sopenharmony_ci ret = -EIO; 9962306a36Sopenharmony_ci goto ret; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci if (st->data[3] != 0x0d) { 10262306a36Sopenharmony_ci err("generic read/write, wrong length in reply."); 10362306a36Sopenharmony_ci ret = -EIO; 10462306a36Sopenharmony_ci goto ret; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci if (st->data[4] != seq) { 10762306a36Sopenharmony_ci err("generic read/write, wrong sequence in reply."); 10862306a36Sopenharmony_ci ret = -EIO; 10962306a36Sopenharmony_ci goto ret; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * In thesis, both input and output buffers should have 11362306a36Sopenharmony_ci * identical values for st->data[5] to st->data[8]. 11462306a36Sopenharmony_ci * However, windows driver doesn't check these fields, in fact 11562306a36Sopenharmony_ci * sometimes the register in the reply is different that what 11662306a36Sopenharmony_ci * has been sent 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci if (st->data[16] != 0x01) { 11962306a36Sopenharmony_ci err("generic read/write wrong status code in reply."); 12062306a36Sopenharmony_ci ret = -EIO; 12162306a36Sopenharmony_ci goto ret; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (readwrite == AF9005_CMD_READ) 12562306a36Sopenharmony_ci for (i = 0; i < len; i++) 12662306a36Sopenharmony_ci values[i] = st->data[8 + i]; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ciret: 12962306a36Sopenharmony_ci mutex_unlock(&d->data_mutex); 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciint af9005_read_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 * value) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci int ret; 13762306a36Sopenharmony_ci deb_reg("read register %x ", reg); 13862306a36Sopenharmony_ci ret = af9005_generic_read_write(d, reg, 13962306a36Sopenharmony_ci AF9005_CMD_READ, AF9005_OFDM_REG, 14062306a36Sopenharmony_ci value, 1); 14162306a36Sopenharmony_ci if (ret) 14262306a36Sopenharmony_ci deb_reg("failed\n"); 14362306a36Sopenharmony_ci else 14462306a36Sopenharmony_ci deb_reg("value %x\n", *value); 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint af9005_read_ofdm_registers(struct dvb_usb_device *d, u16 reg, 14962306a36Sopenharmony_ci u8 * values, int len) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int ret; 15262306a36Sopenharmony_ci deb_reg("read %d registers %x ", len, reg); 15362306a36Sopenharmony_ci ret = af9005_generic_read_write(d, reg, 15462306a36Sopenharmony_ci AF9005_CMD_READ, AF9005_OFDM_REG, 15562306a36Sopenharmony_ci values, len); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci deb_reg("failed\n"); 15862306a36Sopenharmony_ci else 15962306a36Sopenharmony_ci debug_dump(values, len, deb_reg); 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciint af9005_write_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 value) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci int ret; 16662306a36Sopenharmony_ci u8 temp = value; 16762306a36Sopenharmony_ci deb_reg("write register %x value %x ", reg, value); 16862306a36Sopenharmony_ci ret = af9005_generic_read_write(d, reg, 16962306a36Sopenharmony_ci AF9005_CMD_WRITE, AF9005_OFDM_REG, 17062306a36Sopenharmony_ci &temp, 1); 17162306a36Sopenharmony_ci if (ret) 17262306a36Sopenharmony_ci deb_reg("failed\n"); 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci deb_reg("ok\n"); 17562306a36Sopenharmony_ci return ret; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ciint af9005_write_ofdm_registers(struct dvb_usb_device *d, u16 reg, 17962306a36Sopenharmony_ci u8 * values, int len) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int ret; 18262306a36Sopenharmony_ci deb_reg("write %d registers %x values ", len, reg); 18362306a36Sopenharmony_ci debug_dump(values, len, deb_reg); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = af9005_generic_read_write(d, reg, 18662306a36Sopenharmony_ci AF9005_CMD_WRITE, AF9005_OFDM_REG, 18762306a36Sopenharmony_ci values, len); 18862306a36Sopenharmony_ci if (ret) 18962306a36Sopenharmony_ci deb_reg("failed\n"); 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci deb_reg("ok\n"); 19262306a36Sopenharmony_ci return ret; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint af9005_read_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, 19662306a36Sopenharmony_ci u8 len, u8 * value) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u8 temp; 19962306a36Sopenharmony_ci int ret; 20062306a36Sopenharmony_ci deb_reg("read bits %x %x %x", reg, pos, len); 20162306a36Sopenharmony_ci ret = af9005_read_ofdm_register(d, reg, &temp); 20262306a36Sopenharmony_ci if (ret) { 20362306a36Sopenharmony_ci deb_reg(" failed\n"); 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci *value = (temp >> pos) & regmask[len - 1]; 20762306a36Sopenharmony_ci deb_reg(" value %x\n", *value); 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint af9005_write_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, 21362306a36Sopenharmony_ci u8 len, u8 value) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci u8 temp, mask; 21662306a36Sopenharmony_ci int ret; 21762306a36Sopenharmony_ci deb_reg("write bits %x %x %x value %x\n", reg, pos, len, value); 21862306a36Sopenharmony_ci if (pos == 0 && len == 8) 21962306a36Sopenharmony_ci return af9005_write_ofdm_register(d, reg, value); 22062306a36Sopenharmony_ci ret = af9005_read_ofdm_register(d, reg, &temp); 22162306a36Sopenharmony_ci if (ret) 22262306a36Sopenharmony_ci return ret; 22362306a36Sopenharmony_ci mask = regmask[len - 1] << pos; 22462306a36Sopenharmony_ci temp = (temp & ~mask) | ((value << pos) & mask); 22562306a36Sopenharmony_ci return af9005_write_ofdm_register(d, reg, temp); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int af9005_usb_read_tuner_registers(struct dvb_usb_device *d, 23062306a36Sopenharmony_ci u16 reg, u8 * values, int len) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci return af9005_generic_read_write(d, reg, 23362306a36Sopenharmony_ci AF9005_CMD_READ, AF9005_TUNER_REG, 23462306a36Sopenharmony_ci values, len); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int af9005_usb_write_tuner_registers(struct dvb_usb_device *d, 23862306a36Sopenharmony_ci u16 reg, u8 * values, int len) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci return af9005_generic_read_write(d, reg, 24162306a36Sopenharmony_ci AF9005_CMD_WRITE, 24262306a36Sopenharmony_ci AF9005_TUNER_REG, values, len); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ciint af9005_write_tuner_registers(struct dvb_usb_device *d, u16 reg, 24662306a36Sopenharmony_ci u8 * values, int len) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci /* don't let the name of this function mislead you: it's just used 24962306a36Sopenharmony_ci as an interface from the firmware to the i2c bus. The actual 25062306a36Sopenharmony_ci i2c addresses are contained in the data */ 25162306a36Sopenharmony_ci int ret, i, done = 0, fail = 0; 25262306a36Sopenharmony_ci u8 temp; 25362306a36Sopenharmony_ci ret = af9005_usb_write_tuner_registers(d, reg, values, len); 25462306a36Sopenharmony_ci if (ret) 25562306a36Sopenharmony_ci return ret; 25662306a36Sopenharmony_ci if (reg != 0xffff) { 25762306a36Sopenharmony_ci /* check if write done (0xa40d bit 1) or fail (0xa40d bit 2) */ 25862306a36Sopenharmony_ci for (i = 0; i < 200; i++) { 25962306a36Sopenharmony_ci ret = 26062306a36Sopenharmony_ci af9005_read_ofdm_register(d, 26162306a36Sopenharmony_ci xd_I2C_i2c_m_status_wdat_done, 26262306a36Sopenharmony_ci &temp); 26362306a36Sopenharmony_ci if (ret) 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci done = temp & (regmask[i2c_m_status_wdat_done_len - 1] 26662306a36Sopenharmony_ci << i2c_m_status_wdat_done_pos); 26762306a36Sopenharmony_ci if (done) 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci fail = temp & (regmask[i2c_m_status_wdat_fail_len - 1] 27062306a36Sopenharmony_ci << i2c_m_status_wdat_fail_pos); 27162306a36Sopenharmony_ci if (fail) 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci msleep(50); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci if (i == 200) 27662306a36Sopenharmony_ci return -ETIMEDOUT; 27762306a36Sopenharmony_ci if (fail) { 27862306a36Sopenharmony_ci /* clear write fail bit */ 27962306a36Sopenharmony_ci af9005_write_register_bits(d, 28062306a36Sopenharmony_ci xd_I2C_i2c_m_status_wdat_fail, 28162306a36Sopenharmony_ci i2c_m_status_wdat_fail_pos, 28262306a36Sopenharmony_ci i2c_m_status_wdat_fail_len, 28362306a36Sopenharmony_ci 1); 28462306a36Sopenharmony_ci return -EIO; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci /* clear write done bit */ 28762306a36Sopenharmony_ci ret = 28862306a36Sopenharmony_ci af9005_write_register_bits(d, 28962306a36Sopenharmony_ci xd_I2C_i2c_m_status_wdat_fail, 29062306a36Sopenharmony_ci i2c_m_status_wdat_done_pos, 29162306a36Sopenharmony_ci i2c_m_status_wdat_done_len, 1); 29262306a36Sopenharmony_ci if (ret) 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciint af9005_read_tuner_registers(struct dvb_usb_device *d, u16 reg, u8 addr, 29962306a36Sopenharmony_ci u8 * values, int len) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci /* don't let the name of this function mislead you: it's just used 30262306a36Sopenharmony_ci as an interface from the firmware to the i2c bus. The actual 30362306a36Sopenharmony_ci i2c addresses are contained in the data */ 30462306a36Sopenharmony_ci int ret, i; 30562306a36Sopenharmony_ci u8 temp, buf[2]; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci buf[0] = addr; /* tuner i2c address */ 30862306a36Sopenharmony_ci buf[1] = values[0]; /* tuner register */ 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci values[0] = addr + 0x01; /* i2c read address */ 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (reg == APO_REG_I2C_RW_SILICON_TUNER) { 31362306a36Sopenharmony_ci /* write tuner i2c address to tuner, 0c00c0 undocumented, found by sniffing */ 31462306a36Sopenharmony_ci ret = af9005_write_tuner_registers(d, 0x00c0, buf, 2); 31562306a36Sopenharmony_ci if (ret) 31662306a36Sopenharmony_ci return ret; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* send read command to ofsm */ 32062306a36Sopenharmony_ci ret = af9005_usb_read_tuner_registers(d, reg, values, 1); 32162306a36Sopenharmony_ci if (ret) 32262306a36Sopenharmony_ci return ret; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* check if read done */ 32562306a36Sopenharmony_ci for (i = 0; i < 200; i++) { 32662306a36Sopenharmony_ci ret = af9005_read_ofdm_register(d, 0xa408, &temp); 32762306a36Sopenharmony_ci if (ret) 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci if (temp & 0x01) 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci msleep(50); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci if (i == 200) 33462306a36Sopenharmony_ci return -ETIMEDOUT; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* clear read done bit (by writing 1) */ 33762306a36Sopenharmony_ci ret = af9005_write_ofdm_register(d, xd_I2C_i2c_m_data8, 1); 33862306a36Sopenharmony_ci if (ret) 33962306a36Sopenharmony_ci return ret; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* get read data (available from 0xa400) */ 34262306a36Sopenharmony_ci for (i = 0; i < len; i++) { 34362306a36Sopenharmony_ci ret = af9005_read_ofdm_register(d, 0xa400 + i, &temp); 34462306a36Sopenharmony_ci if (ret) 34562306a36Sopenharmony_ci return ret; 34662306a36Sopenharmony_ci values[i] = temp; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int af9005_i2c_write(struct dvb_usb_device *d, u8 i2caddr, u8 reg, 35262306a36Sopenharmony_ci u8 * data, int len) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci int ret, i; 35562306a36Sopenharmony_ci u8 buf[3]; 35662306a36Sopenharmony_ci deb_i2c("i2c_write i2caddr %x, reg %x, len %d data ", i2caddr, 35762306a36Sopenharmony_ci reg, len); 35862306a36Sopenharmony_ci debug_dump(data, len, deb_i2c); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 36162306a36Sopenharmony_ci buf[0] = i2caddr; 36262306a36Sopenharmony_ci buf[1] = reg + (u8) i; 36362306a36Sopenharmony_ci buf[2] = data[i]; 36462306a36Sopenharmony_ci ret = 36562306a36Sopenharmony_ci af9005_write_tuner_registers(d, 36662306a36Sopenharmony_ci APO_REG_I2C_RW_SILICON_TUNER, 36762306a36Sopenharmony_ci buf, 3); 36862306a36Sopenharmony_ci if (ret) { 36962306a36Sopenharmony_ci deb_i2c("i2c_write failed\n"); 37062306a36Sopenharmony_ci return ret; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci deb_i2c("i2c_write ok\n"); 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int af9005_i2c_read(struct dvb_usb_device *d, u8 i2caddr, u8 reg, 37862306a36Sopenharmony_ci u8 * data, int len) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci int ret, i; 38162306a36Sopenharmony_ci u8 temp; 38262306a36Sopenharmony_ci deb_i2c("i2c_read i2caddr %x, reg %x, len %d\n ", i2caddr, reg, len); 38362306a36Sopenharmony_ci for (i = 0; i < len; i++) { 38462306a36Sopenharmony_ci temp = reg + i; 38562306a36Sopenharmony_ci ret = 38662306a36Sopenharmony_ci af9005_read_tuner_registers(d, 38762306a36Sopenharmony_ci APO_REG_I2C_RW_SILICON_TUNER, 38862306a36Sopenharmony_ci i2caddr, &temp, 1); 38962306a36Sopenharmony_ci if (ret) { 39062306a36Sopenharmony_ci deb_i2c("i2c_read failed\n"); 39162306a36Sopenharmony_ci return ret; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci data[i] = temp; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci deb_i2c("i2c data read: "); 39662306a36Sopenharmony_ci debug_dump(data, len, deb_i2c); 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int af9005_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], 40162306a36Sopenharmony_ci int num) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci /* only implements what the mt2060 module does, don't know how 40462306a36Sopenharmony_ci to make it really generic */ 40562306a36Sopenharmony_ci struct dvb_usb_device *d = i2c_get_adapdata(adap); 40662306a36Sopenharmony_ci int ret; 40762306a36Sopenharmony_ci u8 reg, addr; 40862306a36Sopenharmony_ci u8 *value; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (mutex_lock_interruptible(&d->i2c_mutex) < 0) 41162306a36Sopenharmony_ci return -EAGAIN; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (num > 2) 41462306a36Sopenharmony_ci warn("more than 2 i2c messages at a time is not handled yet. TODO."); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (num == 2) { 41762306a36Sopenharmony_ci /* reads a single register */ 41862306a36Sopenharmony_ci reg = *msg[0].buf; 41962306a36Sopenharmony_ci addr = msg[0].addr; 42062306a36Sopenharmony_ci value = msg[1].buf; 42162306a36Sopenharmony_ci ret = af9005_i2c_read(d, addr, reg, value, 1); 42262306a36Sopenharmony_ci if (ret == 0) 42362306a36Sopenharmony_ci ret = 2; 42462306a36Sopenharmony_ci } else { 42562306a36Sopenharmony_ci if (msg[0].len < 2) { 42662306a36Sopenharmony_ci ret = -EOPNOTSUPP; 42762306a36Sopenharmony_ci goto unlock; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci /* write one or more registers */ 43062306a36Sopenharmony_ci reg = msg[0].buf[0]; 43162306a36Sopenharmony_ci addr = msg[0].addr; 43262306a36Sopenharmony_ci value = &msg[0].buf[1]; 43362306a36Sopenharmony_ci ret = af9005_i2c_write(d, addr, reg, value, msg[0].len - 1); 43462306a36Sopenharmony_ci if (ret == 0) 43562306a36Sopenharmony_ci ret = 1; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciunlock: 43962306a36Sopenharmony_ci mutex_unlock(&d->i2c_mutex); 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic u32 af9005_i2c_func(struct i2c_adapter *adapter) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci return I2C_FUNC_I2C; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic struct i2c_algorithm af9005_i2c_algo = { 44962306a36Sopenharmony_ci .master_xfer = af9005_i2c_xfer, 45062306a36Sopenharmony_ci .functionality = af9005_i2c_func, 45162306a36Sopenharmony_ci}; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ciint af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, 45462306a36Sopenharmony_ci int wlen, u8 * rbuf, int rlen) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct af9005_device_state *st = d->priv; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci int ret, i, packet_len; 45962306a36Sopenharmony_ci u8 seq; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (wlen < 0) { 46262306a36Sopenharmony_ci err("send command, wlen less than 0 bytes. Makes no sense."); 46362306a36Sopenharmony_ci return -EINVAL; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci if (wlen > 54) { 46662306a36Sopenharmony_ci err("send command, wlen more than 54 bytes. Not supported."); 46762306a36Sopenharmony_ci return -EINVAL; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci if (rlen > 54) { 47062306a36Sopenharmony_ci err("send command, rlen more than 54 bytes. Not supported."); 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci packet_len = wlen + 5; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci mutex_lock(&d->data_mutex); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci st->data[0] = (u8) (packet_len & 0xff); 47862306a36Sopenharmony_ci st->data[1] = (u8) ((packet_len & 0xff00) >> 8); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci st->data[2] = 0x26; /* packet type */ 48162306a36Sopenharmony_ci st->data[3] = wlen + 3; 48262306a36Sopenharmony_ci st->data[4] = seq = st->sequence++; 48362306a36Sopenharmony_ci st->data[5] = command; 48462306a36Sopenharmony_ci st->data[6] = wlen; 48562306a36Sopenharmony_ci for (i = 0; i < wlen; i++) 48662306a36Sopenharmony_ci st->data[7 + i] = wbuf[i]; 48762306a36Sopenharmony_ci ret = dvb_usb_generic_rw(d, st->data, wlen + 7, st->data, rlen + 7, 0); 48862306a36Sopenharmony_ci if (st->data[2] != 0x27) { 48962306a36Sopenharmony_ci err("send command, wrong reply code."); 49062306a36Sopenharmony_ci ret = -EIO; 49162306a36Sopenharmony_ci } else if (st->data[4] != seq) { 49262306a36Sopenharmony_ci err("send command, wrong sequence in reply."); 49362306a36Sopenharmony_ci ret = -EIO; 49462306a36Sopenharmony_ci } else if (st->data[5] != 0x01) { 49562306a36Sopenharmony_ci err("send command, wrong status code in reply."); 49662306a36Sopenharmony_ci ret = -EIO; 49762306a36Sopenharmony_ci } else if (st->data[6] != rlen) { 49862306a36Sopenharmony_ci err("send command, invalid data length in reply."); 49962306a36Sopenharmony_ci ret = -EIO; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci if (!ret) { 50262306a36Sopenharmony_ci for (i = 0; i < rlen; i++) 50362306a36Sopenharmony_ci rbuf[i] = st->data[i + 7]; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci mutex_unlock(&d->data_mutex); 50762306a36Sopenharmony_ci return ret; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ciint af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, 51162306a36Sopenharmony_ci int len) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct af9005_device_state *st = d->priv; 51462306a36Sopenharmony_ci u8 seq; 51562306a36Sopenharmony_ci int ret, i; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci mutex_lock(&d->data_mutex); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci memset(st->data, 0, sizeof(st->data)); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci st->data[0] = 14; /* length of rest of packet low */ 52262306a36Sopenharmony_ci st->data[1] = 0; /* length of rest of packer high */ 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci st->data[2] = 0x2a; /* read/write eeprom */ 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci st->data[3] = 12; /* size */ 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci st->data[4] = seq = st->sequence++; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci st->data[5] = 0; /* read */ 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci st->data[6] = len; 53362306a36Sopenharmony_ci st->data[7] = address; 53462306a36Sopenharmony_ci ret = dvb_usb_generic_rw(d, st->data, 16, st->data, 14, 0); 53562306a36Sopenharmony_ci if (st->data[2] != 0x2b) { 53662306a36Sopenharmony_ci err("Read eeprom, invalid reply code"); 53762306a36Sopenharmony_ci ret = -EIO; 53862306a36Sopenharmony_ci } else if (st->data[3] != 10) { 53962306a36Sopenharmony_ci err("Read eeprom, invalid reply length"); 54062306a36Sopenharmony_ci ret = -EIO; 54162306a36Sopenharmony_ci } else if (st->data[4] != seq) { 54262306a36Sopenharmony_ci err("Read eeprom, wrong sequence in reply "); 54362306a36Sopenharmony_ci ret = -EIO; 54462306a36Sopenharmony_ci } else if (st->data[5] != 1) { 54562306a36Sopenharmony_ci err("Read eeprom, wrong status in reply "); 54662306a36Sopenharmony_ci ret = -EIO; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!ret) { 55062306a36Sopenharmony_ci for (i = 0; i < len; i++) 55162306a36Sopenharmony_ci values[i] = st->data[6 + i]; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci mutex_unlock(&d->data_mutex); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return ret; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int af9005_boot_packet(struct usb_device *udev, int type, u8 *reply, 55962306a36Sopenharmony_ci u8 *buf, int size) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci u16 checksum; 56262306a36Sopenharmony_ci int act_len = 0, i, ret; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci memset(buf, 0, size); 56562306a36Sopenharmony_ci buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); 56662306a36Sopenharmony_ci buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff); 56762306a36Sopenharmony_ci switch (type) { 56862306a36Sopenharmony_ci case FW_CONFIG: 56962306a36Sopenharmony_ci buf[2] = 0x11; 57062306a36Sopenharmony_ci buf[3] = 0x04; 57162306a36Sopenharmony_ci buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ 57262306a36Sopenharmony_ci buf[5] = 0x03; 57362306a36Sopenharmony_ci checksum = buf[4] + buf[5]; 57462306a36Sopenharmony_ci buf[6] = (u8) ((checksum >> 8) & 0xff); 57562306a36Sopenharmony_ci buf[7] = (u8) (checksum & 0xff); 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case FW_CONFIRM: 57862306a36Sopenharmony_ci buf[2] = 0x11; 57962306a36Sopenharmony_ci buf[3] = 0x04; 58062306a36Sopenharmony_ci buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ 58162306a36Sopenharmony_ci buf[5] = 0x01; 58262306a36Sopenharmony_ci checksum = buf[4] + buf[5]; 58362306a36Sopenharmony_ci buf[6] = (u8) ((checksum >> 8) & 0xff); 58462306a36Sopenharmony_ci buf[7] = (u8) (checksum & 0xff); 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci case FW_BOOT: 58762306a36Sopenharmony_ci buf[2] = 0x10; 58862306a36Sopenharmony_ci buf[3] = 0x08; 58962306a36Sopenharmony_ci buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ 59062306a36Sopenharmony_ci buf[5] = 0x97; 59162306a36Sopenharmony_ci buf[6] = 0xaa; 59262306a36Sopenharmony_ci buf[7] = 0x55; 59362306a36Sopenharmony_ci buf[8] = 0xa5; 59462306a36Sopenharmony_ci buf[9] = 0x5a; 59562306a36Sopenharmony_ci checksum = 0; 59662306a36Sopenharmony_ci for (i = 4; i <= 9; i++) 59762306a36Sopenharmony_ci checksum += buf[i]; 59862306a36Sopenharmony_ci buf[10] = (u8) ((checksum >> 8) & 0xff); 59962306a36Sopenharmony_ci buf[11] = (u8) (checksum & 0xff); 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci default: 60262306a36Sopenharmony_ci err("boot packet invalid boot packet type"); 60362306a36Sopenharmony_ci return -EINVAL; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci deb_fw(">>> "); 60662306a36Sopenharmony_ci debug_dump(buf, FW_BULKOUT_SIZE + 2, deb_fw); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ret = usb_bulk_msg(udev, 60962306a36Sopenharmony_ci usb_sndbulkpipe(udev, 0x02), 61062306a36Sopenharmony_ci buf, FW_BULKOUT_SIZE + 2, &act_len, 2000); 61162306a36Sopenharmony_ci if (ret) 61262306a36Sopenharmony_ci err("boot packet bulk message failed: %d (%d/%d)", ret, 61362306a36Sopenharmony_ci FW_BULKOUT_SIZE + 2, act_len); 61462306a36Sopenharmony_ci else 61562306a36Sopenharmony_ci ret = act_len != FW_BULKOUT_SIZE + 2 ? -1 : 0; 61662306a36Sopenharmony_ci if (ret) 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci memset(buf, 0, 9); 61962306a36Sopenharmony_ci ret = usb_bulk_msg(udev, 62062306a36Sopenharmony_ci usb_rcvbulkpipe(udev, 0x01), buf, 9, &act_len, 2000); 62162306a36Sopenharmony_ci if (ret) { 62262306a36Sopenharmony_ci err("boot packet recv bulk message failed: %d", ret); 62362306a36Sopenharmony_ci return ret; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci deb_fw("<<< "); 62662306a36Sopenharmony_ci debug_dump(buf, act_len, deb_fw); 62762306a36Sopenharmony_ci checksum = 0; 62862306a36Sopenharmony_ci switch (type) { 62962306a36Sopenharmony_ci case FW_CONFIG: 63062306a36Sopenharmony_ci if (buf[2] != 0x11) { 63162306a36Sopenharmony_ci err("boot bad config header."); 63262306a36Sopenharmony_ci return -EIO; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci if (buf[3] != 0x05) { 63562306a36Sopenharmony_ci err("boot bad config size."); 63662306a36Sopenharmony_ci return -EIO; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci if (buf[4] != 0x00) { 63962306a36Sopenharmony_ci err("boot bad config sequence."); 64062306a36Sopenharmony_ci return -EIO; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci if (buf[5] != 0x04) { 64362306a36Sopenharmony_ci err("boot bad config subtype."); 64462306a36Sopenharmony_ci return -EIO; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci for (i = 4; i <= 6; i++) 64762306a36Sopenharmony_ci checksum += buf[i]; 64862306a36Sopenharmony_ci if (buf[7] * 256 + buf[8] != checksum) { 64962306a36Sopenharmony_ci err("boot bad config checksum."); 65062306a36Sopenharmony_ci return -EIO; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci *reply = buf[6]; 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci case FW_CONFIRM: 65562306a36Sopenharmony_ci if (buf[2] != 0x11) { 65662306a36Sopenharmony_ci err("boot bad confirm header."); 65762306a36Sopenharmony_ci return -EIO; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci if (buf[3] != 0x05) { 66062306a36Sopenharmony_ci err("boot bad confirm size."); 66162306a36Sopenharmony_ci return -EIO; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci if (buf[4] != 0x00) { 66462306a36Sopenharmony_ci err("boot bad confirm sequence."); 66562306a36Sopenharmony_ci return -EIO; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (buf[5] != 0x02) { 66862306a36Sopenharmony_ci err("boot bad confirm subtype."); 66962306a36Sopenharmony_ci return -EIO; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci for (i = 4; i <= 6; i++) 67262306a36Sopenharmony_ci checksum += buf[i]; 67362306a36Sopenharmony_ci if (buf[7] * 256 + buf[8] != checksum) { 67462306a36Sopenharmony_ci err("boot bad confirm checksum."); 67562306a36Sopenharmony_ci return -EIO; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci *reply = buf[6]; 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci case FW_BOOT: 68062306a36Sopenharmony_ci if (buf[2] != 0x10) { 68162306a36Sopenharmony_ci err("boot bad boot header."); 68262306a36Sopenharmony_ci return -EIO; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci if (buf[3] != 0x05) { 68562306a36Sopenharmony_ci err("boot bad boot size."); 68662306a36Sopenharmony_ci return -EIO; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci if (buf[4] != 0x00) { 68962306a36Sopenharmony_ci err("boot bad boot sequence."); 69062306a36Sopenharmony_ci return -EIO; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci if (buf[5] != 0x01) { 69362306a36Sopenharmony_ci err("boot bad boot pattern 01."); 69462306a36Sopenharmony_ci return -EIO; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci if (buf[6] != 0x10) { 69762306a36Sopenharmony_ci err("boot bad boot pattern 10."); 69862306a36Sopenharmony_ci return -EIO; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci for (i = 4; i <= 6; i++) 70162306a36Sopenharmony_ci checksum += buf[i]; 70262306a36Sopenharmony_ci if (buf[7] * 256 + buf[8] != checksum) { 70362306a36Sopenharmony_ci err("boot bad boot checksum."); 70462306a36Sopenharmony_ci return -EIO; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci break; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci int i, packets, ret, act_len; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci u8 *buf; 71862306a36Sopenharmony_ci u8 reply; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci buf = kmalloc(FW_BULKOUT_SIZE + 2, GFP_KERNEL); 72162306a36Sopenharmony_ci if (!buf) 72262306a36Sopenharmony_ci return -ENOMEM; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ret = af9005_boot_packet(udev, FW_CONFIG, &reply, buf, 72562306a36Sopenharmony_ci FW_BULKOUT_SIZE + 2); 72662306a36Sopenharmony_ci if (ret) 72762306a36Sopenharmony_ci goto err; 72862306a36Sopenharmony_ci if (reply != 0x01) { 72962306a36Sopenharmony_ci err("before downloading firmware, FW_CONFIG expected 0x01, received 0x%x", reply); 73062306a36Sopenharmony_ci ret = -EIO; 73162306a36Sopenharmony_ci goto err; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci packets = fw->size / FW_BULKOUT_SIZE; 73462306a36Sopenharmony_ci buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); 73562306a36Sopenharmony_ci buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff); 73662306a36Sopenharmony_ci for (i = 0; i < packets; i++) { 73762306a36Sopenharmony_ci memcpy(&buf[2], fw->data + i * FW_BULKOUT_SIZE, 73862306a36Sopenharmony_ci FW_BULKOUT_SIZE); 73962306a36Sopenharmony_ci deb_fw(">>> "); 74062306a36Sopenharmony_ci debug_dump(buf, FW_BULKOUT_SIZE + 2, deb_fw); 74162306a36Sopenharmony_ci ret = usb_bulk_msg(udev, 74262306a36Sopenharmony_ci usb_sndbulkpipe(udev, 0x02), 74362306a36Sopenharmony_ci buf, FW_BULKOUT_SIZE + 2, &act_len, 1000); 74462306a36Sopenharmony_ci if (ret) { 74562306a36Sopenharmony_ci err("firmware download failed at packet %d with code %d", i, ret); 74662306a36Sopenharmony_ci goto err; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci ret = af9005_boot_packet(udev, FW_CONFIRM, &reply, 75062306a36Sopenharmony_ci buf, FW_BULKOUT_SIZE + 2); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci goto err; 75362306a36Sopenharmony_ci if (reply != (u8) (packets & 0xff)) { 75462306a36Sopenharmony_ci err("after downloading firmware, FW_CONFIRM expected 0x%x, received 0x%x", packets & 0xff, reply); 75562306a36Sopenharmony_ci ret = -EIO; 75662306a36Sopenharmony_ci goto err; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci ret = af9005_boot_packet(udev, FW_BOOT, &reply, buf, 75962306a36Sopenharmony_ci FW_BULKOUT_SIZE + 2); 76062306a36Sopenharmony_ci if (ret) 76162306a36Sopenharmony_ci goto err; 76262306a36Sopenharmony_ci ret = af9005_boot_packet(udev, FW_CONFIG, &reply, buf, 76362306a36Sopenharmony_ci FW_BULKOUT_SIZE + 2); 76462306a36Sopenharmony_ci if (ret) 76562306a36Sopenharmony_ci goto err; 76662306a36Sopenharmony_ci if (reply != 0x02) { 76762306a36Sopenharmony_ci err("after downloading firmware, FW_CONFIG expected 0x02, received 0x%x", reply); 76862306a36Sopenharmony_ci ret = -EIO; 76962306a36Sopenharmony_ci goto err; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cierr: 77362306a36Sopenharmony_ci kfree(buf); 77462306a36Sopenharmony_ci return ret; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ciint af9005_led_control(struct dvb_usb_device *d, int onoff) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct af9005_device_state *st = d->priv; 78162306a36Sopenharmony_ci int temp, ret; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (onoff && dvb_usb_af9005_led) 78462306a36Sopenharmony_ci temp = 1; 78562306a36Sopenharmony_ci else 78662306a36Sopenharmony_ci temp = 0; 78762306a36Sopenharmony_ci if (st->led_state != temp) { 78862306a36Sopenharmony_ci ret = 78962306a36Sopenharmony_ci af9005_write_register_bits(d, xd_p_reg_top_locken1, 79062306a36Sopenharmony_ci reg_top_locken1_pos, 79162306a36Sopenharmony_ci reg_top_locken1_len, temp); 79262306a36Sopenharmony_ci if (ret) 79362306a36Sopenharmony_ci return ret; 79462306a36Sopenharmony_ci ret = 79562306a36Sopenharmony_ci af9005_write_register_bits(d, xd_p_reg_top_lock1, 79662306a36Sopenharmony_ci reg_top_lock1_pos, 79762306a36Sopenharmony_ci reg_top_lock1_len, temp); 79862306a36Sopenharmony_ci if (ret) 79962306a36Sopenharmony_ci return ret; 80062306a36Sopenharmony_ci st->led_state = temp; 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic int af9005_frontend_attach(struct dvb_usb_adapter *adap) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci u8 buf[8]; 80862306a36Sopenharmony_ci int i; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* without these calls the first commands after downloading 81162306a36Sopenharmony_ci the firmware fail. I put these calls here to simulate 81262306a36Sopenharmony_ci what it is done in dvb-usb-init.c. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci struct usb_device *udev = adap->dev->udev; 81562306a36Sopenharmony_ci usb_clear_halt(udev, usb_sndbulkpipe(udev, 2)); 81662306a36Sopenharmony_ci usb_clear_halt(udev, usb_rcvbulkpipe(udev, 1)); 81762306a36Sopenharmony_ci if (dvb_usb_af9005_dump_eeprom) { 81862306a36Sopenharmony_ci printk("EEPROM DUMP\n"); 81962306a36Sopenharmony_ci for (i = 0; i < 255; i += 8) { 82062306a36Sopenharmony_ci af9005_read_eeprom(adap->dev, i, buf, 8); 82162306a36Sopenharmony_ci debug_dump(buf, 8, printk); 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci adap->fe_adap[0].fe = af9005_fe_attach(adap->dev); 82562306a36Sopenharmony_ci return 0; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct af9005_device_state *st = d->priv; 83162306a36Sopenharmony_ci int ret, len; 83262306a36Sopenharmony_ci u8 seq; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci *state = REMOTE_NO_KEY_PRESSED; 83562306a36Sopenharmony_ci if (rc_decode == NULL) { 83662306a36Sopenharmony_ci /* it shouldn't never come here */ 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci mutex_lock(&d->data_mutex); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* deb_info("rc_query\n"); */ 84362306a36Sopenharmony_ci st->data[0] = 3; /* rest of packet length low */ 84462306a36Sopenharmony_ci st->data[1] = 0; /* rest of packet length high */ 84562306a36Sopenharmony_ci st->data[2] = 0x40; /* read remote */ 84662306a36Sopenharmony_ci st->data[3] = 1; /* rest of packet length */ 84762306a36Sopenharmony_ci st->data[4] = seq = st->sequence++; /* sequence number */ 84862306a36Sopenharmony_ci ret = dvb_usb_generic_rw(d, st->data, 5, st->data, 256, 0); 84962306a36Sopenharmony_ci if (ret) { 85062306a36Sopenharmony_ci err("rc query failed"); 85162306a36Sopenharmony_ci goto ret; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci if (st->data[2] != 0x41) { 85462306a36Sopenharmony_ci err("rc query bad header."); 85562306a36Sopenharmony_ci ret = -EIO; 85662306a36Sopenharmony_ci goto ret; 85762306a36Sopenharmony_ci } else if (st->data[4] != seq) { 85862306a36Sopenharmony_ci err("rc query bad sequence."); 85962306a36Sopenharmony_ci ret = -EIO; 86062306a36Sopenharmony_ci goto ret; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci len = st->data[5]; 86362306a36Sopenharmony_ci if (len > 246) { 86462306a36Sopenharmony_ci err("rc query invalid length"); 86562306a36Sopenharmony_ci ret = -EIO; 86662306a36Sopenharmony_ci goto ret; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci if (len > 0) { 86962306a36Sopenharmony_ci deb_rc("rc data (%d) ", len); 87062306a36Sopenharmony_ci debug_dump((st->data + 6), len, deb_rc); 87162306a36Sopenharmony_ci ret = rc_decode(d, &st->data[6], len, event, state); 87262306a36Sopenharmony_ci if (ret) { 87362306a36Sopenharmony_ci err("rc_decode failed"); 87462306a36Sopenharmony_ci goto ret; 87562306a36Sopenharmony_ci } else { 87662306a36Sopenharmony_ci deb_rc("rc_decode state %x event %x\n", *state, *event); 87762306a36Sopenharmony_ci if (*state == REMOTE_KEY_REPEAT) 87862306a36Sopenharmony_ci *event = d->last_event; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ciret: 88362306a36Sopenharmony_ci mutex_unlock(&d->data_mutex); 88462306a36Sopenharmony_ci return ret; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic int af9005_power_ctrl(struct dvb_usb_device *d, int onoff) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int af9005_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci int ret; 89662306a36Sopenharmony_ci deb_info("pid filter control onoff %d\n", onoff); 89762306a36Sopenharmony_ci if (onoff) { 89862306a36Sopenharmony_ci ret = 89962306a36Sopenharmony_ci af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 1); 90062306a36Sopenharmony_ci if (ret) 90162306a36Sopenharmony_ci return ret; 90262306a36Sopenharmony_ci ret = 90362306a36Sopenharmony_ci af9005_write_register_bits(adap->dev, 90462306a36Sopenharmony_ci XD_MP2IF_DMX_CTRL, 1, 1, 1); 90562306a36Sopenharmony_ci if (ret) 90662306a36Sopenharmony_ci return ret; 90762306a36Sopenharmony_ci ret = 90862306a36Sopenharmony_ci af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 1); 90962306a36Sopenharmony_ci } else 91062306a36Sopenharmony_ci ret = 91162306a36Sopenharmony_ci af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 0); 91262306a36Sopenharmony_ci if (ret) 91362306a36Sopenharmony_ci return ret; 91462306a36Sopenharmony_ci deb_info("pid filter control ok\n"); 91562306a36Sopenharmony_ci return 0; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int af9005_pid_filter(struct dvb_usb_adapter *adap, int index, 91962306a36Sopenharmony_ci u16 pid, int onoff) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci u8 cmd = index & 0x1f; 92262306a36Sopenharmony_ci int ret; 92362306a36Sopenharmony_ci deb_info("set pid filter, index %d, pid %x, onoff %d\n", index, 92462306a36Sopenharmony_ci pid, onoff); 92562306a36Sopenharmony_ci if (onoff) { 92662306a36Sopenharmony_ci /* cannot use it as pid_filter_ctrl since it has to be done 92762306a36Sopenharmony_ci before setting the first pid */ 92862306a36Sopenharmony_ci if (adap->feedcount == 1) { 92962306a36Sopenharmony_ci deb_info("first pid set, enable pid table\n"); 93062306a36Sopenharmony_ci ret = af9005_pid_filter_control(adap, onoff); 93162306a36Sopenharmony_ci if (ret) 93262306a36Sopenharmony_ci return ret; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci ret = 93562306a36Sopenharmony_ci af9005_write_ofdm_register(adap->dev, 93662306a36Sopenharmony_ci XD_MP2IF_PID_DATA_L, 93762306a36Sopenharmony_ci (u8) (pid & 0xff)); 93862306a36Sopenharmony_ci if (ret) 93962306a36Sopenharmony_ci return ret; 94062306a36Sopenharmony_ci ret = 94162306a36Sopenharmony_ci af9005_write_ofdm_register(adap->dev, 94262306a36Sopenharmony_ci XD_MP2IF_PID_DATA_H, 94362306a36Sopenharmony_ci (u8) (pid >> 8)); 94462306a36Sopenharmony_ci if (ret) 94562306a36Sopenharmony_ci return ret; 94662306a36Sopenharmony_ci cmd |= 0x20 | 0x40; 94762306a36Sopenharmony_ci } else { 94862306a36Sopenharmony_ci if (adap->feedcount == 0) { 94962306a36Sopenharmony_ci deb_info("last pid unset, disable pid table\n"); 95062306a36Sopenharmony_ci ret = af9005_pid_filter_control(adap, onoff); 95162306a36Sopenharmony_ci if (ret) 95262306a36Sopenharmony_ci return ret; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci ret = af9005_write_ofdm_register(adap->dev, XD_MP2IF_PID_IDX, cmd); 95662306a36Sopenharmony_ci if (ret) 95762306a36Sopenharmony_ci return ret; 95862306a36Sopenharmony_ci deb_info("set pid ok\n"); 95962306a36Sopenharmony_ci return 0; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic int af9005_identify_state(struct usb_device *udev, 96362306a36Sopenharmony_ci const struct dvb_usb_device_properties *props, 96462306a36Sopenharmony_ci const struct dvb_usb_device_description **desc, 96562306a36Sopenharmony_ci int *cold) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci int ret; 96862306a36Sopenharmony_ci u8 reply, *buf; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci buf = kmalloc(FW_BULKOUT_SIZE + 2, GFP_KERNEL); 97162306a36Sopenharmony_ci if (!buf) 97262306a36Sopenharmony_ci return -ENOMEM; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci ret = af9005_boot_packet(udev, FW_CONFIG, &reply, 97562306a36Sopenharmony_ci buf, FW_BULKOUT_SIZE + 2); 97662306a36Sopenharmony_ci if (ret) 97762306a36Sopenharmony_ci goto err; 97862306a36Sopenharmony_ci deb_info("result of FW_CONFIG in identify state %d\n", reply); 97962306a36Sopenharmony_ci if (reply == 0x01) 98062306a36Sopenharmony_ci *cold = 1; 98162306a36Sopenharmony_ci else if (reply == 0x02) 98262306a36Sopenharmony_ci *cold = 0; 98362306a36Sopenharmony_ci else 98462306a36Sopenharmony_ci ret = -EIO; 98562306a36Sopenharmony_ci if (!ret) 98662306a36Sopenharmony_ci deb_info("Identify state cold = %d\n", *cold); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cierr: 98962306a36Sopenharmony_ci kfree(buf); 99062306a36Sopenharmony_ci return ret; 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic struct dvb_usb_device_properties af9005_properties; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cistatic int af9005_usb_probe(struct usb_interface *intf, 99662306a36Sopenharmony_ci const struct usb_device_id *id) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci return dvb_usb_device_init(intf, &af9005_properties, 99962306a36Sopenharmony_ci THIS_MODULE, NULL, adapter_nr); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cienum { 100362306a36Sopenharmony_ci AFATECH_AF9005, 100462306a36Sopenharmony_ci TERRATEC_CINERGY_T_USB_XE, 100562306a36Sopenharmony_ci ANSONIC_DVBT_USB, 100662306a36Sopenharmony_ci}; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic struct usb_device_id af9005_usb_table[] = { 100962306a36Sopenharmony_ci DVB_USB_DEV(AFATECH, AFATECH_AF9005), 101062306a36Sopenharmony_ci DVB_USB_DEV(TERRATEC, TERRATEC_CINERGY_T_USB_XE), 101162306a36Sopenharmony_ci DVB_USB_DEV(ANSONIC, ANSONIC_DVBT_USB), 101262306a36Sopenharmony_ci { } 101362306a36Sopenharmony_ci}; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, af9005_usb_table); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic struct dvb_usb_device_properties af9005_properties = { 101862306a36Sopenharmony_ci .caps = DVB_USB_IS_AN_I2C_ADAPTER, 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci .usb_ctrl = DEVICE_SPECIFIC, 102162306a36Sopenharmony_ci .firmware = "af9005.fw", 102262306a36Sopenharmony_ci .download_firmware = af9005_download_firmware, 102362306a36Sopenharmony_ci .no_reconnect = 1, 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci .size_of_priv = sizeof(struct af9005_device_state), 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci .num_adapters = 1, 102862306a36Sopenharmony_ci .adapter = { 102962306a36Sopenharmony_ci { 103062306a36Sopenharmony_ci .num_frontends = 1, 103162306a36Sopenharmony_ci .fe = {{ 103262306a36Sopenharmony_ci .caps = 103362306a36Sopenharmony_ci DVB_USB_ADAP_HAS_PID_FILTER | 103462306a36Sopenharmony_ci DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, 103562306a36Sopenharmony_ci .pid_filter_count = 32, 103662306a36Sopenharmony_ci .pid_filter = af9005_pid_filter, 103762306a36Sopenharmony_ci /* .pid_filter_ctrl = af9005_pid_filter_control, */ 103862306a36Sopenharmony_ci .frontend_attach = af9005_frontend_attach, 103962306a36Sopenharmony_ci /* .tuner_attach = af9005_tuner_attach, */ 104062306a36Sopenharmony_ci /* parameter for the MPEG2-data transfer */ 104162306a36Sopenharmony_ci .stream = { 104262306a36Sopenharmony_ci .type = USB_BULK, 104362306a36Sopenharmony_ci .count = 10, 104462306a36Sopenharmony_ci .endpoint = 0x04, 104562306a36Sopenharmony_ci .u = { 104662306a36Sopenharmony_ci .bulk = { 104762306a36Sopenharmony_ci .buffersize = 4096, /* actual size seen is 3948 */ 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci }, 105162306a36Sopenharmony_ci }}, 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci }, 105462306a36Sopenharmony_ci .power_ctrl = af9005_power_ctrl, 105562306a36Sopenharmony_ci .identify_state = af9005_identify_state, 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci .i2c_algo = &af9005_i2c_algo, 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci .rc.legacy = { 106062306a36Sopenharmony_ci .rc_interval = 200, 106162306a36Sopenharmony_ci .rc_map_table = NULL, 106262306a36Sopenharmony_ci .rc_map_size = 0, 106362306a36Sopenharmony_ci .rc_query = af9005_rc_query, 106462306a36Sopenharmony_ci }, 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci .generic_bulk_ctrl_endpoint = 2, 106762306a36Sopenharmony_ci .generic_bulk_ctrl_endpoint_response = 1, 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci .num_device_descs = 3, 107062306a36Sopenharmony_ci .devices = { 107162306a36Sopenharmony_ci {.name = "Afatech DVB-T USB1.1 stick", 107262306a36Sopenharmony_ci .cold_ids = {&af9005_usb_table[AFATECH_AF9005], NULL}, 107362306a36Sopenharmony_ci .warm_ids = {NULL}, 107462306a36Sopenharmony_ci }, 107562306a36Sopenharmony_ci {.name = "TerraTec Cinergy T USB XE", 107662306a36Sopenharmony_ci .cold_ids = {&af9005_usb_table[TERRATEC_CINERGY_T_USB_XE], NULL}, 107762306a36Sopenharmony_ci .warm_ids = {NULL}, 107862306a36Sopenharmony_ci }, 107962306a36Sopenharmony_ci {.name = "Ansonic DVB-T USB1.1 stick", 108062306a36Sopenharmony_ci .cold_ids = {&af9005_usb_table[ANSONIC_DVBT_USB], NULL}, 108162306a36Sopenharmony_ci .warm_ids = {NULL}, 108262306a36Sopenharmony_ci }, 108362306a36Sopenharmony_ci {NULL}, 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci}; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */ 108862306a36Sopenharmony_cistatic struct usb_driver af9005_usb_driver = { 108962306a36Sopenharmony_ci .name = "dvb_usb_af9005", 109062306a36Sopenharmony_ci .probe = af9005_usb_probe, 109162306a36Sopenharmony_ci .disconnect = dvb_usb_device_exit, 109262306a36Sopenharmony_ci .id_table = af9005_usb_table, 109362306a36Sopenharmony_ci}; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/* module stuff */ 109662306a36Sopenharmony_cistatic int __init af9005_usb_module_init(void) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci int result; 109962306a36Sopenharmony_ci if ((result = usb_register(&af9005_usb_driver))) { 110062306a36Sopenharmony_ci err("usb_register failed. (%d)", result); 110162306a36Sopenharmony_ci return result; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci#if IS_MODULE(CONFIG_DVB_USB_AF9005) || defined(CONFIG_DVB_USB_AF9005_REMOTE) 110462306a36Sopenharmony_ci /* FIXME: convert to todays kernel IR infrastructure */ 110562306a36Sopenharmony_ci rc_decode = symbol_request(af9005_rc_decode); 110662306a36Sopenharmony_ci rc_keys = symbol_request(rc_map_af9005_table); 110762306a36Sopenharmony_ci rc_keys_size = symbol_request(rc_map_af9005_table_size); 110862306a36Sopenharmony_ci#endif 110962306a36Sopenharmony_ci if (rc_decode == NULL || rc_keys == NULL || rc_keys_size == NULL) { 111062306a36Sopenharmony_ci err("af9005_rc_decode function not found, disabling remote"); 111162306a36Sopenharmony_ci af9005_properties.rc.legacy.rc_query = NULL; 111262306a36Sopenharmony_ci } else { 111362306a36Sopenharmony_ci af9005_properties.rc.legacy.rc_map_table = rc_keys; 111462306a36Sopenharmony_ci af9005_properties.rc.legacy.rc_map_size = *rc_keys_size; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci return 0; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic void __exit af9005_usb_module_exit(void) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci /* release rc decode symbols */ 112362306a36Sopenharmony_ci if (rc_decode != NULL) 112462306a36Sopenharmony_ci symbol_put(af9005_rc_decode); 112562306a36Sopenharmony_ci if (rc_keys != NULL) 112662306a36Sopenharmony_ci symbol_put(rc_map_af9005_table); 112762306a36Sopenharmony_ci if (rc_keys_size != NULL) 112862306a36Sopenharmony_ci symbol_put(rc_map_af9005_table_size); 112962306a36Sopenharmony_ci /* deregister this driver from the USB subsystem */ 113062306a36Sopenharmony_ci usb_deregister(&af9005_usb_driver); 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cimodule_init(af9005_usb_module_init); 113462306a36Sopenharmony_cimodule_exit(af9005_usb_module_exit); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ciMODULE_AUTHOR("Luca Olivetti <luca@ventoso.org>"); 113762306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Afatech 9005 DVB-T USB1.1 stick"); 113862306a36Sopenharmony_ciMODULE_VERSION("1.0"); 113962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1140