/**************************************************************** Siano Mobile Silicon, Inc. MDTV receiver kernel modules. Copyright (C) 2006-2008, Uri Shkolnik This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ****************************************************************/ #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> #include "dmxdev.h" #include "dvbdev.h" #include "dvb_demux.h" #include "dvb_frontend.h" #include "smscoreapi.h" #include "smsendian.h" #include "sms-cards.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct smsdvb_client_t { struct list_head entry; struct smscore_device_t *coredev; struct smscore_client_t *smsclient; struct dvb_adapter adapter; struct dvb_demux demux; struct dmxdev dmxdev; struct dvb_frontend frontend; fe_status_t fe_status; struct completion tune_done; /* todo: save freq/band instead whole struct */ struct dvb_frontend_parameters fe_params; struct SMSHOSTLIB_STATISTICS_DVB_S sms_stat_dvb; int event_fe_state; int event_unc_state; }; static struct list_head g_smsdvb_clients; static struct mutex g_smsdvb_clientslock; static int sms_dbg; module_param_named(debug, sms_dbg, int, 0644); MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); /* Events that may come from DVB v3 adapter */ static void sms_board_dvb3_event(struct smsdvb_client_t *client, enum SMS_DVB3_EVENTS event) { struct smscore_device_t *coredev = client->coredev; switch (event) { case DVB3_EVENT_INIT: sms_debug("DVB3_EVENT_INIT"); sms_board_event(coredev, BOARD_EVENT_BIND); break; case DVB3_EVENT_SLEEP: sms_debug("DVB3_EVENT_SLEEP"); sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND); break; case DVB3_EVENT_HOTPLUG: sms_debug("DVB3_EVENT_HOTPLUG"); sms_board_event(coredev, BOARD_EVENT_POWER_INIT); break; case DVB3_EVENT_FE_LOCK: if (client->event_fe_state != DVB3_EVENT_FE_LOCK) { client->event_fe_state = DVB3_EVENT_FE_LOCK; sms_debug("DVB3_EVENT_FE_LOCK"); sms_board_event(coredev, BOARD_EVENT_FE_LOCK); } break; case DVB3_EVENT_FE_UNLOCK: if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) { client->event_fe_state = DVB3_EVENT_FE_UNLOCK; sms_debug("DVB3_EVENT_FE_UNLOCK"); sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK); } break; case DVB3_EVENT_UNC_OK: if (client->event_unc_state != DVB3_EVENT_UNC_OK) { client->event_unc_state = DVB3_EVENT_UNC_OK; sms_debug("DVB3_EVENT_UNC_OK"); sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK); } break; case DVB3_EVENT_UNC_ERR: if (client->event_unc_state != DVB3_EVENT_UNC_ERR) { client->event_unc_state = DVB3_EVENT_UNC_ERR; sms_debug("DVB3_EVENT_UNC_ERR"); sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS); } break; default: sms_err("Unknown dvb3 api event"); break; } } static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData, struct SMSHOSTLIB_STATISTICS_ST *p) { if (sms_dbg & 2) { printk(KERN_DEBUG "Reserved = %d", p->Reserved); printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); printk(KERN_DEBUG "SNR = %d", p->SNR); printk(KERN_DEBUG "BER = %d", p->BER); printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC); printk(KERN_DEBUG "TS_PER = %d", p->TS_PER); printk(KERN_DEBUG "MFER = %d", p->MFER); printk(KERN_DEBUG "RSSI = %d", p->RSSI); printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); printk(KERN_DEBUG "Frequency = %d", p->Frequency); printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); printk(KERN_DEBUG "ModemState = %d", p->ModemState); printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); printk(KERN_DEBUG "CodeRate = %d", p->CodeRate); printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate); printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy); printk(KERN_DEBUG "Constellation = %d", p->Constellation); printk(KERN_DEBUG "BurstSize = %d", p->BurstSize); printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration); printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime); printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime); printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows); printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols); printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols); printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets); printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets); printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs); printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs); printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs); printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount); printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount); printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); printk(KERN_DEBUG "PreBER = %d", p->PreBER); printk(KERN_DEBUG "CellId = %d", p->CellId); printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); } pReceptionData->IsDemodLocked = p->IsDemodLocked; pReceptionData->SNR = p->SNR; pReceptionData->BER = p->BER; pReceptionData->BERErrorCount = p->BERErrorCount; pReceptionData->InBandPwr = p->InBandPwr; pReceptionData->ErrorTSPackets = p->ErrorTSPackets; }; static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData, struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) { int i; if (sms_dbg & 2) { printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); printk(KERN_DEBUG "SNR = %d", p->SNR); printk(KERN_DEBUG "RSSI = %d", p->RSSI); printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); printk(KERN_DEBUG "Frequency = %d", p->Frequency); printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); printk(KERN_DEBUG "ModemState = %d", p->ModemState); printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); printk(KERN_DEBUG "SystemType = %d", p->SystemType); printk(KERN_DEBUG "PartialReception = %d", p->PartialReception); printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers); printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); for (i = 0; i < 3; i++) { printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate); printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation); printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER); printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount); printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount); printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER); printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER); printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets); printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets); printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI); printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments); printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors); } } pReceptionData->IsDemodLocked = p->IsDemodLocked; pReceptionData->SNR = p->SNR; pReceptionData->InBandPwr = p->InBandPwr; pReceptionData->ErrorTSPackets = 0; pReceptionData->BER = 0; pReceptionData->BERErrorCount = 0; for (i = 0; i < 3; i++) { pReceptionData->BER += p->LayerInfo[i].BER; pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount; pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets; } } static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) { struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) (((u8 *) cb->p) + cb->offset); u32 *pMsgData = (u32 *) phdr + 1; /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/ bool is_status_update = false; smsendian_handle_rx_message((struct SmsMsgData_ST *) phdr); switch (phdr->msgType) { case MSG_SMS_DVBT_BDA_DATA: dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1), cb->size - sizeof(struct SmsMsgHdr_ST)); break; case MSG_SMS_RF_TUNE_RES: case MSG_SMS_ISDBT_TUNE_RES: complete(&client->tune_done); break; case MSG_SMS_SIGNAL_DETECTED_IND: sms_info("MSG_SMS_SIGNAL_DETECTED_IND"); client->sms_stat_dvb.TransmissionData.IsDemodLocked = true; is_status_update = true; break; case MSG_SMS_NO_SIGNAL_IND: sms_info("MSG_SMS_NO_SIGNAL_IND"); client->sms_stat_dvb.TransmissionData.IsDemodLocked = false; is_status_update = true; break; case MSG_SMS_TRANSMISSION_IND: { sms_info("MSG_SMS_TRANSMISSION_IND"); pMsgData++; memcpy(&client->sms_stat_dvb.TransmissionData, pMsgData, sizeof(struct TRANSMISSION_STATISTICS_S)); /* Mo need to correct guard interval * (as opposed to old statistics message). */ CORRECT_STAT_BANDWIDTH(client->sms_stat_dvb.TransmissionData); CORRECT_STAT_TRANSMISSON_MODE( client->sms_stat_dvb.TransmissionData); is_status_update = true; break; } case MSG_SMS_HO_PER_SLICES_IND: { struct RECEPTION_STATISTICS_S *pReceptionData = &client->sms_stat_dvb.ReceptionData; struct SRVM_SIGNAL_STATUS_S SignalStatusData; /*sms_info("MSG_SMS_HO_PER_SLICES_IND");*/ pMsgData++; SignalStatusData.result = pMsgData[0]; SignalStatusData.snr = pMsgData[1]; SignalStatusData.inBandPower = (s32) pMsgData[2]; SignalStatusData.tsPackets = pMsgData[3]; SignalStatusData.etsPackets = pMsgData[4]; SignalStatusData.constellation = pMsgData[5]; SignalStatusData.hpCode = pMsgData[6]; SignalStatusData.tpsSrvIndLP = pMsgData[7] & 0x03; SignalStatusData.tpsSrvIndHP = pMsgData[8] & 0x03; SignalStatusData.cellId = pMsgData[9] & 0xFFFF; SignalStatusData.reason = pMsgData[10]; SignalStatusData.requestId = pMsgData[11]; pReceptionData->IsRfLocked = pMsgData[16]; pReceptionData->IsDemodLocked = pMsgData[17]; pReceptionData->ModemState = pMsgData[12]; pReceptionData->SNR = pMsgData[1]; pReceptionData->BER = pMsgData[13]; pReceptionData->RSSI = pMsgData[14]; CORRECT_STAT_RSSI(client->sms_stat_dvb.ReceptionData); pReceptionData->InBandPwr = (s32) pMsgData[2]; pReceptionData->CarrierOffset = (s32) pMsgData[15]; pReceptionData->TotalTSPackets = pMsgData[3]; pReceptionData->ErrorTSPackets = pMsgData[4]; /* TS PER */ if ((SignalStatusData.tsPackets + SignalStatusData.etsPackets) > 0) { pReceptionData->TS_PER = (SignalStatusData.etsPackets * 100) / (SignalStatusData.tsPackets + SignalStatusData.etsPackets); } else { pReceptionData->TS_PER = 0; } pReceptionData->BERBitCount = pMsgData[18]; pReceptionData->BERErrorCount = pMsgData[19]; pReceptionData->MRC_SNR = pMsgData[20]; pReceptionData->MRC_InBandPwr = pMsgData[21]; pReceptionData->MRC_RSSI = pMsgData[22]; is_status_update = true; break; } case MSG_SMS_GET_STATISTICS_RES: { union { struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt; struct SmsMsgStatisticsInfo_ST dvb; } *p = (void *) (phdr + 1); struct RECEPTION_STATISTICS_S *pReceptionData = &client->sms_stat_dvb.ReceptionData; sms_info("MSG_SMS_GET_STATISTICS_RES"); is_status_update = true; switch (smscore_get_device_mode(client->coredev)) { case DEVICE_MODE_ISDBT: case DEVICE_MODE_ISDBT_BDA: smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt); break; default: smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat); } if (!pReceptionData->IsDemodLocked) { pReceptionData->SNR = 0; pReceptionData->BER = 0; pReceptionData->BERErrorCount = 0; pReceptionData->InBandPwr = 0; pReceptionData->ErrorTSPackets = 0; } complete(&client->tune_done); break; } default: sms_info("Unhandled message %d", phdr->msgType); } smscore_putbuffer(client->coredev, cb); if (is_status_update) { if (client->sms_stat_dvb.ReceptionData.IsDemodLocked) { client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); if (client->sms_stat_dvb.ReceptionData.ErrorTSPackets == 0) sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); else sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR); } else { if (client->sms_stat_dvb.ReceptionData.IsRfLocked) client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER; else client->fe_status = 0; sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); } } return 0; } static void smsdvb_unregister_client(struct smsdvb_client_t *client) { /* must be called under clientslock */ list_del(&client->entry); smscore_unregister_client(client->smsclient); dvb_unregister_frontend(&client->frontend); dvb_dmxdev_release(&client->dmxdev); dvb_dmx_release(&client->demux); dvb_unregister_adapter(&client->adapter); kfree(client); } static void smsdvb_onremove(void *context) { kmutex_lock(&g_smsdvb_clientslock); smsdvb_unregister_client((struct smsdvb_client_t *) context); kmutex_unlock(&g_smsdvb_clientslock); } static int smsdvb_start_feed(struct dvb_demux_feed *feed) { struct smsdvb_client_t *client = container_of(feed->demux, struct smsdvb_client_t, demux); struct SmsMsgData_ST PidMsg; sms_debug("add pid %d(%x)", feed->pid, feed->pid); PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; PidMsg.xMsgHeader.msgDstId = HIF_TASK; PidMsg.xMsgHeader.msgFlags = 0; PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); PidMsg.msgData[0] = feed->pid; smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); return smsclient_sendrequest(client->smsclient, &PidMsg, sizeof(PidMsg)); } static int smsdvb_stop_feed(struct dvb_demux_feed *feed) { struct smsdvb_client_t *client = container_of(feed->demux, struct smsdvb_client_t, demux); struct SmsMsgData_ST PidMsg; sms_debug("remove pid %d(%x)", feed->pid, feed->pid); PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; PidMsg.xMsgHeader.msgDstId = HIF_TASK; PidMsg.xMsgHeader.msgFlags = 0; PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); PidMsg.msgData[0] = feed->pid; smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); return smsclient_sendrequest(client->smsclient, &PidMsg, sizeof(PidMsg)); } static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, void *buffer, size_t size, struct completion *completion) { int rc; smsendian_handle_tx_message((struct SmsMsgHdr_ST *)buffer); rc = smsclient_sendrequest(client->smsclient, buffer, size); if (rc < 0) return rc; return wait_for_completion_timeout(completion, msecs_to_jiffies(2000)) ? 0 : -ETIME; } static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) { int rc; struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, DVBT_BDA_CONTROL_MSG_ID, HIF_TASK, sizeof(struct SmsMsgHdr_ST), 0 }; rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->tune_done); return rc; } static inline int led_feedback(struct smsdvb_client_t *client) { if (client->fe_status & FE_HAS_LOCK) return sms_board_led_feedback(client->coredev, (client->sms_stat_dvb.ReceptionData.BER == 0) ? SMS_LED_HI : SMS_LED_LO); else return sms_board_led_feedback(client->coredev, SMS_LED_OFF); } static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) { int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); rc = smsdvb_send_statistics_request(client); *stat = client->fe_status; led_feedback(client); return rc; } static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) { int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); rc = smsdvb_send_statistics_request(client); *ber = client->sms_stat_dvb.ReceptionData.BER; led_feedback(client); return rc; } static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); rc = smsdvb_send_statistics_request(client); if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95) *strength = 0; else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29) *strength = 100; else *strength = (client->sms_stat_dvb.ReceptionData.InBandPwr + 95) * 3 / 2; led_feedback(client); return rc; } static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) { int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); rc = smsdvb_send_statistics_request(client); *snr = client->sms_stat_dvb.ReceptionData.SNR; led_feedback(client); return rc; } static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); rc = smsdvb_send_statistics_request(client); *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; led_feedback(client); return rc; } static int smsdvb_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) { sms_debug(""); tune->min_delay_ms = 400; tune->step_size = 250000; tune->max_drift = 0; return 0; } static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); struct { struct SmsMsgHdr_ST Msg; u32 Data[3]; } Msg; int ret; client->fe_status = FE_HAS_SIGNAL; client->event_fe_state = -1; client->event_unc_state = -1; fe->dtv_property_cache.delivery_system = SYS_DVBT; Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; Msg.Msg.msgDstId = HIF_TASK; Msg.Msg.msgFlags = 0; Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; Msg.Msg.msgLength = sizeof(Msg); Msg.Data[0] = c->frequency; Msg.Data[2] = 12000000; sms_info("%s: freq %d band %d", __func__, c->frequency, c->bandwidth_hz); switch (c->bandwidth_hz / 1000000) { case 8: Msg.Data[1] = BW_8_MHZ; break; case 7: Msg.Data[1] = BW_7_MHZ; break; case 6: Msg.Data[1] = BW_6_MHZ; break; case 0: return -EOPNOTSUPP; default: return -EINVAL; } /* Disable LNA, if any. An error is returned if no LNA is present */ ret = sms_board_lna_control(client->coredev, 0); if (ret == 0) { fe_status_t status; /* tune with LNA off at first */ ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->tune_done); smsdvb_read_status(fe, &status); if (status & FE_HAS_LOCK) return ret; /* previous tune didn't lock - enable LNA and tune again */ sms_board_lna_control(client->coredev, 1); } return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->tune_done); } static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); struct { struct SmsMsgHdr_ST Msg; u32 Data[4]; } Msg; fe->dtv_property_cache.delivery_system = SYS_ISDBT; Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; Msg.Msg.msgDstId = HIF_TASK; Msg.Msg.msgFlags = 0; Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; Msg.Msg.msgLength = sizeof(Msg); if (c->isdbt_sb_segment_idx == -1) c->isdbt_sb_segment_idx = 0; switch (c->isdbt_sb_segment_count) { case 3: Msg.Data[1] = BW_ISDBT_3SEG; break; case 1: Msg.Data[1] = BW_ISDBT_1SEG; break; case 0: /* AUTO */ switch (c->bandwidth_hz / 1000000) { case 8: case 7: c->isdbt_sb_segment_count = 3; Msg.Data[1] = BW_ISDBT_3SEG; break; case 6: c->isdbt_sb_segment_count = 1; Msg.Data[1] = BW_ISDBT_1SEG; break; default: /* Assumes 6 MHZ bw */ c->isdbt_sb_segment_count = 1; c->bandwidth_hz = 6000; Msg.Data[1] = BW_ISDBT_1SEG; break; } break; default: sms_info("Segment count %d not supported", c->isdbt_sb_segment_count); return -EINVAL; } Msg.Data[0] = c->frequency; Msg.Data[2] = 12000000; Msg.Data[3] = c->isdbt_sb_segment_idx; sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, c->frequency, c->isdbt_sb_segment_count, c->isdbt_sb_segment_idx); return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->tune_done); } static int smsdvb_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); struct smscore_device_t *coredev = client->coredev; switch (smscore_get_device_mode(coredev)) { case DEVICE_MODE_DVBT: case DEVICE_MODE_DVBT_BDA: return smsdvb_dvbt_set_frontend(fe, fep); case DEVICE_MODE_ISDBT: case DEVICE_MODE_ISDBT_BDA: return smsdvb_isdbt_set_frontend(fe, fep); default: return -EINVAL; } } static int smsdvb_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); sms_debug(""); /* todo: */ memcpy(fep, &client->fe_params, sizeof(struct dvb_frontend_parameters)); return 0; } static int smsdvb_init(struct dvb_frontend *fe) { struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); sms_board_power(client->coredev, 1); sms_board_dvb3_event(client, DVB3_EVENT_INIT); return 0; } static int smsdvb_sleep(struct dvb_frontend *fe) { struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); sms_board_led_feedback(client->coredev, SMS_LED_OFF); sms_board_power(client->coredev, 0); sms_board_dvb3_event(client, DVB3_EVENT_SLEEP); return 0; } static void smsdvb_release(struct dvb_frontend *fe) { /* do nothing */ } static struct dvb_frontend_ops smsdvb_fe_ops = { .info = { .name = "Siano Mobile Digital MDTV Receiver", .type = FE_OFDM, .frequency_min = 44250000, .frequency_max = 867250000, .frequency_stepsize = 250000, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, }, .release = smsdvb_release, .set_frontend = smsdvb_set_frontend, .get_frontend = smsdvb_get_frontend, .get_tune_settings = smsdvb_get_tune_settings, .read_status = smsdvb_read_status, .read_ber = smsdvb_read_ber, .read_signal_strength = smsdvb_read_signal_strength, .read_snr = smsdvb_read_snr, .read_ucblocks = smsdvb_read_ucblocks, .init = smsdvb_init, .sleep = smsdvb_sleep, }; static int smsdvb_hotplug(struct smscore_device_t *coredev, struct device *device, int arrival) { struct smsclient_params_t params; struct smsdvb_client_t *client; int rc; /* device removal handled by onremove callback */ if (!arrival) return 0; client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL); if (!client) { sms_err("kmalloc() failed"); return -ENOMEM; } /* register dvb adapter */ rc = dvb_register_adapter(&client->adapter, sms_get_board( smscore_get_board_id(coredev))->name, THIS_MODULE, device, adapter_nr); if (rc < 0) { sms_err("dvb_register_adapter() failed %d", rc); goto adapter_error; } /* init dvb demux */ client->demux.dmx.capabilities = DMX_TS_FILTERING; client->demux.filternum = 32; /* todo: nova ??? */ client->demux.feednum = 32; client->demux.start_feed = smsdvb_start_feed; client->demux.stop_feed = smsdvb_stop_feed; rc = dvb_dmx_init(&client->demux); if (rc < 0) { sms_err("dvb_dmx_init failed %d", rc); goto dvbdmx_error; } /* init dmxdev */ client->dmxdev.filternum = 32; client->dmxdev.demux = &client->demux.dmx; client->dmxdev.capabilities = 0; rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter); if (rc < 0) { sms_err("dvb_dmxdev_init failed %d", rc); goto dmxdev_error; } /* init and register frontend */ memcpy(&client->frontend.ops, &smsdvb_fe_ops, sizeof(struct dvb_frontend_ops)); rc = dvb_register_frontend(&client->adapter, &client->frontend); if (rc < 0) { sms_err("frontend registration failed %d", rc); goto frontend_error; } params.initial_id = 1; params.data_type = MSG_SMS_DVBT_BDA_DATA; params.onresponse_handler = smsdvb_onresponse; params.onremove_handler = smsdvb_onremove; params.context = client; rc = smscore_register_client(coredev, ¶ms, &client->smsclient); if (rc < 0) { sms_err("smscore_register_client() failed %d", rc); goto client_error; } client->coredev = coredev; init_completion(&client->tune_done); kmutex_lock(&g_smsdvb_clientslock); list_add(&client->entry, &g_smsdvb_clients); kmutex_unlock(&g_smsdvb_clientslock); client->event_fe_state = -1; client->event_unc_state = -1; sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG); sms_info("success"); sms_board_setup(coredev); return 0; client_error: dvb_unregister_frontend(&client->frontend); frontend_error: dvb_dmxdev_release(&client->dmxdev); dmxdev_error: dvb_dmx_release(&client->demux); dvbdmx_error: dvb_unregister_adapter(&client->adapter); adapter_error: kfree(client); return rc; } static int __init smsdvb_module_init(void) { int rc; INIT_LIST_HEAD(&g_smsdvb_clients); kmutex_init(&g_smsdvb_clientslock); rc = smscore_register_hotplug(smsdvb_hotplug); sms_debug(""); return rc; } static void __exit smsdvb_module_exit(void) { smscore_unregister_hotplug(smsdvb_hotplug); kmutex_lock(&g_smsdvb_clientslock); while (!list_empty(&g_smsdvb_clients)) smsdvb_unregister_client( (struct smsdvb_client_t *) g_smsdvb_clients.next); kmutex_unlock(&g_smsdvb_clientslock); } module_init(smsdvb_module_init); module_exit(smsdvb_module_exit); MODULE_DESCRIPTION("SMS DVB subsystem adaptation module"); MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)"); MODULE_LICENSE("GPL");