From: Mauro Carvalho Chehab Date: Sun, 10 Mar 2013 12:04:44 +0000 (-0300) Subject: [media] siano: split debugfs code into a separate file X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=503efe5cfc9fb9f67a6659c4ab39174b442876f3;p=openwrt%2Fstaging%2Fblogic.git [media] siano: split debugfs code into a separate file To avoid mixing two different things at the same place, move the debugfs code into a separate file. Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig index 68f0f604678e..f3f5ec44e685 100644 --- a/drivers/media/common/siano/Kconfig +++ b/drivers/media/common/siano/Kconfig @@ -17,3 +17,15 @@ config SMS_SIANO_RC default y ---help--- Choose Y to select Remote Controller support for Siano driver. + +config SMS_SIANO_DEBUGFS + bool "Enable debugfs for smsdvb" + depends on SMS_SIANO_MDTV + depends on DEBUG_FS + depends on SMS_USB_DRV + ---help--- + Choose Y to enable visualizing a dump of the frontend + statistics response packets via debugfs. Currently, works + only with Siano USB devices. + + Useful only for developers. In doubt, say N. diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile index 81b1e985bea5..4c0567f106b2 100644 --- a/drivers/media/common/siano/Makefile +++ b/drivers/media/common/siano/Makefile @@ -1,4 +1,5 @@ smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o +smsdvb-objs := smsdvb-main.o obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o @@ -6,6 +7,10 @@ ifeq ($(CONFIG_SMS_SIANO_RC),y) smsmdtv-objs += smsir.o endif +ifeq ($(CONFIG_SMS_SIANO_DEBUGFS),y) + smsdvb-objs += smsdvb-debugfs.o +endif + ccflags-y += -Idrivers/media/dvb-core ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index 7925c04e3edc..53b81cbc1bda 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -184,6 +184,12 @@ struct smscore_device_t { /* Infrared (IR) */ struct ir_t ir; + /* + * Identify if device is USB or not. + * Used by smsdvb-sysfs to know the root node for debugfs + */ + bool is_usb_device; + int led_state; }; diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c new file mode 100644 index 000000000000..4d5dd471e2e1 --- /dev/null +++ b/drivers/media/common/siano/smsdvb-debugfs.c @@ -0,0 +1,503 @@ +/*********************************************************************** + * + * Copyright(c) 2013 Mauro Carvalho Chehab + * + * 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 . + * + ***********************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" + +#include "smscoreapi.h" + +#include "smsdvb.h" + +static struct dentry *smsdvb_debugfs_usb_root; + +struct smsdvb_debugfs { + struct kref refcount; + spinlock_t lock; + + char stats_data[PAGE_SIZE]; + unsigned stats_count; + bool stats_was_read; + + wait_queue_head_t stats_queue; +}; + +void smsdvb_print_dvb_stats(struct smsdvb_debugfs *debug_data, + struct SMSHOSTLIB_STATISTICS_ST *p) +{ + int n = 0; + char *buf; + + spin_lock(&debug_data->lock); + if (debug_data->stats_count) { + spin_unlock(&debug_data->lock); + return; + } + + buf = debug_data->stats_data; + + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsRfLocked = %d\n", p->IsRfLocked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsDemodLocked = %d\n", p->IsDemodLocked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsExternalLNAOn = %d\n", p->IsExternalLNAOn); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SNR = %d\n", p->SNR); + n += snprintf(&buf[n], PAGE_SIZE - n, + "BER = %d\n", p->BER); + n += snprintf(&buf[n], PAGE_SIZE - n, + "FIB_CRC = %d\n", p->FIB_CRC); + n += snprintf(&buf[n], PAGE_SIZE - n, + "TS_PER = %d\n", p->TS_PER); + n += snprintf(&buf[n], PAGE_SIZE - n, + "MFER = %d\n", p->MFER); + n += snprintf(&buf[n], PAGE_SIZE - n, + "RSSI = %d\n", p->RSSI); + n += snprintf(&buf[n], PAGE_SIZE - n, + "InBandPwr = %d\n", p->InBandPwr); + n += snprintf(&buf[n], PAGE_SIZE - n, + "CarrierOffset = %d\n", p->CarrierOffset); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ModemState = %d\n", p->ModemState); + n += snprintf(&buf[n], PAGE_SIZE - n, + "Frequency = %d\n", p->Frequency); + n += snprintf(&buf[n], PAGE_SIZE - n, + "Bandwidth = %d\n", p->Bandwidth); + n += snprintf(&buf[n], PAGE_SIZE - n, + "TransmissionMode = %d\n", p->TransmissionMode); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ModemState = %d\n", p->ModemState); + n += snprintf(&buf[n], PAGE_SIZE - n, + "GuardInterval = %d\n", p->GuardInterval); + n += snprintf(&buf[n], PAGE_SIZE - n, + "CodeRate = %d\n", p->CodeRate); + n += snprintf(&buf[n], PAGE_SIZE - n, + "LPCodeRate = %d\n", p->LPCodeRate); + n += snprintf(&buf[n], PAGE_SIZE - n, + "Hierarchy = %d\n", p->Hierarchy); + n += snprintf(&buf[n], PAGE_SIZE - n, + "Constellation = %d\n", p->Constellation); + n += snprintf(&buf[n], PAGE_SIZE - n, + "BurstSize = %d\n", p->BurstSize); + n += snprintf(&buf[n], PAGE_SIZE - n, + "BurstDuration = %d\n", p->BurstDuration); + n += snprintf(&buf[n], PAGE_SIZE - n, + "BurstCycleTime = %d\n", p->BurstCycleTime); + n += snprintf(&buf[n], PAGE_SIZE - n, + "CalculatedBurstCycleTime = %d\n", + p->CalculatedBurstCycleTime); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumOfRows = %d\n", p->NumOfRows); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumOfPaddCols = %d\n", p->NumOfPaddCols); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumOfPunctCols = %d\n", p->NumOfPunctCols); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ErrorTSPackets = %d\n", p->ErrorTSPackets); + n += snprintf(&buf[n], PAGE_SIZE - n, + "TotalTSPackets = %d\n", p->TotalTSPackets); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumOfValidMpeTlbs = %d\n", p->NumOfValidMpeTlbs); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumOfInvalidMpeTlbs = %d\n", p->NumOfInvalidMpeTlbs); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumOfCorrectedMpeTlbs = %d\n", p->NumOfCorrectedMpeTlbs); + n += snprintf(&buf[n], PAGE_SIZE - n, + "BERErrorCount = %d\n", p->BERErrorCount); + n += snprintf(&buf[n], PAGE_SIZE - n, + "BERBitCount = %d\n", p->BERBitCount); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SmsToHostTxErrors = %d\n", p->SmsToHostTxErrors); + n += snprintf(&buf[n], PAGE_SIZE - n, + "PreBER = %d\n", p->PreBER); + n += snprintf(&buf[n], PAGE_SIZE - n, + "CellId = %d\n", p->CellId); + n += snprintf(&buf[n], PAGE_SIZE - n, + "DvbhSrvIndHP = %d\n", p->DvbhSrvIndHP); + n += snprintf(&buf[n], PAGE_SIZE - n, + "DvbhSrvIndLP = %d\n", p->DvbhSrvIndLP); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumMPEReceived = %d\n", p->NumMPEReceived); + + debug_data->stats_count = n; + spin_unlock(&debug_data->lock); + wake_up(&debug_data->stats_queue); +} + +void smsdvb_print_isdb_stats(struct smsdvb_debugfs *debug_data, + struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) +{ + int i, n = 0; + char *buf; + + spin_lock(&debug_data->lock); + if (debug_data->stats_count) { + spin_unlock(&debug_data->lock); + return; + } + + buf = debug_data->stats_data; + + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsRfLocked = %d\t\t", p->IsRfLocked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsDemodLocked = %d\t", p->IsDemodLocked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsExternalLNAOn = %d\n", p->IsExternalLNAOn); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SNR = %d dB\t\t", p->SNR); + n += snprintf(&buf[n], PAGE_SIZE - n, + "RSSI = %d dBm\t\t", p->RSSI); + n += snprintf(&buf[n], PAGE_SIZE - n, + "InBandPwr = %d dBm\n", p->InBandPwr); + n += snprintf(&buf[n], PAGE_SIZE - n, + "CarrierOffset = %d\t", p->CarrierOffset); + n += snprintf(&buf[n], PAGE_SIZE - n, + "Bandwidth = %d\t\t", p->Bandwidth); + n += snprintf(&buf[n], PAGE_SIZE - n, + "Frequency = %d Hz\n", p->Frequency); + n += snprintf(&buf[n], PAGE_SIZE - n, + "TransmissionMode = %d\t", p->TransmissionMode); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ModemState = %d\t\t", p->ModemState); + n += snprintf(&buf[n], PAGE_SIZE - n, + "GuardInterval = %d\n", p->GuardInterval); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SystemType = %d\t\t", p->SystemType); + n += snprintf(&buf[n], PAGE_SIZE - n, + "PartialReception = %d\t", p->PartialReception); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumOfLayers = %d\n", p->NumOfLayers); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SmsToHostTxErrors = %d\n", p->SmsToHostTxErrors); + + for (i = 0; i < 3; i++) { + if (p->LayerInfo[i].NumberOfSegments < 1 || + p->LayerInfo[i].NumberOfSegments > 13) + continue; + + n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tCodeRate = %d\t", + p->LayerInfo[i].CodeRate); + n += snprintf(&buf[n], PAGE_SIZE - n, "Constellation = %d\n", + p->LayerInfo[i].Constellation); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tBER = %-5d\t", + p->LayerInfo[i].BER); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tBERErrorCount = %-5d\t", + p->LayerInfo[i].BERErrorCount); + n += snprintf(&buf[n], PAGE_SIZE - n, "BERBitCount = %-5d\n", + p->LayerInfo[i].BERBitCount); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tPreBER = %-5d\t", + p->LayerInfo[i].PreBER); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tTS_PER = %-5d\n", + p->LayerInfo[i].TS_PER); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tErrorTSPackets = %-5d\t", + p->LayerInfo[i].ErrorTSPackets); + n += snprintf(&buf[n], PAGE_SIZE - n, "TotalTSPackets = %-5d\t", + p->LayerInfo[i].TotalTSPackets); + n += snprintf(&buf[n], PAGE_SIZE - n, "TILdepthI = %d\n", + p->LayerInfo[i].TILdepthI); + n += snprintf(&buf[n], PAGE_SIZE - n, + "\tNumberOfSegments = %d\t", + p->LayerInfo[i].NumberOfSegments); + n += snprintf(&buf[n], PAGE_SIZE - n, "TMCCErrors = %d\n", + p->LayerInfo[i].TMCCErrors); + } + + debug_data->stats_count = n; + spin_unlock(&debug_data->lock); + wake_up(&debug_data->stats_queue); +} + +void smsdvb_print_isdb_stats_ex(struct smsdvb_debugfs *debug_data, + struct SMSHOSTLIB_STATISTICS_ISDBT_EX_ST *p) +{ + int i, n = 0; + char *buf; + + spin_lock(&debug_data->lock); + if (debug_data->stats_count) { + spin_unlock(&debug_data->lock); + return; + } + + buf = debug_data->stats_data; + + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsRfLocked = %d\t\t", p->IsRfLocked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsDemodLocked = %d\t", p->IsDemodLocked); + n += snprintf(&buf[n], PAGE_SIZE - n, + "IsExternalLNAOn = %d\n", p->IsExternalLNAOn); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SNR = %d dB\t\t", p->SNR); + n += snprintf(&buf[n], PAGE_SIZE - n, + "RSSI = %d dBm\t\t", p->RSSI); + n += snprintf(&buf[n], PAGE_SIZE - n, + "InBandPwr = %d dBm\n", p->InBandPwr); + n += snprintf(&buf[n], PAGE_SIZE - n, + "CarrierOffset = %d\t", p->CarrierOffset); + n += snprintf(&buf[n], PAGE_SIZE - n, + "Bandwidth = %d\t\t", p->Bandwidth); + n += snprintf(&buf[n], PAGE_SIZE - n, + "Frequency = %d Hz\n", p->Frequency); + n += snprintf(&buf[n], PAGE_SIZE - n, + "TransmissionMode = %d\t", p->TransmissionMode); + n += snprintf(&buf[n], PAGE_SIZE - n, + "ModemState = %d\t\t", p->ModemState); + n += snprintf(&buf[n], PAGE_SIZE - n, + "GuardInterval = %d\n", p->GuardInterval); + n += snprintf(&buf[n], PAGE_SIZE - n, + "SystemType = %d\t\t", p->SystemType); + n += snprintf(&buf[n], PAGE_SIZE - n, + "PartialReception = %d\t", p->PartialReception); + n += snprintf(&buf[n], PAGE_SIZE - n, + "NumOfLayers = %d\n", p->NumOfLayers); + n += snprintf(&buf[n], PAGE_SIZE - n, "SegmentNumber = %d\t", + p->SegmentNumber); + n += snprintf(&buf[n], PAGE_SIZE - n, "TuneBW = %d\n", + p->TuneBW); + + for (i = 0; i < 3; i++) { + if (p->LayerInfo[i].NumberOfSegments < 1 || + p->LayerInfo[i].NumberOfSegments > 13) + continue; + + n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tCodeRate = %d\t", + p->LayerInfo[i].CodeRate); + n += snprintf(&buf[n], PAGE_SIZE - n, "Constellation = %d\n", + p->LayerInfo[i].Constellation); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tBER = %-5d\t", + p->LayerInfo[i].BER); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tBERErrorCount = %-5d\t", + p->LayerInfo[i].BERErrorCount); + n += snprintf(&buf[n], PAGE_SIZE - n, "BERBitCount = %-5d\n", + p->LayerInfo[i].BERBitCount); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tPreBER = %-5d\t", + p->LayerInfo[i].PreBER); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tTS_PER = %-5d\n", + p->LayerInfo[i].TS_PER); + n += snprintf(&buf[n], PAGE_SIZE - n, "\tErrorTSPackets = %-5d\t", + p->LayerInfo[i].ErrorTSPackets); + n += snprintf(&buf[n], PAGE_SIZE - n, "TotalTSPackets = %-5d\t", + p->LayerInfo[i].TotalTSPackets); + n += snprintf(&buf[n], PAGE_SIZE - n, "TILdepthI = %d\n", + p->LayerInfo[i].TILdepthI); + n += snprintf(&buf[n], PAGE_SIZE - n, + "\tNumberOfSegments = %d\t", + p->LayerInfo[i].NumberOfSegments); + n += snprintf(&buf[n], PAGE_SIZE - n, "TMCCErrors = %d\n", + p->LayerInfo[i].TMCCErrors); + } + + + debug_data->stats_count = n; + spin_unlock(&debug_data->lock); + + wake_up(&debug_data->stats_queue); +} + +static int smsdvb_stats_open(struct inode *inode, struct file *file) +{ + struct smsdvb_client_t *client = inode->i_private; + struct smsdvb_debugfs *debug_data = client->debug_data; + + kref_get(&debug_data->refcount); + + spin_lock(&debug_data->lock); + debug_data->stats_count = 0; + debug_data->stats_was_read = false; + spin_unlock(&debug_data->lock); + + file->private_data = debug_data; + + return 0; +} + +static int smsdvb_stats_wait_read(struct smsdvb_debugfs *debug_data) +{ + int rc = 1; + + spin_lock(&debug_data->lock); + + if (debug_data->stats_was_read) + goto exit; + + rc = debug_data->stats_count; + +exit: + spin_unlock(&debug_data->lock); + return rc; +} + +static ssize_t smsdvb_stats_read(struct file *file, char __user *user_buf, + size_t nbytes, loff_t *ppos) +{ + int rc = 0; + struct smsdvb_debugfs *debug_data = file->private_data; + + rc = wait_event_interruptible(debug_data->stats_queue, + smsdvb_stats_wait_read(debug_data)); + if (rc < 0) + return rc; + + rc = simple_read_from_buffer(user_buf, nbytes, ppos, + debug_data->stats_data, + debug_data->stats_count); + spin_lock(&debug_data->lock); + debug_data->stats_was_read = true; + spin_unlock(&debug_data->lock); + + return rc; +} + +static void smsdvb_debugfs_data_release(struct kref *ref) +{ + struct smsdvb_debugfs *debug_data; + + debug_data = container_of(ref, struct smsdvb_debugfs, refcount); + kfree(debug_data); +} + +static int smsdvb_stats_release(struct inode *inode, struct file *file) +{ + struct smsdvb_debugfs *debug_data = file->private_data; + + spin_lock(&debug_data->lock); + debug_data->stats_was_read = true; + spin_unlock(&debug_data->lock); + wake_up_interruptible_sync(&debug_data->stats_queue); + + kref_put(&debug_data->refcount, smsdvb_debugfs_data_release); + file->private_data = NULL; + + return 0; +} + +static const struct file_operations debugfs_stats_ops = { + .open = smsdvb_stats_open, + .read = smsdvb_stats_read, + .release = smsdvb_stats_release, + .llseek = generic_file_llseek, +}; + +/* + * Functions used by smsdvb, in order to create the interfaces + */ + +int smsdvb_debugfs_create(struct smsdvb_client_t *client) +{ + struct smscore_device_t *coredev = client->coredev; + struct dentry *d; + struct smsdvb_debugfs *debug_data; + + if (!smsdvb_debugfs_usb_root || !coredev->is_usb_device) + return -ENODEV; + + client->debugfs = debugfs_create_dir(coredev->devpath, + smsdvb_debugfs_usb_root); + if (IS_ERR_OR_NULL(client->debugfs)) { + pr_info("Unable to create debugfs %s directory.\n", + coredev->devpath); + return -ENODEV; + } + + d = debugfs_create_file("stats", S_IRUGO | S_IWUSR, client->debugfs, + client, &debugfs_stats_ops); + if (!d) { + debugfs_remove(client->debugfs); + return -ENOMEM; + } + + debug_data = kzalloc(sizeof(*client->debug_data), GFP_KERNEL); + if (!debug_data) + return -ENOMEM; + + client->debug_data = debug_data; + client->prt_dvb_stats = smsdvb_print_dvb_stats; + client->prt_isdb_stats = smsdvb_print_isdb_stats; + client->prt_isdb_stats_ex = smsdvb_print_isdb_stats_ex; + + init_waitqueue_head(&debug_data->stats_queue); + spin_lock_init(&debug_data->lock); + kref_init(&debug_data->refcount); + + return 0; +} + +void smsdvb_debugfs_release(struct smsdvb_client_t *client) +{ + if (!client->debugfs) + return; + +printk("%s\n", __func__); + + client->prt_dvb_stats = NULL; + client->prt_isdb_stats = NULL; + client->prt_isdb_stats_ex = NULL; + + debugfs_remove_recursive(client->debugfs); + kref_put(&client->debug_data->refcount, smsdvb_debugfs_data_release); + + client->debug_data = NULL; + client->debugfs = NULL; +} + +int smsdvb_debugfs_register(void) +{ + struct dentry *d; + + /* + * FIXME: This was written to debug Siano USB devices. So, it creates + * the debugfs node under /usb. + * A similar logic would be needed for Siano sdio devices, but, in that + * case, usb_debug_root is not a good choice. + * + * Perhaps the right fix here would be to create another sysfs root + * node for sdio-based boards, but this may need some logic at sdio + * subsystem. + */ + d = debugfs_create_dir("smsdvb", usb_debug_root); + if (IS_ERR_OR_NULL(d)) { + sms_err("Couldn't create sysfs node for smsdvb"); + return PTR_ERR(d); + } else { + smsdvb_debugfs_usb_root = d; + } + return 0; +} + +void smsdvb_debugfs_unregister(void) +{ + if (smsdvb_debugfs_usb_root) + debugfs_remove_recursive(smsdvb_debugfs_usb_root); + smsdvb_debugfs_usb_root = NULL; +} diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c new file mode 100644 index 000000000000..c14f10d5d6c0 --- /dev/null +++ b/drivers/media/common/siano/smsdvb-main.c @@ -0,0 +1,1184 @@ +/**************************************************************** + +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 . + +****************************************************************/ + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" + +#include "smscoreapi.h" +#include "sms-cards.h" + +#include "smsdvb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +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))"); + + +u32 sms_to_bw_table[] = { + [BW_8_MHZ] = 8000000, + [BW_7_MHZ] = 7000000, + [BW_6_MHZ] = 6000000, + [BW_5_MHZ] = 5000000, + [BW_2_MHZ] = 2000000, + [BW_1_5_MHZ] = 1500000, + [BW_ISDBT_1SEG] = 6000000, + [BW_ISDBT_3SEG] = 6000000, + [BW_ISDBT_13SEG] = 6000000, +}; + +u32 sms_to_guard_interval_table[] = { + [0] = GUARD_INTERVAL_1_32, + [1] = GUARD_INTERVAL_1_16, + [2] = GUARD_INTERVAL_1_8, + [3] = GUARD_INTERVAL_1_4, +}; + +u32 sms_to_code_rate_table[] = { + [0] = FEC_1_2, + [1] = FEC_2_3, + [2] = FEC_3_4, + [3] = FEC_5_6, + [4] = FEC_7_8, +}; + + +u32 sms_to_hierarchy_table[] = { + [0] = HIERARCHY_NONE, + [1] = HIERARCHY_1, + [2] = HIERARCHY_2, + [3] = HIERARCHY_4, +}; + +u32 sms_to_modulation_table[] = { + [0] = QPSK, + [1] = QAM_16, + [2] = QAM_64, + [3] = DQPSK, +}; + + +/* 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_stats_not_ready(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + struct smscore_device_t *coredev = client->coredev; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int i, n_layers; + + switch (smscore_get_device_mode(coredev)) { + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + n_layers = 4; + default: + n_layers = 1; + } + + /* Fill the length of each status counter */ + + /* Only global stats */ + c->strength.len = 1; + c->cnr.len = 1; + + /* Per-layer stats */ + c->post_bit_error.len = n_layers; + c->post_bit_count.len = n_layers; + c->block_error.len = n_layers; + c->block_count.len = n_layers; + + /* Signal is always available */ + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = 0; + + /* Put all of them at FE_SCALE_NOT_AVAILABLE */ + for (i = 0; i < n_layers; i++) { + c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + } +} + +static inline int sms_to_mode(u32 mode) +{ + switch (mode) { + case 2: + return TRANSMISSION_MODE_2K; + case 4: + return TRANSMISSION_MODE_4K; + case 8: + return TRANSMISSION_MODE_8K; + } + return TRANSMISSION_MODE_AUTO; +} + +static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked) +{ + if (is_demod_locked) + return FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + + if (is_rf_locked) + return FE_HAS_SIGNAL | FE_HAS_CARRIER; + + return 0; +} + + +#define convert_from_table(value, table, defval) ({ \ + u32 __ret; \ + if (value < ARRAY_SIZE(table)) \ + __ret = table[value]; \ + else \ + __ret = defval; \ + __ret; \ +}) + +#define sms_to_bw(value) \ + convert_from_table(value, sms_to_bw_table, 0); + +#define sms_to_guard_interval(value) \ + convert_from_table(value, sms_to_guard_interval_table, \ + GUARD_INTERVAL_AUTO); + +#define sms_to_code_rate(value) \ + convert_from_table(value, sms_to_code_rate_table, \ + FEC_NONE); + +#define sms_to_hierarchy(value) \ + convert_from_table(value, sms_to_hierarchy_table, \ + FEC_NONE); + +#define sms_to_modulation(value) \ + convert_from_table(value, sms_to_modulation_table, \ + FEC_NONE); + +static void smsdvb_update_tx_params(struct smsdvb_client_t *client, + struct TRANSMISSION_STATISTICS_S *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + c->frequency = p->Frequency; + client->fe_status = sms_to_status(p->IsDemodLocked, 0); + c->bandwidth_hz = sms_to_bw(p->Bandwidth); + c->transmission_mode = sms_to_mode(p->TransmissionMode); + c->guard_interval = sms_to_guard_interval(p->GuardInterval); + c->code_rate_HP = sms_to_code_rate(p->CodeRate); + c->code_rate_LP = sms_to_code_rate(p->LPCodeRate); + c->hierarchy = sms_to_hierarchy(p->Hierarchy); + c->modulation = sms_to_modulation(p->Constellation); +} + +static void smsdvb_update_per_slices(struct smsdvb_client_t *client, + struct RECEPTION_STATISTICS_PER_SLICES_S *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + client->fe_status = sms_to_status(p->IsDemodLocked, p->IsRfLocked); + c->modulation = sms_to_modulation(p->constellation); + + /* TS PER */ + client->last_per = c->block_error.stat[0].uvalue; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += p->etsPackets; + c->block_count.stat[0].uvalue += p->etsPackets + p->tsPackets; + + /* BER */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += p->BERErrorCount; + c->post_bit_count.stat[0].uvalue += p->BERBitCount; + + /* Legacy PER/BER */ + client->legacy_per = (p->etsPackets * 65535) / + (p->tsPackets + p->etsPackets); + + /* Signal Strength, in DBm */ + c->strength.stat[0].uvalue = p->RSSI * 1000; + + /* Carrier to Noise ratio, in DB */ + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = p->snr * 1000; +} + +static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client, + struct SMSHOSTLIB_STATISTICS_ST *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (client->prt_dvb_stats) + client->prt_dvb_stats(client->debug_data, p); + + client->fe_status = sms_to_status(p->IsDemodLocked, p->IsRfLocked); + + /* Update DVB modulation parameters */ + c->frequency = p->Frequency; + client->fe_status = sms_to_status(p->IsDemodLocked, 0); + c->bandwidth_hz = sms_to_bw(p->Bandwidth); + c->transmission_mode = sms_to_mode(p->TransmissionMode); + c->guard_interval = sms_to_guard_interval(p->GuardInterval); + c->code_rate_HP = sms_to_code_rate(p->CodeRate); + c->code_rate_LP = sms_to_code_rate(p->LPCodeRate); + c->hierarchy = sms_to_hierarchy(p->Hierarchy); + c->modulation = sms_to_modulation(p->Constellation); + + /* update reception data */ + c->lna = p->IsExternalLNAOn ? 1 : 0; + + /* Carrier to Noise ratio, in DB */ + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = p->SNR * 1000; + + /* Signal Strength, in DBm */ + c->strength.stat[0].uvalue = p->RSSI * 1000; + + /* TS PER */ + client->last_per = c->block_error.stat[0].uvalue; + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_count.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += p->ErrorTSPackets; + c->block_count.stat[0].uvalue += p->TotalTSPackets; + + /* BER */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += p->BERErrorCount; + c->post_bit_count.stat[0].uvalue += p->BERBitCount; + + /* Legacy PER/BER */ + client->legacy_ber = p->BER; +}; + +static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client, + struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST *lr; + int i, n_layers; + + if (client->prt_isdb_stats) + client->prt_isdb_stats(client->debug_data, p); + + /* Update ISDB-T transmission parameters */ + c->frequency = p->Frequency; + client->fe_status = sms_to_status(p->IsDemodLocked, 0); + c->bandwidth_hz = sms_to_bw(p->Bandwidth); + c->transmission_mode = sms_to_mode(p->TransmissionMode); + c->guard_interval = sms_to_guard_interval(p->GuardInterval); + c->isdbt_partial_reception = p->PartialReception ? 1 : 0; + n_layers = p->NumOfLayers; + if (n_layers < 1) + n_layers = 1; + if (n_layers > 3) + n_layers = 3; + c->isdbt_layer_enabled = 0; + + /* update reception data */ + c->lna = p->IsExternalLNAOn ? 1 : 0; + + /* Carrier to Noise ratio, in DB */ + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = p->SNR * 1000; + + /* Signal Strength, in DBm */ + c->strength.stat[0].uvalue = p->RSSI * 1000; + + client->last_per = c->block_error.stat[0].uvalue; + + /* Clears global counters, as the code below will sum it again */ + c->block_error.stat[0].uvalue = 0; + c->block_count.stat[0].uvalue = 0; + c->post_bit_error.stat[0].uvalue = 0; + c->post_bit_count.stat[0].uvalue = 0; + + for (i = 0; i < n_layers; i++) { + lr = &p->LayerInfo[i]; + + /* Update per-layer transmission parameters */ + if (lr->NumberOfSegments > 0 && lr->NumberOfSegments < 13) { + c->isdbt_layer_enabled |= 1 << i; + c->layer[i].segment_count = lr->NumberOfSegments; + } else { + continue; + } + c->layer[i].modulation = sms_to_modulation(lr->Constellation); + + /* TS PER */ + c->block_error.stat[i].scale = FE_SCALE_COUNTER; + c->block_count.stat[i].scale = FE_SCALE_COUNTER; + c->block_error.stat[i].uvalue += lr->ErrorTSPackets; + c->block_count.stat[i].uvalue += lr->TotalTSPackets; + + /* Update global PER counter */ + c->block_error.stat[0].uvalue += lr->ErrorTSPackets; + c->block_count.stat[0].uvalue += lr->TotalTSPackets; + + /* BER */ + c->post_bit_error.stat[i].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[i].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[i].uvalue += lr->BERErrorCount; + c->post_bit_count.stat[i].uvalue += lr->BERBitCount; + + /* Update global BER counter */ + c->post_bit_error.stat[0].uvalue += lr->BERErrorCount; + c->post_bit_count.stat[0].uvalue += lr->BERBitCount; + } +} + +static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client, + struct SMSHOSTLIB_STATISTICS_ISDBT_EX_ST *p) +{ + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST *lr; + int i, n_layers; + + if (client->prt_isdb_stats_ex) + client->prt_isdb_stats_ex(client->debug_data, p); + + /* Update ISDB-T transmission parameters */ + c->frequency = p->Frequency; + client->fe_status = sms_to_status(p->IsDemodLocked, 0); + c->bandwidth_hz = sms_to_bw(p->Bandwidth); + c->transmission_mode = sms_to_mode(p->TransmissionMode); + c->guard_interval = sms_to_guard_interval(p->GuardInterval); + c->isdbt_partial_reception = p->PartialReception ? 1 : 0; + n_layers = p->NumOfLayers; + if (n_layers < 1) + n_layers = 1; + if (n_layers > 3) + n_layers = 3; + c->isdbt_layer_enabled = 0; + + /* update reception data */ + c->lna = p->IsExternalLNAOn ? 1 : 0; + + /* Carrier to Noise ratio, in DB */ + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = p->SNR * 1000; + + /* Signal Strength, in DBm */ + c->strength.stat[0].uvalue = p->RSSI * 1000; + + client->last_per = c->block_error.stat[0].uvalue; + + /* Clears global counters, as the code below will sum it again */ + c->block_error.stat[0].uvalue = 0; + c->block_count.stat[0].uvalue = 0; + c->post_bit_error.stat[0].uvalue = 0; + c->post_bit_count.stat[0].uvalue = 0; + + for (i = 0; i < n_layers; i++) { + lr = &p->LayerInfo[i]; + + /* Update per-layer transmission parameters */ + if (lr->NumberOfSegments > 0 && lr->NumberOfSegments < 13) { + c->isdbt_layer_enabled |= 1 << i; + c->layer[i].segment_count = lr->NumberOfSegments; + } else { + continue; + } + c->layer[i].modulation = sms_to_modulation(lr->Constellation); + + /* TS PER */ + c->block_error.stat[i].scale = FE_SCALE_COUNTER; + c->block_count.stat[i].scale = FE_SCALE_COUNTER; + c->block_error.stat[i].uvalue += lr->ErrorTSPackets; + c->block_count.stat[i].uvalue += lr->TotalTSPackets; + + /* Update global PER counter */ + c->block_error.stat[0].uvalue += lr->ErrorTSPackets; + c->block_count.stat[0].uvalue += lr->TotalTSPackets; + + /* BER */ + c->post_bit_error.stat[i].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[i].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[i].uvalue += lr->BERErrorCount; + c->post_bit_count.stat[i].uvalue += lr->BERBitCount; + + /* Update global BER counter */ + c->post_bit_error.stat[0].uvalue += lr->BERErrorCount; + c->post_bit_count.stat[0].uvalue += lr->BERBitCount; + } +} + +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); + void *p = phdr + 1; + struct dvb_frontend *fe = &client->frontend; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + bool is_status_update = false; + + switch (phdr->msgType) { + case MSG_SMS_DVBT_BDA_DATA: + dvb_dmx_swfilter(&client->demux, p, + 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: + client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + + is_status_update = true; + break; + + case MSG_SMS_NO_SIGNAL_IND: + client->fe_status = 0; + + is_status_update = true; + break; + + case MSG_SMS_TRANSMISSION_IND: + smsdvb_update_tx_params(client, p); + + is_status_update = true; + break; + + case MSG_SMS_HO_PER_SLICES_IND: + smsdvb_update_per_slices(client, p); + + is_status_update = true; + break; + + case MSG_SMS_GET_STATISTICS_RES: + switch (smscore_get_device_mode(client->coredev)) { + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + smsdvb_update_isdbt_stats(client, p); + break; + default: + /* Skip SmsMsgStatisticsInfo_ST:RequestResult field */ + smsdvb_update_dvb_stats(client, p + sizeof(u32)); + } + + is_status_update = true; + break; + + /* Only for ISDB-T */ + case MSG_SMS_GET_STATISTICS_EX_RES: + /* Skip SmsMsgStatisticsInfo_ST:RequestResult field? */ + smsdvb_update_isdbt_stats_ex(client, p + sizeof(u32)); + is_status_update = true; + break; + default: + sms_info("message not handled"); + } + smscore_putbuffer(client->coredev, cb); + + if (is_status_update) { + if (client->fe_status == FE_HAS_LOCK) { + sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); + if (client->last_per == c->block_error.stat[0].uvalue) + sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); + else + sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR); + } else { + smsdvb_stats_not_ready(fe); + + sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); + } + complete(&client->stats_done); + } + + return 0; +} + +static void smsdvb_unregister_client(struct smsdvb_client_t *client) +{ + /* must be called under clientslock */ + + list_del(&client->entry); + + smsdvb_debugfs_release(client); + 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; + + 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; + + 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; + + 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.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + Msg.msgDstId = HIF_TASK; + Msg.msgFlags = 0; + Msg.msgLength = sizeof(Msg); + + switch (smscore_get_device_mode(client->coredev)) { + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + /* + * Check for firmware version, to avoid breaking for old cards + */ + if (client->coredev->fw_version >= 0x800) + Msg.msgType = MSG_SMS_GET_STATISTICS_EX_REQ; + else + Msg.msgType = MSG_SMS_GET_STATISTICS_REQ; + break; + default: + Msg.msgType = MSG_SMS_GET_STATISTICS_REQ; + } + + rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->stats_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, SMS_LED_OFF); + + return sms_board_led_feedback(client->coredev, + (client->legacy_ber == 0) ? + SMS_LED_HI : SMS_LED_LO); +} + +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->legacy_ber; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + s32 power = (s32) c->strength.stat[0].uvalue; + struct smsdvb_client_t *client; + + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + if (power < -95) + *strength = 0; + else if (power > -29) + *strength = 65535; + else + *strength = (power + 95) * 65535 / 66; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + struct smsdvb_client_t *client; + + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + /* Preferred scale for SNR with legacy API: 0.1 dB */ + *snr = c->cnr.stat[0].svalue / 100; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + int rc; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct smsdvb_client_t *client; + + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + *ucblocks = c->block_error.stat[0].uvalue; + + 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 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 = 0; + 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 dtv_frontend_properties *c = &fe->dtv_property_cache; + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + int board_id = smscore_get_board_id(client->coredev); + struct sms_board *board = sms_get_board(board_id); + enum sms_device_type_st type = board->type; + int ret; + + 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; + + if (!c->isdbt_layer_enabled) + c->isdbt_layer_enabled = 7; + + Msg.Data[0] = c->frequency; + Msg.Data[1] = BW_ISDBT_1SEG; + Msg.Data[2] = 12000000; + Msg.Data[3] = c->isdbt_sb_segment_idx; + + if (c->isdbt_partial_reception) { + if ((type == SMS_PELE || type == SMS_RIO) && + c->isdbt_sb_segment_count > 3) + Msg.Data[1] = BW_ISDBT_13SEG; + else if (c->isdbt_sb_segment_count > 1) + Msg.Data[1] = BW_ISDBT_3SEG; + } else if (type == SMS_PELE || type == SMS_RIO) + Msg.Data[1] = BW_ISDBT_13SEG; + + c->bandwidth_hz = 6000000; + + sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, + c->frequency, c->isdbt_sb_segment_count, + c->isdbt_sb_segment_idx); + + /* 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_set_frontend(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + struct smscore_device_t *coredev = client->coredev; + + smsdvb_stats_not_ready(fe); + + switch (smscore_get_device_mode(coredev)) { + case DEVICE_MODE_DVBT: + case DEVICE_MODE_DVBT_BDA: + return smsdvb_dvbt_set_frontend(fe); + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + return smsdvb_isdbt_set_frontend(fe); + default: + return -EINVAL; + } +} + +/* Nothing to do here, as stats are automatically updated */ +static int smsdvb_get_frontend(struct dvb_frontend *fe) +{ + 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", + .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)); + + switch (smscore_get_device_mode(coredev)) { + case DEVICE_MODE_DVBT: + case DEVICE_MODE_DVBT_BDA: + client->frontend.ops.delsys[0] = SYS_DVBT; + break; + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + client->frontend.ops.delsys[0] = SYS_ISDBT; + break; + } + + 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); + init_completion(&client->stats_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); + + if (smsdvb_debugfs_create(client) < 0) + sms_info("failed to create debugfs node"); + + 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); + + smsdvb_debugfs_register(); + + 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); + + smsdvb_debugfs_unregister(); + + 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"); diff --git a/drivers/media/common/siano/smsdvb.c b/drivers/media/common/siano/smsdvb.c deleted file mode 100644 index aeadd8a42775..000000000000 --- a/drivers/media/common/siano/smsdvb.c +++ /dev/null @@ -1,1603 +0,0 @@ -/**************************************************************** - -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 . - -****************************************************************/ - -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" - -#include "smscoreapi.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; - struct completion stats_done; - - int last_per; - - int legacy_ber, legacy_per; - - int event_fe_state; - int event_unc_state; - - /* Stats debugfs data */ - struct dentry *debugfs; - char *stats_data; - atomic_t stats_count; - bool stats_was_read; - wait_queue_head_t stats_queue; -}; - -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))"); - -/* - * This struct is a mix of RECEPTION_STATISTICS_EX_S and SRVM_SIGNAL_STATUS_S. - * It was obtained by comparing the way it was filled by the original code - */ -struct RECEPTION_STATISTICS_PER_SLICES_S { - u32 result; - u32 snr; - s32 inBandPower; - u32 tsPackets; - u32 etsPackets; - u32 constellation; - u32 hpCode; - u32 tpsSrvIndLP; - u32 tpsSrvIndHP; - u32 cellId; - u32 reason; - u32 requestId; - u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ - - u32 BER; /* Post Viterbi BER [1E-5] */ - s32 RSSI; /* dBm */ - s32 CarrierOffset; /* Carrier Offset in bin/1024 */ - - u32 IsRfLocked; /* 0 - not locked, 1 - locked */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ - - u32 BERBitCount; /* Total number of SYNC bits. */ - u32 BERErrorCount; /* Number of erronous SYNC bits. */ - - s32 MRC_SNR; /* dB */ - s32 MRC_InBandPwr; /* In band power in dBM */ - s32 MRC_RSSI; /* dBm */ -}; - -u32 sms_to_bw_table[] = { - [BW_8_MHZ] = 8000000, - [BW_7_MHZ] = 7000000, - [BW_6_MHZ] = 6000000, - [BW_5_MHZ] = 5000000, - [BW_2_MHZ] = 2000000, - [BW_1_5_MHZ] = 1500000, - [BW_ISDBT_1SEG] = 6000000, - [BW_ISDBT_3SEG] = 6000000, - [BW_ISDBT_13SEG] = 6000000, -}; - -u32 sms_to_guard_interval_table[] = { - [0] = GUARD_INTERVAL_1_32, - [1] = GUARD_INTERVAL_1_16, - [2] = GUARD_INTERVAL_1_8, - [3] = GUARD_INTERVAL_1_4, -}; - -u32 sms_to_code_rate_table[] = { - [0] = FEC_1_2, - [1] = FEC_2_3, - [2] = FEC_3_4, - [3] = FEC_5_6, - [4] = FEC_7_8, -}; - - -u32 sms_to_hierarchy_table[] = { - [0] = HIERARCHY_NONE, - [1] = HIERARCHY_1, - [2] = HIERARCHY_2, - [3] = HIERARCHY_4, -}; - -u32 sms_to_modulation_table[] = { - [0] = QPSK, - [1] = QAM_16, - [2] = QAM_64, - [3] = DQPSK, -}; - -static struct dentry *smsdvb_debugfs; - -static void smsdvb_print_dvb_stats(struct smsdvb_client_t *client, - struct SMSHOSTLIB_STATISTICS_ST *p) -{ - int n = 0; - char *buf; - - if (!client->stats_data || atomic_read(&client->stats_count)) - return; - - buf = client->stats_data; - - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsRfLocked = %d\n", p->IsRfLocked); - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsDemodLocked = %d\n", p->IsDemodLocked); - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsExternalLNAOn = %d\n", p->IsExternalLNAOn); - n += snprintf(&buf[n], PAGE_SIZE - n, - "SNR = %d\n", p->SNR); - n += snprintf(&buf[n], PAGE_SIZE - n, - "BER = %d\n", p->BER); - n += snprintf(&buf[n], PAGE_SIZE - n, - "FIB_CRC = %d\n", p->FIB_CRC); - n += snprintf(&buf[n], PAGE_SIZE - n, - "TS_PER = %d\n", p->TS_PER); - n += snprintf(&buf[n], PAGE_SIZE - n, - "MFER = %d\n", p->MFER); - n += snprintf(&buf[n], PAGE_SIZE - n, - "RSSI = %d\n", p->RSSI); - n += snprintf(&buf[n], PAGE_SIZE - n, - "InBandPwr = %d\n", p->InBandPwr); - n += snprintf(&buf[n], PAGE_SIZE - n, - "CarrierOffset = %d\n", p->CarrierOffset); - n += snprintf(&buf[n], PAGE_SIZE - n, - "ModemState = %d\n", p->ModemState); - n += snprintf(&buf[n], PAGE_SIZE - n, - "Frequency = %d\n", p->Frequency); - n += snprintf(&buf[n], PAGE_SIZE - n, - "Bandwidth = %d\n", p->Bandwidth); - n += snprintf(&buf[n], PAGE_SIZE - n, - "TransmissionMode = %d\n", p->TransmissionMode); - n += snprintf(&buf[n], PAGE_SIZE - n, - "ModemState = %d\n", p->ModemState); - n += snprintf(&buf[n], PAGE_SIZE - n, - "GuardInterval = %d\n", p->GuardInterval); - n += snprintf(&buf[n], PAGE_SIZE - n, - "CodeRate = %d\n", p->CodeRate); - n += snprintf(&buf[n], PAGE_SIZE - n, - "LPCodeRate = %d\n", p->LPCodeRate); - n += snprintf(&buf[n], PAGE_SIZE - n, - "Hierarchy = %d\n", p->Hierarchy); - n += snprintf(&buf[n], PAGE_SIZE - n, - "Constellation = %d\n", p->Constellation); - n += snprintf(&buf[n], PAGE_SIZE - n, - "BurstSize = %d\n", p->BurstSize); - n += snprintf(&buf[n], PAGE_SIZE - n, - "BurstDuration = %d\n", p->BurstDuration); - n += snprintf(&buf[n], PAGE_SIZE - n, - "BurstCycleTime = %d\n", p->BurstCycleTime); - n += snprintf(&buf[n], PAGE_SIZE - n, - "CalculatedBurstCycleTime = %d\n", - p->CalculatedBurstCycleTime); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumOfRows = %d\n", p->NumOfRows); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumOfPaddCols = %d\n", p->NumOfPaddCols); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumOfPunctCols = %d\n", p->NumOfPunctCols); - n += snprintf(&buf[n], PAGE_SIZE - n, - "ErrorTSPackets = %d\n", p->ErrorTSPackets); - n += snprintf(&buf[n], PAGE_SIZE - n, - "TotalTSPackets = %d\n", p->TotalTSPackets); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumOfValidMpeTlbs = %d\n", p->NumOfValidMpeTlbs); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumOfInvalidMpeTlbs = %d\n", p->NumOfInvalidMpeTlbs); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumOfCorrectedMpeTlbs = %d\n", p->NumOfCorrectedMpeTlbs); - n += snprintf(&buf[n], PAGE_SIZE - n, - "BERErrorCount = %d\n", p->BERErrorCount); - n += snprintf(&buf[n], PAGE_SIZE - n, - "BERBitCount = %d\n", p->BERBitCount); - n += snprintf(&buf[n], PAGE_SIZE - n, - "SmsToHostTxErrors = %d\n", p->SmsToHostTxErrors); - n += snprintf(&buf[n], PAGE_SIZE - n, - "PreBER = %d\n", p->PreBER); - n += snprintf(&buf[n], PAGE_SIZE - n, - "CellId = %d\n", p->CellId); - n += snprintf(&buf[n], PAGE_SIZE - n, - "DvbhSrvIndHP = %d\n", p->DvbhSrvIndHP); - n += snprintf(&buf[n], PAGE_SIZE - n, - "DvbhSrvIndLP = %d\n", p->DvbhSrvIndLP); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumMPEReceived = %d\n", p->NumMPEReceived); - - atomic_set(&client->stats_count, n); - wake_up(&client->stats_queue); -} - -static void smsdvb_print_isdb_stats(struct smsdvb_client_t *client, - struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) -{ - int i, n = 0; - char *buf; - - if (!client->stats_data || atomic_read(&client->stats_count)) - return; - - buf = client->stats_data; - - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsRfLocked = %d\t\t", p->IsRfLocked); - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsDemodLocked = %d\t", p->IsDemodLocked); - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsExternalLNAOn = %d\n", p->IsExternalLNAOn); - n += snprintf(&buf[n], PAGE_SIZE - n, - "SNR = %d dB\t\t", p->SNR); - n += snprintf(&buf[n], PAGE_SIZE - n, - "RSSI = %d dBm\t\t", p->RSSI); - n += snprintf(&buf[n], PAGE_SIZE - n, - "InBandPwr = %d dBm\n", p->InBandPwr); - n += snprintf(&buf[n], PAGE_SIZE - n, - "CarrierOffset = %d\t", p->CarrierOffset); - n += snprintf(&buf[n], PAGE_SIZE - n, - "Bandwidth = %d\t\t", p->Bandwidth); - n += snprintf(&buf[n], PAGE_SIZE - n, - "Frequency = %d Hz\n", p->Frequency); - n += snprintf(&buf[n], PAGE_SIZE - n, - "TransmissionMode = %d\t", p->TransmissionMode); - n += snprintf(&buf[n], PAGE_SIZE - n, - "ModemState = %d\t\t", p->ModemState); - n += snprintf(&buf[n], PAGE_SIZE - n, - "GuardInterval = %d\n", p->GuardInterval); - n += snprintf(&buf[n], PAGE_SIZE - n, - "SystemType = %d\t\t", p->SystemType); - n += snprintf(&buf[n], PAGE_SIZE - n, - "PartialReception = %d\t", p->PartialReception); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumOfLayers = %d\n", p->NumOfLayers); - n += snprintf(&buf[n], PAGE_SIZE - n, - "SmsToHostTxErrors = %d\n", p->SmsToHostTxErrors); - - for (i = 0; i < 3; i++) { - if (p->LayerInfo[i].NumberOfSegments < 1 || - p->LayerInfo[i].NumberOfSegments > 13) - continue; - - n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tCodeRate = %d\t", - p->LayerInfo[i].CodeRate); - n += snprintf(&buf[n], PAGE_SIZE - n, "Constellation = %d\n", - p->LayerInfo[i].Constellation); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tBER = %-5d\t", - p->LayerInfo[i].BER); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tBERErrorCount = %-5d\t", - p->LayerInfo[i].BERErrorCount); - n += snprintf(&buf[n], PAGE_SIZE - n, "BERBitCount = %-5d\n", - p->LayerInfo[i].BERBitCount); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tPreBER = %-5d\t", - p->LayerInfo[i].PreBER); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tTS_PER = %-5d\n", - p->LayerInfo[i].TS_PER); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tErrorTSPackets = %-5d\t", - p->LayerInfo[i].ErrorTSPackets); - n += snprintf(&buf[n], PAGE_SIZE - n, "TotalTSPackets = %-5d\t", - p->LayerInfo[i].TotalTSPackets); - n += snprintf(&buf[n], PAGE_SIZE - n, "TILdepthI = %d\n", - p->LayerInfo[i].TILdepthI); - n += snprintf(&buf[n], PAGE_SIZE - n, - "\tNumberOfSegments = %d\t", - p->LayerInfo[i].NumberOfSegments); - n += snprintf(&buf[n], PAGE_SIZE - n, "TMCCErrors = %d\n", - p->LayerInfo[i].TMCCErrors); - } - - atomic_set(&client->stats_count, n); - wake_up(&client->stats_queue); -} - -static void -smsdvb_print_isdb_stats_ex(struct smsdvb_client_t *client, - struct SMSHOSTLIB_STATISTICS_ISDBT_EX_ST *p) -{ - int i, n = 0; - char *buf; - - if (!client->stats_data || atomic_read(&client->stats_count)) - return; - - buf = client->stats_data; - - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsRfLocked = %d\t\t", p->IsRfLocked); - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsDemodLocked = %d\t", p->IsDemodLocked); - n += snprintf(&buf[n], PAGE_SIZE - n, - "IsExternalLNAOn = %d\n", p->IsExternalLNAOn); - n += snprintf(&buf[n], PAGE_SIZE - n, - "SNR = %d dB\t\t", p->SNR); - n += snprintf(&buf[n], PAGE_SIZE - n, - "RSSI = %d dBm\t\t", p->RSSI); - n += snprintf(&buf[n], PAGE_SIZE - n, - "InBandPwr = %d dBm\n", p->InBandPwr); - n += snprintf(&buf[n], PAGE_SIZE - n, - "CarrierOffset = %d\t", p->CarrierOffset); - n += snprintf(&buf[n], PAGE_SIZE - n, - "Bandwidth = %d\t\t", p->Bandwidth); - n += snprintf(&buf[n], PAGE_SIZE - n, - "Frequency = %d Hz\n", p->Frequency); - n += snprintf(&buf[n], PAGE_SIZE - n, - "TransmissionMode = %d\t", p->TransmissionMode); - n += snprintf(&buf[n], PAGE_SIZE - n, - "ModemState = %d\t\t", p->ModemState); - n += snprintf(&buf[n], PAGE_SIZE - n, - "GuardInterval = %d\n", p->GuardInterval); - n += snprintf(&buf[n], PAGE_SIZE - n, - "SystemType = %d\t\t", p->SystemType); - n += snprintf(&buf[n], PAGE_SIZE - n, - "PartialReception = %d\t", p->PartialReception); - n += snprintf(&buf[n], PAGE_SIZE - n, - "NumOfLayers = %d\n", p->NumOfLayers); - n += snprintf(&buf[n], PAGE_SIZE - n, "SegmentNumber = %d\t", - p->SegmentNumber); - n += snprintf(&buf[n], PAGE_SIZE - n, "TuneBW = %d\n", - p->TuneBW); - - for (i = 0; i < 3; i++) { - if (p->LayerInfo[i].NumberOfSegments < 1 || - p->LayerInfo[i].NumberOfSegments > 13) - continue; - - n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tCodeRate = %d\t", - p->LayerInfo[i].CodeRate); - n += snprintf(&buf[n], PAGE_SIZE - n, "Constellation = %d\n", - p->LayerInfo[i].Constellation); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tBER = %-5d\t", - p->LayerInfo[i].BER); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tBERErrorCount = %-5d\t", - p->LayerInfo[i].BERErrorCount); - n += snprintf(&buf[n], PAGE_SIZE - n, "BERBitCount = %-5d\n", - p->LayerInfo[i].BERBitCount); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tPreBER = %-5d\t", - p->LayerInfo[i].PreBER); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tTS_PER = %-5d\n", - p->LayerInfo[i].TS_PER); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tErrorTSPackets = %-5d\t", - p->LayerInfo[i].ErrorTSPackets); - n += snprintf(&buf[n], PAGE_SIZE - n, "TotalTSPackets = %-5d\t", - p->LayerInfo[i].TotalTSPackets); - n += snprintf(&buf[n], PAGE_SIZE - n, "TILdepthI = %d\n", - p->LayerInfo[i].TILdepthI); - n += snprintf(&buf[n], PAGE_SIZE - n, - "\tNumberOfSegments = %d\t", - p->LayerInfo[i].NumberOfSegments); - n += snprintf(&buf[n], PAGE_SIZE - n, "TMCCErrors = %d\n", - p->LayerInfo[i].TMCCErrors); - } - - atomic_set(&client->stats_count, n); - wake_up(&client->stats_queue); -} - -static int smsdvb_stats_open(struct inode *inode, struct file *file) -{ - struct smsdvb_client_t *client = inode->i_private; - - atomic_set(&client->stats_count, 0); - client->stats_was_read = false; - - init_waitqueue_head(&client->stats_queue); - - client->stats_data = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (client->stats_data == NULL) - return -ENOMEM; - - file->private_data = client; - - return 0; -} - -static ssize_t smsdvb_stats_read(struct file *file, char __user *user_buf, - size_t nbytes, loff_t *ppos) -{ - struct smsdvb_client_t *client = file->private_data; - - if (!client->stats_data || client->stats_was_read) - return 0; - - wait_event_interruptible(client->stats_queue, - atomic_read(&client->stats_count)); - - return simple_read_from_buffer(user_buf, nbytes, ppos, - client->stats_data, - atomic_read(&client->stats_count)); - - client->stats_was_read = true; -} - -static int smsdvb_stats_release(struct inode *inode, struct file *file) -{ - struct smsdvb_client_t *client = file->private_data; - - kfree(client->stats_data); - client->stats_data = NULL; - - return 0; -} - -static const struct file_operations debugfs_stats_ops = { - .open = smsdvb_stats_open, - .read = smsdvb_stats_read, - .release = smsdvb_stats_release, - .llseek = generic_file_llseek, -}; - -static int create_stats_debugfs(struct smsdvb_client_t *client) -{ - struct smscore_device_t *coredev = client->coredev; - struct dentry *d; - - if (!smsdvb_debugfs) - return -ENODEV; - - client->debugfs = debugfs_create_dir(coredev->devpath, smsdvb_debugfs); - if (IS_ERR_OR_NULL(client->debugfs)) { - sms_info("Unable to create debugfs %s directory.\n", - coredev->devpath); - return -ENODEV; - } - - d = debugfs_create_file("stats", S_IRUGO | S_IWUSR, client->debugfs, - client, &debugfs_stats_ops); - if (!d) { - debugfs_remove(client->debugfs); - return -ENOMEM; - } - - return 0; -} - -static void release_stats_debugfs(struct smsdvb_client_t *client) -{ - if (!client->debugfs) - return; - - debugfs_remove_recursive(client->debugfs); - - client->debugfs = NULL; -} - -/* 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_stats_not_ready(struct dvb_frontend *fe) -{ - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - struct smscore_device_t *coredev = client->coredev; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int i, n_layers; - - switch (smscore_get_device_mode(coredev)) { - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - n_layers = 4; - default: - n_layers = 1; - } - - /* Fill the length of each status counter */ - - /* Only global stats */ - c->strength.len = 1; - c->cnr.len = 1; - - /* Per-layer stats */ - c->post_bit_error.len = n_layers; - c->post_bit_count.len = n_layers; - c->block_error.len = n_layers; - c->block_count.len = n_layers; - - /* Signal is always available */ - c->strength.stat[0].scale = FE_SCALE_RELATIVE; - c->strength.stat[0].uvalue = 0; - - /* Put all of them at FE_SCALE_NOT_AVAILABLE */ - for (i = 0; i < n_layers; i++) { - c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; - } -} - -static inline int sms_to_mode(u32 mode) -{ - switch (mode) { - case 2: - return TRANSMISSION_MODE_2K; - case 4: - return TRANSMISSION_MODE_4K; - case 8: - return TRANSMISSION_MODE_8K; - } - return TRANSMISSION_MODE_AUTO; -} - -static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked) -{ - if (is_demod_locked) - return FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | - FE_HAS_SYNC | FE_HAS_LOCK; - - if (is_rf_locked) - return FE_HAS_SIGNAL | FE_HAS_CARRIER; - - return 0; -} - - -#define convert_from_table(value, table, defval) ({ \ - u32 __ret; \ - if (value < ARRAY_SIZE(table)) \ - __ret = table[value]; \ - else \ - __ret = defval; \ - __ret; \ -}) - -#define sms_to_bw(value) \ - convert_from_table(value, sms_to_bw_table, 0); - -#define sms_to_guard_interval(value) \ - convert_from_table(value, sms_to_guard_interval_table, \ - GUARD_INTERVAL_AUTO); - -#define sms_to_code_rate(value) \ - convert_from_table(value, sms_to_code_rate_table, \ - FEC_NONE); - -#define sms_to_hierarchy(value) \ - convert_from_table(value, sms_to_hierarchy_table, \ - FEC_NONE); - -#define sms_to_modulation(value) \ - convert_from_table(value, sms_to_modulation_table, \ - FEC_NONE); - -static void smsdvb_update_tx_params(struct smsdvb_client_t *client, - struct TRANSMISSION_STATISTICS_S *p) -{ - struct dvb_frontend *fe = &client->frontend; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - - c->frequency = p->Frequency; - client->fe_status = sms_to_status(p->IsDemodLocked, 0); - c->bandwidth_hz = sms_to_bw(p->Bandwidth); - c->transmission_mode = sms_to_mode(p->TransmissionMode); - c->guard_interval = sms_to_guard_interval(p->GuardInterval); - c->code_rate_HP = sms_to_code_rate(p->CodeRate); - c->code_rate_LP = sms_to_code_rate(p->LPCodeRate); - c->hierarchy = sms_to_hierarchy(p->Hierarchy); - c->modulation = sms_to_modulation(p->Constellation); -} - -static void smsdvb_update_per_slices(struct smsdvb_client_t *client, - struct RECEPTION_STATISTICS_PER_SLICES_S *p) -{ - struct dvb_frontend *fe = &client->frontend; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - - client->fe_status = sms_to_status(p->IsDemodLocked, p->IsRfLocked); - c->modulation = sms_to_modulation(p->constellation); - - /* TS PER */ - client->last_per = c->block_error.stat[0].uvalue; - c->block_error.stat[0].scale = FE_SCALE_COUNTER; - c->block_count.stat[0].scale = FE_SCALE_COUNTER; - c->block_error.stat[0].uvalue += p->etsPackets; - c->block_count.stat[0].uvalue += p->etsPackets + p->tsPackets; - - /* BER */ - c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_error.stat[0].uvalue += p->BERErrorCount; - c->post_bit_count.stat[0].uvalue += p->BERBitCount; - - /* Legacy PER/BER */ - client->legacy_per = (p->etsPackets * 65535) / - (p->tsPackets + p->etsPackets); - - /* Signal Strength, in DBm */ - c->strength.stat[0].uvalue = p->RSSI * 1000; - - /* Carrier to Noise ratio, in DB */ - c->cnr.stat[0].scale = FE_SCALE_DECIBEL; - c->cnr.stat[0].svalue = p->snr * 1000; -} - -static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client, - struct SMSHOSTLIB_STATISTICS_ST *p) -{ - struct dvb_frontend *fe = &client->frontend; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - - smsdvb_print_dvb_stats(client, p); - - client->fe_status = sms_to_status(p->IsDemodLocked, p->IsRfLocked); - - /* Update DVB modulation parameters */ - c->frequency = p->Frequency; - client->fe_status = sms_to_status(p->IsDemodLocked, 0); - c->bandwidth_hz = sms_to_bw(p->Bandwidth); - c->transmission_mode = sms_to_mode(p->TransmissionMode); - c->guard_interval = sms_to_guard_interval(p->GuardInterval); - c->code_rate_HP = sms_to_code_rate(p->CodeRate); - c->code_rate_LP = sms_to_code_rate(p->LPCodeRate); - c->hierarchy = sms_to_hierarchy(p->Hierarchy); - c->modulation = sms_to_modulation(p->Constellation); - - /* update reception data */ - c->lna = p->IsExternalLNAOn ? 1 : 0; - - /* Carrier to Noise ratio, in DB */ - c->cnr.stat[0].scale = FE_SCALE_DECIBEL; - c->cnr.stat[0].svalue = p->SNR * 1000; - - /* Signal Strength, in DBm */ - c->strength.stat[0].uvalue = p->RSSI * 1000; - - /* TS PER */ - client->last_per = c->block_error.stat[0].uvalue; - c->block_error.stat[0].scale = FE_SCALE_COUNTER; - c->block_count.stat[0].scale = FE_SCALE_COUNTER; - c->block_error.stat[0].uvalue += p->ErrorTSPackets; - c->block_count.stat[0].uvalue += p->TotalTSPackets; - - /* BER */ - c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; - c->post_bit_error.stat[0].uvalue += p->BERErrorCount; - c->post_bit_count.stat[0].uvalue += p->BERBitCount; - - /* Legacy PER/BER */ - client->legacy_ber = p->BER; -}; - -static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client, - struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) -{ - struct dvb_frontend *fe = &client->frontend; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST *lr; - int i, n_layers; - - smsdvb_print_isdb_stats(client, p); - - /* Update ISDB-T transmission parameters */ - c->frequency = p->Frequency; - client->fe_status = sms_to_status(p->IsDemodLocked, 0); - c->bandwidth_hz = sms_to_bw(p->Bandwidth); - c->transmission_mode = sms_to_mode(p->TransmissionMode); - c->guard_interval = sms_to_guard_interval(p->GuardInterval); - c->isdbt_partial_reception = p->PartialReception ? 1 : 0; - n_layers = p->NumOfLayers; - if (n_layers < 1) - n_layers = 1; - if (n_layers > 3) - n_layers = 3; - c->isdbt_layer_enabled = 0; - - /* update reception data */ - c->lna = p->IsExternalLNAOn ? 1 : 0; - - /* Carrier to Noise ratio, in DB */ - c->cnr.stat[0].scale = FE_SCALE_DECIBEL; - c->cnr.stat[0].svalue = p->SNR * 1000; - - /* Signal Strength, in DBm */ - c->strength.stat[0].uvalue = p->RSSI * 1000; - - client->last_per = c->block_error.stat[0].uvalue; - - /* Clears global counters, as the code below will sum it again */ - c->block_error.stat[0].uvalue = 0; - c->block_count.stat[0].uvalue = 0; - c->post_bit_error.stat[0].uvalue = 0; - c->post_bit_count.stat[0].uvalue = 0; - - for (i = 0; i < n_layers; i++) { - lr = &p->LayerInfo[i]; - - /* Update per-layer transmission parameters */ - if (lr->NumberOfSegments > 0 && lr->NumberOfSegments < 13) { - c->isdbt_layer_enabled |= 1 << i; - c->layer[i].segment_count = lr->NumberOfSegments; - } else { - continue; - } - c->layer[i].modulation = sms_to_modulation(lr->Constellation); - - /* TS PER */ - c->block_error.stat[i].scale = FE_SCALE_COUNTER; - c->block_count.stat[i].scale = FE_SCALE_COUNTER; - c->block_error.stat[i].uvalue += lr->ErrorTSPackets; - c->block_count.stat[i].uvalue += lr->TotalTSPackets; - - /* Update global PER counter */ - c->block_error.stat[0].uvalue += lr->ErrorTSPackets; - c->block_count.stat[0].uvalue += lr->TotalTSPackets; - - /* BER */ - c->post_bit_error.stat[i].scale = FE_SCALE_COUNTER; - c->post_bit_count.stat[i].scale = FE_SCALE_COUNTER; - c->post_bit_error.stat[i].uvalue += lr->BERErrorCount; - c->post_bit_count.stat[i].uvalue += lr->BERBitCount; - - /* Update global BER counter */ - c->post_bit_error.stat[0].uvalue += lr->BERErrorCount; - c->post_bit_count.stat[0].uvalue += lr->BERBitCount; - } -} - -static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client, - struct SMSHOSTLIB_STATISTICS_ISDBT_EX_ST *p) -{ - struct dvb_frontend *fe = &client->frontend; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST *lr; - int i, n_layers; - - smsdvb_print_isdb_stats_ex(client, p); - - /* Update ISDB-T transmission parameters */ - c->frequency = p->Frequency; - client->fe_status = sms_to_status(p->IsDemodLocked, 0); - c->bandwidth_hz = sms_to_bw(p->Bandwidth); - c->transmission_mode = sms_to_mode(p->TransmissionMode); - c->guard_interval = sms_to_guard_interval(p->GuardInterval); - c->isdbt_partial_reception = p->PartialReception ? 1 : 0; - n_layers = p->NumOfLayers; - if (n_layers < 1) - n_layers = 1; - if (n_layers > 3) - n_layers = 3; - c->isdbt_layer_enabled = 0; - - /* update reception data */ - c->lna = p->IsExternalLNAOn ? 1 : 0; - - /* Carrier to Noise ratio, in DB */ - c->cnr.stat[0].scale = FE_SCALE_DECIBEL; - c->cnr.stat[0].svalue = p->SNR * 1000; - - /* Signal Strength, in DBm */ - c->strength.stat[0].uvalue = p->RSSI * 1000; - - client->last_per = c->block_error.stat[0].uvalue; - - /* Clears global counters, as the code below will sum it again */ - c->block_error.stat[0].uvalue = 0; - c->block_count.stat[0].uvalue = 0; - c->post_bit_error.stat[0].uvalue = 0; - c->post_bit_count.stat[0].uvalue = 0; - - for (i = 0; i < n_layers; i++) { - lr = &p->LayerInfo[i]; - - /* Update per-layer transmission parameters */ - if (lr->NumberOfSegments > 0 && lr->NumberOfSegments < 13) { - c->isdbt_layer_enabled |= 1 << i; - c->layer[i].segment_count = lr->NumberOfSegments; - } else { - continue; - } - c->layer[i].modulation = sms_to_modulation(lr->Constellation); - - /* TS PER */ - c->block_error.stat[i].scale = FE_SCALE_COUNTER; - c->block_count.stat[i].scale = FE_SCALE_COUNTER; - c->block_error.stat[i].uvalue += lr->ErrorTSPackets; - c->block_count.stat[i].uvalue += lr->TotalTSPackets; - - /* Update global PER counter */ - c->block_error.stat[0].uvalue += lr->ErrorTSPackets; - c->block_count.stat[0].uvalue += lr->TotalTSPackets; - - /* BER */ - c->post_bit_error.stat[i].scale = FE_SCALE_COUNTER; - c->post_bit_count.stat[i].scale = FE_SCALE_COUNTER; - c->post_bit_error.stat[i].uvalue += lr->BERErrorCount; - c->post_bit_count.stat[i].uvalue += lr->BERBitCount; - - /* Update global BER counter */ - c->post_bit_error.stat[0].uvalue += lr->BERErrorCount; - c->post_bit_count.stat[0].uvalue += lr->BERBitCount; - } -} - -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); - void *p = phdr + 1; - struct dvb_frontend *fe = &client->frontend; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - bool is_status_update = false; - - switch (phdr->msgType) { - case MSG_SMS_DVBT_BDA_DATA: - dvb_dmx_swfilter(&client->demux, p, - 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: - client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | - FE_HAS_LOCK; - - is_status_update = true; - break; - - case MSG_SMS_NO_SIGNAL_IND: - client->fe_status = 0; - - is_status_update = true; - break; - - case MSG_SMS_TRANSMISSION_IND: - smsdvb_update_tx_params(client, p); - - is_status_update = true; - break; - - case MSG_SMS_HO_PER_SLICES_IND: - smsdvb_update_per_slices(client, p); - - is_status_update = true; - break; - - case MSG_SMS_GET_STATISTICS_RES: - switch (smscore_get_device_mode(client->coredev)) { - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - smsdvb_update_isdbt_stats(client, p); - break; - default: - /* Skip SmsMsgStatisticsInfo_ST:RequestResult field */ - smsdvb_update_dvb_stats(client, p + sizeof(u32)); - } - - is_status_update = true; - break; - - /* Only for ISDB-T */ - case MSG_SMS_GET_STATISTICS_EX_RES: - /* Skip SmsMsgStatisticsInfo_ST:RequestResult field? */ - smsdvb_update_isdbt_stats_ex(client, p + sizeof(u32)); - is_status_update = true; - break; - default: - sms_info("message not handled"); - } - smscore_putbuffer(client->coredev, cb); - - if (is_status_update) { - if (client->fe_status == FE_HAS_LOCK) { - sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); - if (client->last_per == c->block_error.stat[0].uvalue) - sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); - else - sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR); - } else { - smsdvb_stats_not_ready(fe); - - sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); - } - complete(&client->stats_done); - } - - return 0; -} - -static void smsdvb_unregister_client(struct smsdvb_client_t *client) -{ - /* must be called under clientslock */ - - list_del(&client->entry); - - release_stats_debugfs(client); - 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; - - 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; - - 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; - - 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.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - Msg.msgDstId = HIF_TASK; - Msg.msgFlags = 0; - Msg.msgLength = sizeof(Msg); - - switch (smscore_get_device_mode(client->coredev)) { - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - /* - * Check for firmware version, to avoid breaking for old cards - */ - if (client->coredev->fw_version >= 0x800) - Msg.msgType = MSG_SMS_GET_STATISTICS_EX_REQ; - else - Msg.msgType = MSG_SMS_GET_STATISTICS_REQ; - break; - default: - Msg.msgType = MSG_SMS_GET_STATISTICS_REQ; - } - - rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->stats_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, SMS_LED_OFF); - - return sms_board_led_feedback(client->coredev, - (client->legacy_ber == 0) ? - SMS_LED_HI : SMS_LED_LO); -} - -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->legacy_ber; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int rc; - s32 power = (s32) c->strength.stat[0].uvalue; - struct smsdvb_client_t *client; - - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - if (power < -95) - *strength = 0; - else if (power > -29) - *strength = 65535; - else - *strength = (power + 95) * 65535 / 66; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int rc; - struct smsdvb_client_t *client; - - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - /* Preferred scale for SNR with legacy API: 0.1 dB */ - *snr = c->cnr.stat[0].svalue / 100; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) -{ - int rc; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct smsdvb_client_t *client; - - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - *ucblocks = c->block_error.stat[0].uvalue; - - 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 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 = 0; - 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 dtv_frontend_properties *c = &fe->dtv_property_cache; - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - int board_id = smscore_get_board_id(client->coredev); - struct sms_board *board = sms_get_board(board_id); - enum sms_device_type_st type = board->type; - int ret; - 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; - - if (!c->isdbt_layer_enabled) - c->isdbt_layer_enabled = 7; - - Msg.Data[0] = c->frequency; - Msg.Data[1] = BW_ISDBT_1SEG; - Msg.Data[2] = 12000000; - Msg.Data[3] = c->isdbt_sb_segment_idx; - - if (c->isdbt_partial_reception) { - if ((type == SMS_PELE || type == SMS_RIO) && - c->isdbt_sb_segment_count > 3) - Msg.Data[1] = BW_ISDBT_13SEG; - else if (c->isdbt_sb_segment_count > 1) - Msg.Data[1] = BW_ISDBT_3SEG; - } else if (type == SMS_PELE || type == SMS_RIO) - Msg.Data[1] = BW_ISDBT_13SEG; - - c->bandwidth_hz = 6000000; - - sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, - c->frequency, c->isdbt_sb_segment_count, - c->isdbt_sb_segment_idx); - - /* 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_set_frontend(struct dvb_frontend *fe) -{ - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - struct smscore_device_t *coredev = client->coredev; - - smsdvb_stats_not_ready(fe); - - switch (smscore_get_device_mode(coredev)) { - case DEVICE_MODE_DVBT: - case DEVICE_MODE_DVBT_BDA: - return smsdvb_dvbt_set_frontend(fe); - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - return smsdvb_isdbt_set_frontend(fe); - default: - return -EINVAL; - } -} - -/* Nothing to do here, as stats are automatically updated */ -static int smsdvb_get_frontend(struct dvb_frontend *fe) -{ - 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", - .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)); - - switch (smscore_get_device_mode(coredev)) { - case DEVICE_MODE_DVBT: - case DEVICE_MODE_DVBT_BDA: - client->frontend.ops.delsys[0] = SYS_DVBT; - break; - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - client->frontend.ops.delsys[0] = SYS_ISDBT; - break; - } - - 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); - init_completion(&client->stats_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); - - if (create_stats_debugfs(client) < 0) - sms_info("failed to create debugfs node"); - - 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; - struct dentry *d; - - INIT_LIST_HEAD(&g_smsdvb_clients); - kmutex_init(&g_smsdvb_clientslock); - - d = debugfs_create_dir("smsdvb", usb_debug_root); - if (IS_ERR_OR_NULL(d)) - sms_err("Couldn't create sysfs node for smsdvb"); - else - smsdvb_debugfs = d; - - 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); - - if (smsdvb_debugfs) - debugfs_remove_recursive(smsdvb_debugfs); - - 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"); diff --git a/drivers/media/common/siano/smsdvb.h b/drivers/media/common/siano/smsdvb.h new file mode 100644 index 000000000000..09982bcf2535 --- /dev/null +++ b/drivers/media/common/siano/smsdvb.h @@ -0,0 +1,124 @@ +/*********************************************************************** + * + * 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 . + * + ***********************************************************************/ + +struct smsdvb_debugfs; +struct smsdvb_client_t; + +typedef void (*sms_prt_dvb_stats_t)(struct smsdvb_debugfs *debug_data, + struct SMSHOSTLIB_STATISTICS_ST *p); + +typedef void (*sms_prt_isdb_stats_t)(struct smsdvb_debugfs *debug_data, + struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p); + +typedef void (*sms_prt_isdb_stats_ex_t) + (struct smsdvb_debugfs *debug_data, + struct SMSHOSTLIB_STATISTICS_ISDBT_EX_ST *p); + + +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; + struct completion stats_done; + + int last_per; + + int legacy_ber, legacy_per; + + int event_fe_state; + int event_unc_state; + + /* Stats debugfs data */ + struct dentry *debugfs; + + struct smsdvb_debugfs *debug_data; + + sms_prt_dvb_stats_t prt_dvb_stats; + sms_prt_isdb_stats_t prt_isdb_stats; + sms_prt_isdb_stats_ex_t prt_isdb_stats_ex; +}; + +/* + * This struct is a mix of RECEPTION_STATISTICS_EX_S and SRVM_SIGNAL_STATUS_S. + * It was obtained by comparing the way it was filled by the original code + */ +struct RECEPTION_STATISTICS_PER_SLICES_S { + u32 result; + u32 snr; + s32 inBandPower; + u32 tsPackets; + u32 etsPackets; + u32 constellation; + u32 hpCode; + u32 tpsSrvIndLP; + u32 tpsSrvIndHP; + u32 cellId; + u32 reason; + u32 requestId; + u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ + + u32 BER; /* Post Viterbi BER [1E-5] */ + s32 RSSI; /* dBm */ + s32 CarrierOffset; /* Carrier Offset in bin/1024 */ + + u32 IsRfLocked; /* 0 - not locked, 1 - locked */ + u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ + + u32 BERBitCount; /* Total number of SYNC bits. */ + u32 BERErrorCount; /* Number of erronous SYNC bits. */ + + s32 MRC_SNR; /* dB */ + s32 MRC_InBandPwr; /* In band power in dBM */ + s32 MRC_RSSI; /* dBm */ +}; + +/* From smsdvb-debugfs.c */ +#ifdef CONFIG_SMS_SIANO_DEBUGFS + +int smsdvb_debugfs_create(struct smsdvb_client_t *client); +void smsdvb_debugfs_release(struct smsdvb_client_t *client); +int smsdvb_debugfs_register(void); +void smsdvb_debugfs_unregister(void); + +#else + +static inline int smsdvb_debugfs_create(struct smsdvb_client_t *client) +{ + return 0; +} + +static inline void smsdvb_debugfs_release(struct smsdvb_client_t *client) {} + +static inline int smsdvb_debugfs_register(void) +{ + return 0; +}; + +static inline void smsdvb_debugfs_unregister(void) {}; + +#endif + diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index acd3d1e82e03..def5e41405a4 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -412,6 +412,8 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) smscore_set_board_id(dev->coredev, board_id); + dev->coredev->is_usb_device = true; + /* initialize urbs */ for (i = 0; i < MAX_URBS; i++) { dev->surbs[i].dev = dev;