Path: blob/master/drivers/media/dvb/firewire/firedtv-avc.c
15112 views
/*1* FireDTV driver (formerly known as FireSAT)2*3* Copyright (C) 2004 Andreas Monitzer <[email protected]>4* Copyright (C) 2008 Ben Backx <[email protected]>5* Copyright (C) 2008 Henrik Kurelid <[email protected]>6*7* This program is free software; you can redistribute it and/or8* modify it under the terms of the GNU General Public License as9* published by the Free Software Foundation; either version 2 of10* the License, or (at your option) any later version.11*/1213#include <linux/bug.h>14#include <linux/crc32.h>15#include <linux/delay.h>16#include <linux/device.h>17#include <linux/jiffies.h>18#include <linux/kernel.h>19#include <linux/moduleparam.h>20#include <linux/mutex.h>21#include <linux/string.h>22#include <linux/stringify.h>23#include <linux/wait.h>24#include <linux/workqueue.h>2526#include <dvb_frontend.h>2728#include "firedtv.h"2930#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL3132#define AVC_CTYPE_CONTROL 0x033#define AVC_CTYPE_STATUS 0x134#define AVC_CTYPE_NOTIFY 0x33536#define AVC_RESPONSE_ACCEPTED 0x937#define AVC_RESPONSE_STABLE 0xc38#define AVC_RESPONSE_CHANGED 0xd39#define AVC_RESPONSE_INTERIM 0xf4041#define AVC_SUBUNIT_TYPE_TUNER (0x05 << 3)42#define AVC_SUBUNIT_TYPE_UNIT (0x1f << 3)4344#define AVC_OPCODE_VENDOR 0x0045#define AVC_OPCODE_READ_DESCRIPTOR 0x0946#define AVC_OPCODE_DSIT 0xc847#define AVC_OPCODE_DSD 0xcb4849#define DESCRIPTOR_TUNER_STATUS 0x8050#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x005152#define SFE_VENDOR_DE_COMPANYID_0 0x00 /* OUI of Digital Everywhere */53#define SFE_VENDOR_DE_COMPANYID_1 0x1254#define SFE_VENDOR_DE_COMPANYID_2 0x875556#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0a57#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x5258#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 /* for DVB-S */5960#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x0061#define SFE_VENDOR_OPCODE_HOST2CA 0x5662#define SFE_VENDOR_OPCODE_CA2HOST 0x5763#define SFE_VENDOR_OPCODE_CISTATUS 0x5964#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 /* for DVB-S2 */6566#define SFE_VENDOR_TAG_CA_RESET 0x0067#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x0168#define SFE_VENDOR_TAG_CA_PMT 0x0269#define SFE_VENDOR_TAG_CA_DATE_TIME 0x0470#define SFE_VENDOR_TAG_CA_MMI 0x0571#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x077273#define EN50221_LIST_MANAGEMENT_ONLY 0x0374#define EN50221_TAG_APP_INFO 0x9f802175#define EN50221_TAG_CA_INFO 0x9f80317677struct avc_command_frame {78u8 ctype;79u8 subunit;80u8 opcode;81u8 operand[509];82};8384struct avc_response_frame {85u8 response;86u8 subunit;87u8 opcode;88u8 operand[509];89};9091#define LAST_OPERAND (509 - 1)9293static inline void clear_operands(struct avc_command_frame *c, int from, int to)94{95memset(&c->operand[from], 0, to - from + 1);96}9798static void pad_operands(struct avc_command_frame *c, int from)99{100int to = ALIGN(from, 4);101102if (from <= to && to <= LAST_OPERAND)103clear_operands(c, from, to);104}105106#define AVC_DEBUG_READ_DESCRIPTOR 0x0001107#define AVC_DEBUG_DSIT 0x0002108#define AVC_DEBUG_DSD 0x0004109#define AVC_DEBUG_REGISTER_REMOTE_CONTROL 0x0008110#define AVC_DEBUG_LNB_CONTROL 0x0010111#define AVC_DEBUG_TUNE_QPSK 0x0020112#define AVC_DEBUG_TUNE_QPSK2 0x0040113#define AVC_DEBUG_HOST2CA 0x0080114#define AVC_DEBUG_CA2HOST 0x0100115#define AVC_DEBUG_APPLICATION_PMT 0x4000116#define AVC_DEBUG_FCP_PAYLOADS 0x8000117118static int avc_debug;119module_param_named(debug, avc_debug, int, 0644);120MODULE_PARM_DESC(debug, "Verbose logging (none = 0"121", FCP subactions"122": READ DESCRIPTOR = " __stringify(AVC_DEBUG_READ_DESCRIPTOR)123", DSIT = " __stringify(AVC_DEBUG_DSIT)124", REGISTER_REMOTE_CONTROL = " __stringify(AVC_DEBUG_REGISTER_REMOTE_CONTROL)125", LNB CONTROL = " __stringify(AVC_DEBUG_LNB_CONTROL)126", TUNE QPSK = " __stringify(AVC_DEBUG_TUNE_QPSK)127", TUNE QPSK2 = " __stringify(AVC_DEBUG_TUNE_QPSK2)128", HOST2CA = " __stringify(AVC_DEBUG_HOST2CA)129", CA2HOST = " __stringify(AVC_DEBUG_CA2HOST)130"; Application sent PMT = " __stringify(AVC_DEBUG_APPLICATION_PMT)131", FCP payloads = " __stringify(AVC_DEBUG_FCP_PAYLOADS)132", or a combination, or all = -1)");133134/*135* This is a workaround since there is no vendor specific command to retrieve136* ca_info using AVC. If this parameter is not used, ca_system_id will be137* filled with application_manufacturer from ca_app_info.138* Digital Everywhere have said that adding ca_info is on their TODO list.139*/140static unsigned int num_fake_ca_system_ids;141static int fake_ca_system_ids[4] = { -1, -1, -1, -1 };142module_param_array(fake_ca_system_ids, int, &num_fake_ca_system_ids, 0644);143MODULE_PARM_DESC(fake_ca_system_ids, "If your CAM application manufacturer "144"does not have the same ca_system_id as your CAS, you can "145"override what ca_system_ids are presented to the "146"application by setting this field to an array of ids.");147148static const char *debug_fcp_ctype(unsigned int ctype)149{150static const char *ctypes[] = {151[0x0] = "CONTROL", [0x1] = "STATUS",152[0x2] = "SPECIFIC INQUIRY", [0x3] = "NOTIFY",153[0x4] = "GENERAL INQUIRY", [0x8] = "NOT IMPLEMENTED",154[0x9] = "ACCEPTED", [0xa] = "REJECTED",155[0xb] = "IN TRANSITION", [0xc] = "IMPLEMENTED/STABLE",156[0xd] = "CHANGED", [0xf] = "INTERIM",157};158const char *ret = ctype < ARRAY_SIZE(ctypes) ? ctypes[ctype] : NULL;159160return ret ? ret : "?";161}162163static const char *debug_fcp_opcode(unsigned int opcode,164const u8 *data, int length)165{166switch (opcode) {167case AVC_OPCODE_VENDOR:168break;169case AVC_OPCODE_READ_DESCRIPTOR:170return avc_debug & AVC_DEBUG_READ_DESCRIPTOR ?171"ReadDescriptor" : NULL;172case AVC_OPCODE_DSIT:173return avc_debug & AVC_DEBUG_DSIT ?174"DirectSelectInfo.Type" : NULL;175case AVC_OPCODE_DSD:176return avc_debug & AVC_DEBUG_DSD ? "DirectSelectData" : NULL;177default:178return "Unknown";179}180181if (length < 7 ||182data[3] != SFE_VENDOR_DE_COMPANYID_0 ||183data[4] != SFE_VENDOR_DE_COMPANYID_1 ||184data[5] != SFE_VENDOR_DE_COMPANYID_2)185return "Vendor/Unknown";186187switch (data[6]) {188case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL:189return avc_debug & AVC_DEBUG_REGISTER_REMOTE_CONTROL ?190"RegisterRC" : NULL;191case SFE_VENDOR_OPCODE_LNB_CONTROL:192return avc_debug & AVC_DEBUG_LNB_CONTROL ? "LNBControl" : NULL;193case SFE_VENDOR_OPCODE_TUNE_QPSK:194return avc_debug & AVC_DEBUG_TUNE_QPSK ? "TuneQPSK" : NULL;195case SFE_VENDOR_OPCODE_TUNE_QPSK2:196return avc_debug & AVC_DEBUG_TUNE_QPSK2 ? "TuneQPSK2" : NULL;197case SFE_VENDOR_OPCODE_HOST2CA:198return avc_debug & AVC_DEBUG_HOST2CA ? "Host2CA" : NULL;199case SFE_VENDOR_OPCODE_CA2HOST:200return avc_debug & AVC_DEBUG_CA2HOST ? "CA2Host" : NULL;201}202return "Vendor/Unknown";203}204205static void debug_fcp(const u8 *data, int length)206{207unsigned int subunit_type, subunit_id, opcode;208const char *op, *prefix;209210prefix = data[0] > 7 ? "FCP <- " : "FCP -> ";211subunit_type = data[1] >> 3;212subunit_id = data[1] & 7;213opcode = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2];214op = debug_fcp_opcode(opcode, data, length);215216if (op) {217printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n",218prefix, subunit_type, subunit_id, length,219debug_fcp_ctype(data[0]), op);220if (avc_debug & AVC_DEBUG_FCP_PAYLOADS)221print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE,22216, 1, data, length, false);223}224}225226static void debug_pmt(char *msg, int length)227{228printk(KERN_INFO "APP PMT -> l=%d\n", length);229print_hex_dump(KERN_INFO, "APP PMT -> ", DUMP_PREFIX_NONE,23016, 1, msg, length, false);231}232233static int avc_write(struct firedtv *fdtv)234{235int err, retry;236237fdtv->avc_reply_received = false;238239for (retry = 0; retry < 6; retry++) {240if (unlikely(avc_debug))241debug_fcp(fdtv->avc_data, fdtv->avc_data_length);242243err = fdtv_write(fdtv, FCP_COMMAND_REGISTER,244fdtv->avc_data, fdtv->avc_data_length);245if (err) {246dev_err(fdtv->device, "FCP command write failed\n");247248return err;249}250251/*252* AV/C specs say that answers should be sent within 150 ms.253* Time out after 200 ms.254*/255if (wait_event_timeout(fdtv->avc_wait,256fdtv->avc_reply_received,257msecs_to_jiffies(200)) != 0)258return 0;259}260dev_err(fdtv->device, "FCP response timed out\n");261262return -ETIMEDOUT;263}264265static bool is_register_rc(struct avc_response_frame *r)266{267return r->opcode == AVC_OPCODE_VENDOR &&268r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 &&269r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 &&270r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 &&271r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL;272}273274int avc_recv(struct firedtv *fdtv, void *data, size_t length)275{276struct avc_response_frame *r = data;277278if (unlikely(avc_debug))279debug_fcp(data, length);280281if (length >= 8 && is_register_rc(r)) {282switch (r->response) {283case AVC_RESPONSE_CHANGED:284fdtv_handle_rc(fdtv, r->operand[4] << 8 | r->operand[5]);285schedule_work(&fdtv->remote_ctrl_work);286break;287case AVC_RESPONSE_INTERIM:288if (is_register_rc((void *)fdtv->avc_data))289goto wake;290break;291default:292dev_info(fdtv->device,293"remote control result = %d\n", r->response);294}295return 0;296}297298if (fdtv->avc_reply_received) {299dev_err(fdtv->device, "out-of-order AVC response, ignored\n");300return -EIO;301}302303memcpy(fdtv->avc_data, data, length);304fdtv->avc_data_length = length;305wake:306fdtv->avc_reply_received = true;307wake_up(&fdtv->avc_wait);308309return 0;310}311312static int add_pid_filter(struct firedtv *fdtv, u8 *operand)313{314int i, n, pos = 1;315316for (i = 0, n = 0; i < 16; i++) {317if (test_bit(i, &fdtv->channel_active)) {318operand[pos++] = 0x13; /* flowfunction relay */319operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */320operand[pos++] = (fdtv->channel_pid[i] >> 8) & 0x1f;321operand[pos++] = fdtv->channel_pid[i] & 0xff;322operand[pos++] = 0x00; /* tableID */323operand[pos++] = 0x00; /* filter_length */324n++;325}326}327operand[0] = n;328329return pos;330}331332/*333* tuning command for setting the relative LNB frequency334* (not supported by the AVC standard)335*/336static int avc_tuner_tuneqpsk(struct firedtv *fdtv,337struct dvb_frontend_parameters *params)338{339struct avc_command_frame *c = (void *)fdtv->avc_data;340341c->opcode = AVC_OPCODE_VENDOR;342343c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;344c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;345c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;346if (fdtv->type == FIREDTV_DVB_S2)347c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK2;348else349c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK;350351c->operand[4] = (params->frequency >> 24) & 0xff;352c->operand[5] = (params->frequency >> 16) & 0xff;353c->operand[6] = (params->frequency >> 8) & 0xff;354c->operand[7] = params->frequency & 0xff;355356c->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff;357c->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff;358359switch (params->u.qpsk.fec_inner) {360case FEC_1_2: c->operand[10] = 0x1; break;361case FEC_2_3: c->operand[10] = 0x2; break;362case FEC_3_4: c->operand[10] = 0x3; break;363case FEC_5_6: c->operand[10] = 0x4; break;364case FEC_7_8: c->operand[10] = 0x5; break;365case FEC_4_5:366case FEC_8_9:367case FEC_AUTO:368default: c->operand[10] = 0x0;369}370371if (fdtv->voltage == 0xff)372c->operand[11] = 0xff;373else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */374c->operand[11] = 0;375else376c->operand[11] = 1;377378if (fdtv->tone == 0xff)379c->operand[12] = 0xff;380else if (fdtv->tone == SEC_TONE_ON) /* band */381c->operand[12] = 1;382else383c->operand[12] = 0;384385if (fdtv->type == FIREDTV_DVB_S2) {386if (fdtv->fe.dtv_property_cache.delivery_system == SYS_DVBS2) {387switch (fdtv->fe.dtv_property_cache.modulation) {388case QAM_16: c->operand[13] = 0x1; break;389case QPSK: c->operand[13] = 0x2; break;390case PSK_8: c->operand[13] = 0x3; break;391default: c->operand[13] = 0x2; break;392}393switch (fdtv->fe.dtv_property_cache.rolloff) {394case ROLLOFF_AUTO: c->operand[14] = 0x2; break;395case ROLLOFF_35: c->operand[14] = 0x2; break;396case ROLLOFF_20: c->operand[14] = 0x0; break;397case ROLLOFF_25: c->operand[14] = 0x1; break;398/* case ROLLOFF_NONE: c->operand[14] = 0xff; break; */399}400switch (fdtv->fe.dtv_property_cache.pilot) {401case PILOT_AUTO: c->operand[15] = 0x0; break;402case PILOT_OFF: c->operand[15] = 0x0; break;403case PILOT_ON: c->operand[15] = 0x1; break;404}405} else {406c->operand[13] = 0x1; /* auto modulation */407c->operand[14] = 0xff; /* disable rolloff */408c->operand[15] = 0xff; /* disable pilot */409}410return 16;411} else {412return 13;413}414}415416static int avc_tuner_dsd_dvb_c(struct firedtv *fdtv,417struct dvb_frontend_parameters *params)418{419struct avc_command_frame *c = (void *)fdtv->avc_data;420421c->opcode = AVC_OPCODE_DSD;422423c->operand[0] = 0; /* source plug */424c->operand[1] = 0xd2; /* subfunction replace */425c->operand[2] = 0x20; /* system id = DVB */426c->operand[3] = 0x00; /* antenna number */427c->operand[4] = 0x11; /* system_specific_multiplex selection_length */428429/* multiplex_valid_flags, high byte */430c->operand[5] = 0 << 7 /* reserved */431| 0 << 6 /* Polarisation */432| 0 << 5 /* Orbital_Pos */433| 1 << 4 /* Frequency */434| 1 << 3 /* Symbol_Rate */435| 0 << 2 /* FEC_outer */436| (params->u.qam.fec_inner != FEC_AUTO ? 1 << 1 : 0)437| (params->u.qam.modulation != QAM_AUTO ? 1 << 0 : 0);438439/* multiplex_valid_flags, low byte */440c->operand[6] = 0 << 7 /* NetworkID */441| 0 << 0 /* reserved */ ;442443c->operand[7] = 0x00;444c->operand[8] = 0x00;445c->operand[9] = 0x00;446c->operand[10] = 0x00;447448c->operand[11] = (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6);449c->operand[12] = ((params->frequency / 4000) >> 8) & 0xff;450c->operand[13] = (params->frequency / 4000) & 0xff;451c->operand[14] = ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff;452c->operand[15] = ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff;453c->operand[16] = ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0;454c->operand[17] = 0x00;455456switch (params->u.qpsk.fec_inner) {457case FEC_1_2: c->operand[18] = 0x1; break;458case FEC_2_3: c->operand[18] = 0x2; break;459case FEC_3_4: c->operand[18] = 0x3; break;460case FEC_5_6: c->operand[18] = 0x4; break;461case FEC_7_8: c->operand[18] = 0x5; break;462case FEC_8_9: c->operand[18] = 0x6; break;463case FEC_4_5: c->operand[18] = 0x8; break;464case FEC_AUTO:465default: c->operand[18] = 0x0;466}467468switch (params->u.qam.modulation) {469case QAM_16: c->operand[19] = 0x08; break;470case QAM_32: c->operand[19] = 0x10; break;471case QAM_64: c->operand[19] = 0x18; break;472case QAM_128: c->operand[19] = 0x20; break;473case QAM_256: c->operand[19] = 0x28; break;474case QAM_AUTO:475default: c->operand[19] = 0x00;476}477478c->operand[20] = 0x00;479c->operand[21] = 0x00;480481return 22 + add_pid_filter(fdtv, &c->operand[22]);482}483484static int avc_tuner_dsd_dvb_t(struct firedtv *fdtv,485struct dvb_frontend_parameters *params)486{487struct dvb_ofdm_parameters *ofdm = ¶ms->u.ofdm;488struct avc_command_frame *c = (void *)fdtv->avc_data;489490c->opcode = AVC_OPCODE_DSD;491492c->operand[0] = 0; /* source plug */493c->operand[1] = 0xd2; /* subfunction replace */494c->operand[2] = 0x20; /* system id = DVB */495c->operand[3] = 0x00; /* antenna number */496c->operand[4] = 0x0c; /* system_specific_multiplex selection_length */497498/* multiplex_valid_flags, high byte */499c->operand[5] =5000 << 7 /* reserved */501| 1 << 6 /* CenterFrequency */502| (ofdm->bandwidth != BANDWIDTH_AUTO ? 1 << 5 : 0)503| (ofdm->constellation != QAM_AUTO ? 1 << 4 : 0)504| (ofdm->hierarchy_information != HIERARCHY_AUTO ? 1 << 3 : 0)505| (ofdm->code_rate_HP != FEC_AUTO ? 1 << 2 : 0)506| (ofdm->code_rate_LP != FEC_AUTO ? 1 << 1 : 0)507| (ofdm->guard_interval != GUARD_INTERVAL_AUTO ? 1 << 0 : 0);508509/* multiplex_valid_flags, low byte */510c->operand[6] =5110 << 7 /* NetworkID */512| (ofdm->transmission_mode != TRANSMISSION_MODE_AUTO ? 1 << 6 : 0)513| 0 << 5 /* OtherFrequencyFlag */514| 0 << 0 /* reserved */ ;515516c->operand[7] = 0x0;517c->operand[8] = (params->frequency / 10) >> 24;518c->operand[9] = ((params->frequency / 10) >> 16) & 0xff;519c->operand[10] = ((params->frequency / 10) >> 8) & 0xff;520c->operand[11] = (params->frequency / 10) & 0xff;521522switch (ofdm->bandwidth) {523case BANDWIDTH_7_MHZ: c->operand[12] = 0x20; break;524case BANDWIDTH_8_MHZ:525case BANDWIDTH_6_MHZ: /* not defined by AVC spec */526case BANDWIDTH_AUTO:527default: c->operand[12] = 0x00;528}529530switch (ofdm->constellation) {531case QAM_16: c->operand[13] = 1 << 6; break;532case QAM_64: c->operand[13] = 2 << 6; break;533case QPSK:534default: c->operand[13] = 0x00;535}536537switch (ofdm->hierarchy_information) {538case HIERARCHY_1: c->operand[13] |= 1 << 3; break;539case HIERARCHY_2: c->operand[13] |= 2 << 3; break;540case HIERARCHY_4: c->operand[13] |= 3 << 3; break;541case HIERARCHY_AUTO:542case HIERARCHY_NONE:543default: break;544}545546switch (ofdm->code_rate_HP) {547case FEC_2_3: c->operand[13] |= 1; break;548case FEC_3_4: c->operand[13] |= 2; break;549case FEC_5_6: c->operand[13] |= 3; break;550case FEC_7_8: c->operand[13] |= 4; break;551case FEC_1_2:552default: break;553}554555switch (ofdm->code_rate_LP) {556case FEC_2_3: c->operand[14] = 1 << 5; break;557case FEC_3_4: c->operand[14] = 2 << 5; break;558case FEC_5_6: c->operand[14] = 3 << 5; break;559case FEC_7_8: c->operand[14] = 4 << 5; break;560case FEC_1_2:561default: c->operand[14] = 0x00; break;562}563564switch (ofdm->guard_interval) {565case GUARD_INTERVAL_1_16: c->operand[14] |= 1 << 3; break;566case GUARD_INTERVAL_1_8: c->operand[14] |= 2 << 3; break;567case GUARD_INTERVAL_1_4: c->operand[14] |= 3 << 3; break;568case GUARD_INTERVAL_1_32:569case GUARD_INTERVAL_AUTO:570default: break;571}572573switch (ofdm->transmission_mode) {574case TRANSMISSION_MODE_8K: c->operand[14] |= 1 << 1; break;575case TRANSMISSION_MODE_2K:576case TRANSMISSION_MODE_AUTO:577default: break;578}579580c->operand[15] = 0x00; /* network_ID[0] */581c->operand[16] = 0x00; /* network_ID[1] */582583return 17 + add_pid_filter(fdtv, &c->operand[17]);584}585586int avc_tuner_dsd(struct firedtv *fdtv,587struct dvb_frontend_parameters *params)588{589struct avc_command_frame *c = (void *)fdtv->avc_data;590int pos, ret;591592mutex_lock(&fdtv->avc_mutex);593594c->ctype = AVC_CTYPE_CONTROL;595c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;596597switch (fdtv->type) {598case FIREDTV_DVB_S:599case FIREDTV_DVB_S2: pos = avc_tuner_tuneqpsk(fdtv, params); break;600case FIREDTV_DVB_C: pos = avc_tuner_dsd_dvb_c(fdtv, params); break;601case FIREDTV_DVB_T: pos = avc_tuner_dsd_dvb_t(fdtv, params); break;602default:603BUG();604}605pad_operands(c, pos);606607fdtv->avc_data_length = ALIGN(3 + pos, 4);608ret = avc_write(fdtv);609#if 0610/*611* FIXME:612* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller.613* Check for AVC_RESPONSE_ACCEPTED here instead?614*/615if (status)616*status = r->operand[2];617#endif618mutex_unlock(&fdtv->avc_mutex);619620if (ret == 0)621msleep(500);622623return ret;624}625626int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[])627{628struct avc_command_frame *c = (void *)fdtv->avc_data;629int ret, pos, k;630631if (pidc > 16 && pidc != 0xff)632return -EINVAL;633634mutex_lock(&fdtv->avc_mutex);635636c->ctype = AVC_CTYPE_CONTROL;637c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;638c->opcode = AVC_OPCODE_DSD;639640c->operand[0] = 0; /* source plug */641c->operand[1] = 0xd2; /* subfunction replace */642c->operand[2] = 0x20; /* system id = DVB */643c->operand[3] = 0x00; /* antenna number */644c->operand[4] = 0x00; /* system_specific_multiplex selection_length */645c->operand[5] = pidc; /* Nr_of_dsd_sel_specs */646647pos = 6;648if (pidc != 0xff)649for (k = 0; k < pidc; k++) {650c->operand[pos++] = 0x13; /* flowfunction relay */651c->operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */652c->operand[pos++] = (pid[k] >> 8) & 0x1f;653c->operand[pos++] = pid[k] & 0xff;654c->operand[pos++] = 0x00; /* tableID */655c->operand[pos++] = 0x00; /* filter_length */656}657pad_operands(c, pos);658659fdtv->avc_data_length = ALIGN(3 + pos, 4);660ret = avc_write(fdtv);661662/* FIXME: check response code? */663664mutex_unlock(&fdtv->avc_mutex);665666if (ret == 0)667msleep(50);668669return ret;670}671672int avc_tuner_get_ts(struct firedtv *fdtv)673{674struct avc_command_frame *c = (void *)fdtv->avc_data;675int ret, sl;676677mutex_lock(&fdtv->avc_mutex);678679c->ctype = AVC_CTYPE_CONTROL;680c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;681c->opcode = AVC_OPCODE_DSIT;682683sl = fdtv->type == FIREDTV_DVB_T ? 0x0c : 0x11;684685c->operand[0] = 0; /* source plug */686c->operand[1] = 0xd2; /* subfunction replace */687c->operand[2] = 0xff; /* status */688c->operand[3] = 0x20; /* system id = DVB */689c->operand[4] = 0x00; /* antenna number */690c->operand[5] = 0x0; /* system_specific_search_flags */691c->operand[6] = sl; /* system_specific_multiplex selection_length */692/*693* operand[7]: valid_flags[0]694* operand[8]: valid_flags[1]695* operand[7 + sl]: nr_of_dsit_sel_specs (always 0)696*/697clear_operands(c, 7, 24);698699fdtv->avc_data_length = fdtv->type == FIREDTV_DVB_T ? 24 : 28;700ret = avc_write(fdtv);701702/* FIXME: check response code? */703704mutex_unlock(&fdtv->avc_mutex);705706if (ret == 0)707msleep(250);708709return ret;710}711712int avc_identify_subunit(struct firedtv *fdtv)713{714struct avc_command_frame *c = (void *)fdtv->avc_data;715struct avc_response_frame *r = (void *)fdtv->avc_data;716int ret;717718mutex_lock(&fdtv->avc_mutex);719720c->ctype = AVC_CTYPE_CONTROL;721c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;722c->opcode = AVC_OPCODE_READ_DESCRIPTOR;723724c->operand[0] = DESCRIPTOR_SUBUNIT_IDENTIFIER;725c->operand[1] = 0xff;726c->operand[2] = 0x00;727c->operand[3] = 0x00; /* length highbyte */728c->operand[4] = 0x08; /* length lowbyte */729c->operand[5] = 0x00; /* offset highbyte */730c->operand[6] = 0x0d; /* offset lowbyte */731clear_operands(c, 7, 8); /* padding */732733fdtv->avc_data_length = 12;734ret = avc_write(fdtv);735if (ret < 0)736goto out;737738if ((r->response != AVC_RESPONSE_STABLE &&739r->response != AVC_RESPONSE_ACCEPTED) ||740(r->operand[3] << 8) + r->operand[4] != 8) {741dev_err(fdtv->device, "cannot read subunit identifier\n");742ret = -EINVAL;743}744out:745mutex_unlock(&fdtv->avc_mutex);746747return ret;748}749750#define SIZEOF_ANTENNA_INPUT_INFO 22751752int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat)753{754struct avc_command_frame *c = (void *)fdtv->avc_data;755struct avc_response_frame *r = (void *)fdtv->avc_data;756int length, ret;757758mutex_lock(&fdtv->avc_mutex);759760c->ctype = AVC_CTYPE_CONTROL;761c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;762c->opcode = AVC_OPCODE_READ_DESCRIPTOR;763764c->operand[0] = DESCRIPTOR_TUNER_STATUS;765c->operand[1] = 0xff; /* read_result_status */766/*767* operand[2]: reserved768* operand[3]: SIZEOF_ANTENNA_INPUT_INFO >> 8769* operand[4]: SIZEOF_ANTENNA_INPUT_INFO & 0xff770*/771clear_operands(c, 2, 31);772773fdtv->avc_data_length = 12;774ret = avc_write(fdtv);775if (ret < 0)776goto out;777778if (r->response != AVC_RESPONSE_STABLE &&779r->response != AVC_RESPONSE_ACCEPTED) {780dev_err(fdtv->device, "cannot read tuner status\n");781ret = -EINVAL;782goto out;783}784785length = r->operand[9];786if (r->operand[1] != 0x10 || length != SIZEOF_ANTENNA_INPUT_INFO) {787dev_err(fdtv->device, "got invalid tuner status\n");788ret = -EINVAL;789goto out;790}791792stat->active_system = r->operand[10];793stat->searching = r->operand[11] >> 7 & 1;794stat->moving = r->operand[11] >> 6 & 1;795stat->no_rf = r->operand[11] >> 5 & 1;796stat->input = r->operand[12] >> 7 & 1;797stat->selected_antenna = r->operand[12] & 0x7f;798stat->ber = r->operand[13] << 24 |799r->operand[14] << 16 |800r->operand[15] << 8 |801r->operand[16];802stat->signal_strength = r->operand[17];803stat->raster_frequency = r->operand[18] >> 6 & 2;804stat->rf_frequency = (r->operand[18] & 0x3f) << 16 |805r->operand[19] << 8 |806r->operand[20];807stat->man_dep_info_length = r->operand[21];808stat->front_end_error = r->operand[22] >> 4 & 1;809stat->antenna_error = r->operand[22] >> 3 & 1;810stat->front_end_power_status = r->operand[22] >> 1 & 1;811stat->power_supply = r->operand[22] & 1;812stat->carrier_noise_ratio = r->operand[23] << 8 |813r->operand[24];814stat->power_supply_voltage = r->operand[27];815stat->antenna_voltage = r->operand[28];816stat->firewire_bus_voltage = r->operand[29];817stat->ca_mmi = r->operand[30] & 1;818stat->ca_pmt_reply = r->operand[31] >> 7 & 1;819stat->ca_date_time_request = r->operand[31] >> 6 & 1;820stat->ca_application_info = r->operand[31] >> 5 & 1;821stat->ca_module_present_status = r->operand[31] >> 4 & 1;822stat->ca_dvb_flag = r->operand[31] >> 3 & 1;823stat->ca_error_flag = r->operand[31] >> 2 & 1;824stat->ca_initialization_status = r->operand[31] >> 1 & 1;825out:826mutex_unlock(&fdtv->avc_mutex);827828return ret;829}830831int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst,832char conttone, char nrdiseq,833struct dvb_diseqc_master_cmd *diseqcmd)834{835struct avc_command_frame *c = (void *)fdtv->avc_data;836struct avc_response_frame *r = (void *)fdtv->avc_data;837int pos, j, k, ret;838839mutex_lock(&fdtv->avc_mutex);840841c->ctype = AVC_CTYPE_CONTROL;842c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;843c->opcode = AVC_OPCODE_VENDOR;844845c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;846c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;847c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;848c->operand[3] = SFE_VENDOR_OPCODE_LNB_CONTROL;849c->operand[4] = voltage;850c->operand[5] = nrdiseq;851852pos = 6;853for (j = 0; j < nrdiseq; j++) {854c->operand[pos++] = diseqcmd[j].msg_len;855856for (k = 0; k < diseqcmd[j].msg_len; k++)857c->operand[pos++] = diseqcmd[j].msg[k];858}859c->operand[pos++] = burst;860c->operand[pos++] = conttone;861pad_operands(c, pos);862863fdtv->avc_data_length = ALIGN(3 + pos, 4);864ret = avc_write(fdtv);865if (ret < 0)866goto out;867868if (r->response != AVC_RESPONSE_ACCEPTED) {869dev_err(fdtv->device, "LNB control failed\n");870ret = -EINVAL;871}872out:873mutex_unlock(&fdtv->avc_mutex);874875return ret;876}877878int avc_register_remote_control(struct firedtv *fdtv)879{880struct avc_command_frame *c = (void *)fdtv->avc_data;881int ret;882883mutex_lock(&fdtv->avc_mutex);884885c->ctype = AVC_CTYPE_NOTIFY;886c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7;887c->opcode = AVC_OPCODE_VENDOR;888889c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;890c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;891c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;892c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL;893c->operand[4] = 0; /* padding */894895fdtv->avc_data_length = 8;896ret = avc_write(fdtv);897898/* FIXME: check response code? */899900mutex_unlock(&fdtv->avc_mutex);901902return ret;903}904905void avc_remote_ctrl_work(struct work_struct *work)906{907struct firedtv *fdtv =908container_of(work, struct firedtv, remote_ctrl_work);909910/* Should it be rescheduled in failure cases? */911avc_register_remote_control(fdtv);912}913914#if 0 /* FIXME: unused */915int avc_tuner_host2ca(struct firedtv *fdtv)916{917struct avc_command_frame *c = (void *)fdtv->avc_data;918int ret;919920mutex_lock(&fdtv->avc_mutex);921922c->ctype = AVC_CTYPE_CONTROL;923c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;924c->opcode = AVC_OPCODE_VENDOR;925926c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;927c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;928c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;929c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA;930c->operand[4] = 0; /* slot */931c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */932clear_operands(c, 6, 8);933934fdtv->avc_data_length = 12;935ret = avc_write(fdtv);936937/* FIXME: check response code? */938939mutex_unlock(&fdtv->avc_mutex);940941return ret;942}943#endif944945static int get_ca_object_pos(struct avc_response_frame *r)946{947int length = 1;948949/* Check length of length field */950if (r->operand[7] & 0x80)951length = (r->operand[7] & 0x7f) + 1;952return length + 7;953}954955static int get_ca_object_length(struct avc_response_frame *r)956{957#if 0 /* FIXME: unused */958int size = 0;959int i;960961if (r->operand[7] & 0x80)962for (i = 0; i < (r->operand[7] & 0x7f); i++) {963size <<= 8;964size += r->operand[8 + i];965}966#endif967return r->operand[7];968}969970int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len)971{972struct avc_command_frame *c = (void *)fdtv->avc_data;973struct avc_response_frame *r = (void *)fdtv->avc_data;974int pos, ret;975976mutex_lock(&fdtv->avc_mutex);977978c->ctype = AVC_CTYPE_STATUS;979c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;980c->opcode = AVC_OPCODE_VENDOR;981982c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;983c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;984c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;985c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST;986c->operand[4] = 0; /* slot */987c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */988clear_operands(c, 6, LAST_OPERAND);989990fdtv->avc_data_length = 12;991ret = avc_write(fdtv);992if (ret < 0)993goto out;994995/* FIXME: check response code and validate response data */996997pos = get_ca_object_pos(r);998app_info[0] = (EN50221_TAG_APP_INFO >> 16) & 0xff;999app_info[1] = (EN50221_TAG_APP_INFO >> 8) & 0xff;1000app_info[2] = (EN50221_TAG_APP_INFO >> 0) & 0xff;1001app_info[3] = 6 + r->operand[pos + 4];1002app_info[4] = 0x01;1003memcpy(&app_info[5], &r->operand[pos], 5 + r->operand[pos + 4]);1004*len = app_info[3] + 4;1005out:1006mutex_unlock(&fdtv->avc_mutex);10071008return ret;1009}10101011int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len)1012{1013struct avc_command_frame *c = (void *)fdtv->avc_data;1014struct avc_response_frame *r = (void *)fdtv->avc_data;1015int i, pos, ret;10161017mutex_lock(&fdtv->avc_mutex);10181019c->ctype = AVC_CTYPE_STATUS;1020c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;1021c->opcode = AVC_OPCODE_VENDOR;10221023c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;1024c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;1025c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;1026c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST;1027c->operand[4] = 0; /* slot */1028c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */1029clear_operands(c, 6, LAST_OPERAND);10301031fdtv->avc_data_length = 12;1032ret = avc_write(fdtv);1033if (ret < 0)1034goto out;10351036/* FIXME: check response code and validate response data */10371038pos = get_ca_object_pos(r);1039app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff;1040app_info[1] = (EN50221_TAG_CA_INFO >> 8) & 0xff;1041app_info[2] = (EN50221_TAG_CA_INFO >> 0) & 0xff;1042if (num_fake_ca_system_ids == 0) {1043app_info[3] = 2;1044app_info[4] = r->operand[pos + 0];1045app_info[5] = r->operand[pos + 1];1046} else {1047app_info[3] = num_fake_ca_system_ids * 2;1048for (i = 0; i < num_fake_ca_system_ids; i++) {1049app_info[4 + i * 2] =1050(fake_ca_system_ids[i] >> 8) & 0xff;1051app_info[5 + i * 2] = fake_ca_system_ids[i] & 0xff;1052}1053}1054*len = app_info[3] + 4;1055out:1056mutex_unlock(&fdtv->avc_mutex);10571058return ret;1059}10601061int avc_ca_reset(struct firedtv *fdtv)1062{1063struct avc_command_frame *c = (void *)fdtv->avc_data;1064int ret;10651066mutex_lock(&fdtv->avc_mutex);10671068c->ctype = AVC_CTYPE_CONTROL;1069c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;1070c->opcode = AVC_OPCODE_VENDOR;10711072c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;1073c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;1074c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;1075c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA;1076c->operand[4] = 0; /* slot */1077c->operand[5] = SFE_VENDOR_TAG_CA_RESET; /* ca tag */1078c->operand[6] = 0; /* more/last */1079c->operand[7] = 1; /* length */1080c->operand[8] = 0; /* force hardware reset */10811082fdtv->avc_data_length = 12;1083ret = avc_write(fdtv);10841085/* FIXME: check response code? */10861087mutex_unlock(&fdtv->avc_mutex);10881089return ret;1090}10911092int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)1093{1094struct avc_command_frame *c = (void *)fdtv->avc_data;1095struct avc_response_frame *r = (void *)fdtv->avc_data;1096int list_management;1097int program_info_length;1098int pmt_cmd_id;1099int read_pos;1100int write_pos;1101int es_info_length;1102int crc32_csum;1103int ret;11041105if (unlikely(avc_debug & AVC_DEBUG_APPLICATION_PMT))1106debug_pmt(msg, length);11071108mutex_lock(&fdtv->avc_mutex);11091110c->ctype = AVC_CTYPE_CONTROL;1111c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;1112c->opcode = AVC_OPCODE_VENDOR;11131114if (msg[0] != EN50221_LIST_MANAGEMENT_ONLY) {1115dev_info(fdtv->device, "forcing list_management to ONLY\n");1116msg[0] = EN50221_LIST_MANAGEMENT_ONLY;1117}1118/* We take the cmd_id from the programme level only! */1119list_management = msg[0];1120program_info_length = ((msg[4] & 0x0f) << 8) + msg[5];1121if (program_info_length > 0)1122program_info_length--; /* Remove pmt_cmd_id */1123pmt_cmd_id = msg[6];11241125c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;1126c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;1127c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;1128c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA;1129c->operand[4] = 0; /* slot */1130c->operand[5] = SFE_VENDOR_TAG_CA_PMT; /* ca tag */1131c->operand[6] = 0; /* more/last */1132/* Use three bytes for length field in case length > 127 */1133c->operand[10] = list_management;1134c->operand[11] = 0x01; /* pmt_cmd=OK_descramble */11351136/* TS program map table */11371138c->operand[12] = 0x02; /* Table id=2 */1139c->operand[13] = 0x80; /* Section syntax + length */11401141c->operand[15] = msg[1]; /* Program number */1142c->operand[16] = msg[2];1143c->operand[17] = msg[3]; /* Version number and current/next */1144c->operand[18] = 0x00; /* Section number=0 */1145c->operand[19] = 0x00; /* Last section number=0 */1146c->operand[20] = 0x1f; /* PCR_PID=1FFF */1147c->operand[21] = 0xff;1148c->operand[22] = (program_info_length >> 8); /* Program info length */1149c->operand[23] = (program_info_length & 0xff);11501151/* CA descriptors at programme level */1152read_pos = 6;1153write_pos = 24;1154if (program_info_length > 0) {1155pmt_cmd_id = msg[read_pos++];1156if (pmt_cmd_id != 1 && pmt_cmd_id != 4)1157dev_err(fdtv->device,1158"invalid pmt_cmd_id %d\n", pmt_cmd_id);11591160memcpy(&c->operand[write_pos], &msg[read_pos],1161program_info_length);1162read_pos += program_info_length;1163write_pos += program_info_length;1164}1165while (read_pos < length) {1166c->operand[write_pos++] = msg[read_pos++];1167c->operand[write_pos++] = msg[read_pos++];1168c->operand[write_pos++] = msg[read_pos++];1169es_info_length =1170((msg[read_pos] & 0x0f) << 8) + msg[read_pos + 1];1171read_pos += 2;1172if (es_info_length > 0)1173es_info_length--; /* Remove pmt_cmd_id */1174c->operand[write_pos++] = es_info_length >> 8;1175c->operand[write_pos++] = es_info_length & 0xff;1176if (es_info_length > 0) {1177pmt_cmd_id = msg[read_pos++];1178if (pmt_cmd_id != 1 && pmt_cmd_id != 4)1179dev_err(fdtv->device, "invalid pmt_cmd_id %d "1180"at stream level\n", pmt_cmd_id);11811182memcpy(&c->operand[write_pos], &msg[read_pos],1183es_info_length);1184read_pos += es_info_length;1185write_pos += es_info_length;1186}1187}1188write_pos += 4; /* CRC */11891190c->operand[7] = 0x82;1191c->operand[8] = (write_pos - 10) >> 8;1192c->operand[9] = (write_pos - 10) & 0xff;1193c->operand[14] = write_pos - 15;11941195crc32_csum = crc32_be(0, &c->operand[10], c->operand[12] - 1);1196c->operand[write_pos - 4] = (crc32_csum >> 24) & 0xff;1197c->operand[write_pos - 3] = (crc32_csum >> 16) & 0xff;1198c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff;1199c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff;1200pad_operands(c, write_pos);12011202fdtv->avc_data_length = ALIGN(3 + write_pos, 4);1203ret = avc_write(fdtv);1204if (ret < 0)1205goto out;12061207if (r->response != AVC_RESPONSE_ACCEPTED) {1208dev_err(fdtv->device,1209"CA PMT failed with response 0x%x\n", r->response);1210ret = -EFAULT;1211}1212out:1213mutex_unlock(&fdtv->avc_mutex);12141215return ret;1216}12171218int avc_ca_get_time_date(struct firedtv *fdtv, int *interval)1219{1220struct avc_command_frame *c = (void *)fdtv->avc_data;1221struct avc_response_frame *r = (void *)fdtv->avc_data;1222int ret;12231224mutex_lock(&fdtv->avc_mutex);12251226c->ctype = AVC_CTYPE_STATUS;1227c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;1228c->opcode = AVC_OPCODE_VENDOR;12291230c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;1231c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;1232c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;1233c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST;1234c->operand[4] = 0; /* slot */1235c->operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; /* ca tag */1236clear_operands(c, 6, LAST_OPERAND);12371238fdtv->avc_data_length = 12;1239ret = avc_write(fdtv);1240if (ret < 0)1241goto out;12421243/* FIXME: check response code and validate response data */12441245*interval = r->operand[get_ca_object_pos(r)];1246out:1247mutex_unlock(&fdtv->avc_mutex);12481249return ret;1250}12511252int avc_ca_enter_menu(struct firedtv *fdtv)1253{1254struct avc_command_frame *c = (void *)fdtv->avc_data;1255int ret;12561257mutex_lock(&fdtv->avc_mutex);12581259c->ctype = AVC_CTYPE_STATUS;1260c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;1261c->opcode = AVC_OPCODE_VENDOR;12621263c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;1264c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;1265c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;1266c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA;1267c->operand[4] = 0; /* slot */1268c->operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU;1269clear_operands(c, 6, 8);12701271fdtv->avc_data_length = 12;1272ret = avc_write(fdtv);12731274/* FIXME: check response code? */12751276mutex_unlock(&fdtv->avc_mutex);12771278return ret;1279}12801281int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len)1282{1283struct avc_command_frame *c = (void *)fdtv->avc_data;1284struct avc_response_frame *r = (void *)fdtv->avc_data;1285int ret;12861287mutex_lock(&fdtv->avc_mutex);12881289c->ctype = AVC_CTYPE_STATUS;1290c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit;1291c->opcode = AVC_OPCODE_VENDOR;12921293c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;1294c->operand[1] = SFE_VENDOR_DE_COMPANYID_1;1295c->operand[2] = SFE_VENDOR_DE_COMPANYID_2;1296c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST;1297c->operand[4] = 0; /* slot */1298c->operand[5] = SFE_VENDOR_TAG_CA_MMI;1299clear_operands(c, 6, LAST_OPERAND);13001301fdtv->avc_data_length = 12;1302ret = avc_write(fdtv);1303if (ret < 0)1304goto out;13051306/* FIXME: check response code and validate response data */13071308*len = get_ca_object_length(r);1309memcpy(mmi_object, &r->operand[get_ca_object_pos(r)], *len);1310out:1311mutex_unlock(&fdtv->avc_mutex);13121313return ret;1314}13151316#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL13171318static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data)1319{1320int ret;13211322ret = fdtv_read(fdtv, addr, data);1323if (ret < 0)1324dev_err(fdtv->device, "CMP: read I/O error\n");13251326return ret;1327}13281329static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[])1330{1331int ret;13321333ret = fdtv_lock(fdtv, addr, data);1334if (ret < 0)1335dev_err(fdtv->device, "CMP: lock I/O error\n");13361337return ret;1338}13391340static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift)1341{1342return (be32_to_cpu(opcr) >> shift) & mask;1343}13441345static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift)1346{1347*opcr &= ~cpu_to_be32(mask << shift);1348*opcr |= cpu_to_be32((value & mask) << shift);1349}13501351#define get_opcr_online(v) get_opcr((v), 0x1, 31)1352#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24)1353#define get_opcr_channel(v) get_opcr((v), 0x3f, 16)13541355#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24)1356#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16)1357#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14)1358#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10)13591360int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel)1361{1362__be32 old_opcr, opcr[2];1363u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2);1364int attempts = 0;1365int ret;13661367ret = cmp_read(fdtv, opcr_address, opcr);1368if (ret < 0)1369return ret;13701371repeat:1372if (!get_opcr_online(*opcr)) {1373dev_err(fdtv->device, "CMP: output offline\n");1374return -EBUSY;1375}13761377old_opcr = *opcr;13781379if (get_opcr_p2p_connections(*opcr)) {1380if (get_opcr_channel(*opcr) != channel) {1381dev_err(fdtv->device, "CMP: cannot change channel\n");1382return -EBUSY;1383}1384dev_info(fdtv->device, "CMP: overlaying connection\n");13851386/* We don't allocate isochronous resources. */1387} else {1388set_opcr_channel(opcr, channel);1389set_opcr_data_rate(opcr, 2); /* S400 */13901391/* FIXME: this is for the worst case - optimize */1392set_opcr_overhead_id(opcr, 0);13931394/* FIXME: allocate isochronous channel and bandwidth at IRM */1395}13961397set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1);13981399opcr[1] = *opcr;1400opcr[0] = old_opcr;14011402ret = cmp_lock(fdtv, opcr_address, opcr);1403if (ret < 0)1404return ret;14051406if (old_opcr != *opcr) {1407/*1408* FIXME: if old_opcr.P2P_Connections > 0,1409* deallocate isochronous channel and bandwidth at IRM1410*/14111412if (++attempts < 6) /* arbitrary limit */1413goto repeat;1414return -EBUSY;1415}14161417return 0;1418}14191420void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel)1421{1422__be32 old_opcr, opcr[2];1423u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2);1424int attempts = 0;14251426if (cmp_read(fdtv, opcr_address, opcr) < 0)1427return;14281429repeat:1430if (!get_opcr_online(*opcr) || !get_opcr_p2p_connections(*opcr) ||1431get_opcr_channel(*opcr) != channel) {1432dev_err(fdtv->device, "CMP: no connection to break\n");1433return;1434}14351436old_opcr = *opcr;1437set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) - 1);14381439opcr[1] = *opcr;1440opcr[0] = old_opcr;14411442if (cmp_lock(fdtv, opcr_address, opcr) < 0)1443return;14441445if (old_opcr != *opcr) {1446/*1447* FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last1448* owner, deallocate isochronous channel and bandwidth at IRM1449* if (...)1450* fdtv->backend->dealloc_resources(fdtv, channel, bw);1451*/14521453if (++attempts < 6) /* arbitrary limit */1454goto repeat;1455}1456}145714581459