linux/drivers/media/common/siano/smsdvb-main.c
<<
>>
Prefs
   1/****************************************************************
   2
   3Siano Mobile Silicon, Inc.
   4MDTV receiver kernel modules.
   5Copyright (C) 2006-2008, Uri Shkolnik
   6
   7This program is free software: you can redistribute it and/or modify
   8it under the terms of the GNU General Public License as published by
   9the Free Software Foundation, either version 2 of the License, or
  10(at your option) any later version.
  11
  12 This program is distributed in the hope that it will be useful,
  13but WITHOUT ANY WARRANTY; without even the implied warranty of
  14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15GNU General Public License for more details.
  16
  17You should have received a copy of the GNU General Public License
  18along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19
  20****************************************************************/
  21
  22#include <linux/module.h>
  23#include <linux/slab.h>
  24#include <linux/init.h>
  25#include <asm/div64.h>
  26
  27#include "dmxdev.h"
  28#include "dvbdev.h"
  29#include "dvb_demux.h"
  30#include "dvb_frontend.h"
  31
  32#include "smscoreapi.h"
  33#include "sms-cards.h"
  34
  35#include "smsdvb.h"
  36
  37DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
  38
  39static struct list_head g_smsdvb_clients;
  40static struct mutex g_smsdvb_clientslock;
  41
  42static int sms_dbg;
  43module_param_named(debug, sms_dbg, int, 0644);
  44MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
  45
  46
  47static u32 sms_to_guard_interval_table[] = {
  48        [0] = GUARD_INTERVAL_1_32,
  49        [1] = GUARD_INTERVAL_1_16,
  50        [2] = GUARD_INTERVAL_1_8,
  51        [3] = GUARD_INTERVAL_1_4,
  52};
  53
  54static u32 sms_to_code_rate_table[] = {
  55        [0] = FEC_1_2,
  56        [1] = FEC_2_3,
  57        [2] = FEC_3_4,
  58        [3] = FEC_5_6,
  59        [4] = FEC_7_8,
  60};
  61
  62
  63static u32 sms_to_hierarchy_table[] = {
  64        [0] = HIERARCHY_NONE,
  65        [1] = HIERARCHY_1,
  66        [2] = HIERARCHY_2,
  67        [3] = HIERARCHY_4,
  68};
  69
  70static u32 sms_to_modulation_table[] = {
  71        [0] = QPSK,
  72        [1] = QAM_16,
  73        [2] = QAM_64,
  74        [3] = DQPSK,
  75};
  76
  77
  78/* Events that may come from DVB v3 adapter */
  79static void sms_board_dvb3_event(struct smsdvb_client_t *client,
  80                enum SMS_DVB3_EVENTS event) {
  81
  82        struct smscore_device_t *coredev = client->coredev;
  83        switch (event) {
  84        case DVB3_EVENT_INIT:
  85                sms_debug("DVB3_EVENT_INIT");
  86                sms_board_event(coredev, BOARD_EVENT_BIND);
  87                break;
  88        case DVB3_EVENT_SLEEP:
  89                sms_debug("DVB3_EVENT_SLEEP");
  90                sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND);
  91                break;
  92        case DVB3_EVENT_HOTPLUG:
  93                sms_debug("DVB3_EVENT_HOTPLUG");
  94                sms_board_event(coredev, BOARD_EVENT_POWER_INIT);
  95                break;
  96        case DVB3_EVENT_FE_LOCK:
  97                if (client->event_fe_state != DVB3_EVENT_FE_LOCK) {
  98                        client->event_fe_state = DVB3_EVENT_FE_LOCK;
  99                        sms_debug("DVB3_EVENT_FE_LOCK");
 100                        sms_board_event(coredev, BOARD_EVENT_FE_LOCK);
 101                }
 102                break;
 103        case DVB3_EVENT_FE_UNLOCK:
 104                if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) {
 105                        client->event_fe_state = DVB3_EVENT_FE_UNLOCK;
 106                        sms_debug("DVB3_EVENT_FE_UNLOCK");
 107                        sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK);
 108                }
 109                break;
 110        case DVB3_EVENT_UNC_OK:
 111                if (client->event_unc_state != DVB3_EVENT_UNC_OK) {
 112                        client->event_unc_state = DVB3_EVENT_UNC_OK;
 113                        sms_debug("DVB3_EVENT_UNC_OK");
 114                        sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK);
 115                }
 116                break;
 117        case DVB3_EVENT_UNC_ERR:
 118                if (client->event_unc_state != DVB3_EVENT_UNC_ERR) {
 119                        client->event_unc_state = DVB3_EVENT_UNC_ERR;
 120                        sms_debug("DVB3_EVENT_UNC_ERR");
 121                        sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS);
 122                }
 123                break;
 124
 125        default:
 126                sms_err("Unknown dvb3 api event");
 127                break;
 128        }
 129}
 130
 131static void smsdvb_stats_not_ready(struct dvb_frontend *fe)
 132{
 133        struct smsdvb_client_t *client =
 134                container_of(fe, struct smsdvb_client_t, frontend);
 135        struct smscore_device_t *coredev = client->coredev;
 136        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 137        int i, n_layers;
 138
 139        switch (smscore_get_device_mode(coredev)) {
 140        case DEVICE_MODE_ISDBT:
 141        case DEVICE_MODE_ISDBT_BDA:
 142                n_layers = 4;
 143                break;
 144        default:
 145                n_layers = 1;
 146        }
 147
 148        /* Global stats */
 149        c->strength.len = 1;
 150        c->cnr.len = 1;
 151        c->strength.stat[0].scale = FE_SCALE_DECIBEL;
 152        c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
 153
 154        /* Per-layer stats */
 155        c->post_bit_error.len = n_layers;
 156        c->post_bit_count.len = n_layers;
 157        c->block_error.len = n_layers;
 158        c->block_count.len = n_layers;
 159
 160        /*
 161         * Put all of them at FE_SCALE_NOT_AVAILABLE. They're dynamically
 162         * changed when the stats become available.
 163         */
 164        for (i = 0; i < n_layers; i++) {
 165                c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
 166                c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
 167                c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
 168                c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
 169        }
 170}
 171
 172static inline int sms_to_mode(u32 mode)
 173{
 174        switch (mode) {
 175        case 2:
 176                return TRANSMISSION_MODE_2K;
 177        case 4:
 178                return TRANSMISSION_MODE_4K;
 179        case 8:
 180                return TRANSMISSION_MODE_8K;
 181        }
 182        return TRANSMISSION_MODE_AUTO;
 183}
 184
 185static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked)
 186{
 187        if (is_demod_locked)
 188                return FE_HAS_SIGNAL  | FE_HAS_CARRIER | FE_HAS_VITERBI |
 189                       FE_HAS_SYNC    | FE_HAS_LOCK;
 190
 191        if (is_rf_locked)
 192                return FE_HAS_SIGNAL | FE_HAS_CARRIER;
 193
 194        return 0;
 195}
 196
 197static inline u32 sms_to_bw(u32 value)
 198{
 199        return value * 1000000;
 200}
 201
 202#define convert_from_table(value, table, defval) ({                     \
 203        u32 __ret;                                                      \
 204        if (value < ARRAY_SIZE(table))                                  \
 205                __ret = table[value];                                   \
 206        else                                                            \
 207                __ret = defval;                                         \
 208        __ret;                                                          \
 209})
 210
 211#define sms_to_guard_interval(value)                                    \
 212        convert_from_table(value, sms_to_guard_interval_table,          \
 213                           GUARD_INTERVAL_AUTO);
 214
 215#define sms_to_code_rate(value)                                         \
 216        convert_from_table(value, sms_to_code_rate_table,               \
 217                           FEC_NONE);
 218
 219#define sms_to_hierarchy(value)                                         \
 220        convert_from_table(value, sms_to_hierarchy_table,               \
 221                           FEC_NONE);
 222
 223#define sms_to_modulation(value)                                        \
 224        convert_from_table(value, sms_to_modulation_table,              \
 225                           FEC_NONE);
 226
 227static void smsdvb_update_tx_params(struct smsdvb_client_t *client,
 228                                    struct sms_tx_stats *p)
 229{
 230        struct dvb_frontend *fe = &client->frontend;
 231        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 232
 233        c->frequency = p->frequency;
 234        client->fe_status = sms_to_status(p->is_demod_locked, 0);
 235        c->bandwidth_hz = sms_to_bw(p->bandwidth);
 236        c->transmission_mode = sms_to_mode(p->transmission_mode);
 237        c->guard_interval = sms_to_guard_interval(p->guard_interval);
 238        c->code_rate_HP = sms_to_code_rate(p->code_rate);
 239        c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);
 240        c->hierarchy = sms_to_hierarchy(p->hierarchy);
 241        c->modulation = sms_to_modulation(p->constellation);
 242}
 243
 244static void smsdvb_update_per_slices(struct smsdvb_client_t *client,
 245                                     struct RECEPTION_STATISTICS_PER_SLICES_S *p)
 246{
 247        struct dvb_frontend *fe = &client->frontend;
 248        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 249        u64 tmp;
 250
 251        client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
 252        c->modulation = sms_to_modulation(p->constellation);
 253
 254        /* signal Strength, in DBm */
 255        c->strength.stat[0].uvalue = p->in_band_power * 1000;
 256
 257        /* Carrier to noise ratio, in DB */
 258        c->cnr.stat[0].svalue = p->snr * 1000;
 259
 260        /* PER/BER requires demod lock */
 261        if (!p->is_demod_locked)
 262                return;
 263
 264        /* TS PER */
 265        client->last_per = c->block_error.stat[0].uvalue;
 266        c->block_error.stat[0].scale = FE_SCALE_COUNTER;
 267        c->block_count.stat[0].scale = FE_SCALE_COUNTER;
 268        c->block_error.stat[0].uvalue += p->ets_packets;
 269        c->block_count.stat[0].uvalue += p->ets_packets + p->ts_packets;
 270
 271        /* ber */
 272        c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
 273        c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
 274        c->post_bit_error.stat[0].uvalue += p->ber_error_count;
 275        c->post_bit_count.stat[0].uvalue += p->ber_bit_count;
 276
 277        /* Legacy PER/BER */
 278        tmp = p->ets_packets * 65535;
 279        if (p->ts_packets + p->ets_packets)
 280                do_div(tmp, p->ts_packets + p->ets_packets);
 281        client->legacy_per = tmp;
 282}
 283
 284static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client,
 285                                    struct sms_stats *p)
 286{
 287        struct dvb_frontend *fe = &client->frontend;
 288        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 289
 290        if (client->prt_dvb_stats)
 291                client->prt_dvb_stats(client->debug_data, p);
 292
 293        client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
 294
 295        /* Update DVB modulation parameters */
 296        c->frequency = p->frequency;
 297        client->fe_status = sms_to_status(p->is_demod_locked, 0);
 298        c->bandwidth_hz = sms_to_bw(p->bandwidth);
 299        c->transmission_mode = sms_to_mode(p->transmission_mode);
 300        c->guard_interval = sms_to_guard_interval(p->guard_interval);
 301        c->code_rate_HP = sms_to_code_rate(p->code_rate);
 302        c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);
 303        c->hierarchy = sms_to_hierarchy(p->hierarchy);
 304        c->modulation = sms_to_modulation(p->constellation);
 305
 306        /* update reception data */
 307        c->lna = p->is_external_lna_on ? 1 : 0;
 308
 309        /* Carrier to noise ratio, in DB */
 310        c->cnr.stat[0].svalue = p->SNR * 1000;
 311
 312        /* signal Strength, in DBm */
 313        c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
 314
 315        /* PER/BER requires demod lock */
 316        if (!p->is_demod_locked)
 317                return;
 318
 319        /* TS PER */
 320        client->last_per = c->block_error.stat[0].uvalue;
 321        c->block_error.stat[0].scale = FE_SCALE_COUNTER;
 322        c->block_count.stat[0].scale = FE_SCALE_COUNTER;
 323        c->block_error.stat[0].uvalue += p->error_ts_packets;
 324        c->block_count.stat[0].uvalue += p->total_ts_packets;
 325
 326        /* ber */
 327        c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
 328        c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
 329        c->post_bit_error.stat[0].uvalue += p->ber_error_count;
 330        c->post_bit_count.stat[0].uvalue += p->ber_bit_count;
 331
 332        /* Legacy PER/BER */
 333        client->legacy_ber = p->ber;
 334};
 335
 336static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client,
 337                                      struct sms_isdbt_stats *p)
 338{
 339        struct dvb_frontend *fe = &client->frontend;
 340        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 341        struct sms_isdbt_layer_stats *lr;
 342        int i, n_layers;
 343
 344        if (client->prt_isdb_stats)
 345                client->prt_isdb_stats(client->debug_data, p);
 346
 347        client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
 348
 349        /*
 350         * Firmware 2.1 seems to report only lock status and
 351         * signal strength. The signal strength indicator is at the
 352         * wrong field.
 353         */
 354        if (p->statistics_type == 0) {
 355                c->strength.stat[0].uvalue = ((s32)p->transmission_mode) * 1000;
 356                c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
 357                return;
 358        }
 359
 360        /* Update ISDB-T transmission parameters */
 361        c->frequency = p->frequency;
 362        c->bandwidth_hz = sms_to_bw(p->bandwidth);
 363        c->transmission_mode = sms_to_mode(p->transmission_mode);
 364        c->guard_interval = sms_to_guard_interval(p->guard_interval);
 365        c->isdbt_partial_reception = p->partial_reception ? 1 : 0;
 366        n_layers = p->num_of_layers;
 367        if (n_layers < 1)
 368                n_layers = 1;
 369        if (n_layers > 3)
 370                n_layers = 3;
 371        c->isdbt_layer_enabled = 0;
 372
 373        /* update reception data */
 374        c->lna = p->is_external_lna_on ? 1 : 0;
 375
 376        /* Carrier to noise ratio, in DB */
 377        c->cnr.stat[0].svalue = p->SNR * 1000;
 378
 379        /* signal Strength, in DBm */
 380        c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
 381
 382        /* PER/BER and per-layer stats require demod lock */
 383        if (!p->is_demod_locked)
 384                return;
 385
 386        client->last_per = c->block_error.stat[0].uvalue;
 387
 388        /* Clears global counters, as the code below will sum it again */
 389        c->block_error.stat[0].uvalue = 0;
 390        c->block_count.stat[0].uvalue = 0;
 391        c->block_error.stat[0].scale = FE_SCALE_COUNTER;
 392        c->block_count.stat[0].scale = FE_SCALE_COUNTER;
 393        c->post_bit_error.stat[0].uvalue = 0;
 394        c->post_bit_count.stat[0].uvalue = 0;
 395        c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
 396        c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
 397
 398        for (i = 0; i < n_layers; i++) {
 399                lr = &p->layer_info[i];
 400
 401                /* Update per-layer transmission parameters */
 402                if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {
 403                        c->isdbt_layer_enabled |= 1 << i;
 404                        c->layer[i].segment_count = lr->number_of_segments;
 405                } else {
 406                        continue;
 407                }
 408                c->layer[i].modulation = sms_to_modulation(lr->constellation);
 409
 410                /* TS PER */
 411                c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;
 412                c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;
 413                c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;
 414                c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;
 415
 416                /* Update global PER counter */
 417                c->block_error.stat[0].uvalue += lr->error_ts_packets;
 418                c->block_count.stat[0].uvalue += lr->total_ts_packets;
 419
 420                /* BER */
 421                c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;
 422                c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;
 423                c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;
 424                c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;
 425
 426                /* Update global BER counter */
 427                c->post_bit_error.stat[0].uvalue += lr->ber_error_count;
 428                c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;
 429        }
 430}
 431
 432static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client,
 433                                         struct sms_isdbt_stats_ex *p)
 434{
 435        struct dvb_frontend *fe = &client->frontend;
 436        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 437        struct sms_isdbt_layer_stats *lr;
 438        int i, n_layers;
 439
 440        if (client->prt_isdb_stats_ex)
 441                client->prt_isdb_stats_ex(client->debug_data, p);
 442
 443        /* Update ISDB-T transmission parameters */
 444        c->frequency = p->frequency;
 445        client->fe_status = sms_to_status(p->is_demod_locked, 0);
 446        c->bandwidth_hz = sms_to_bw(p->bandwidth);
 447        c->transmission_mode = sms_to_mode(p->transmission_mode);
 448        c->guard_interval = sms_to_guard_interval(p->guard_interval);
 449        c->isdbt_partial_reception = p->partial_reception ? 1 : 0;
 450        n_layers = p->num_of_layers;
 451        if (n_layers < 1)
 452                n_layers = 1;
 453        if (n_layers > 3)
 454                n_layers = 3;
 455        c->isdbt_layer_enabled = 0;
 456
 457        /* update reception data */
 458        c->lna = p->is_external_lna_on ? 1 : 0;
 459
 460        /* Carrier to noise ratio, in DB */
 461        c->cnr.stat[0].svalue = p->SNR * 1000;
 462
 463        /* signal Strength, in DBm */
 464        c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
 465
 466        /* PER/BER and per-layer stats require demod lock */
 467        if (!p->is_demod_locked)
 468                return;
 469
 470        client->last_per = c->block_error.stat[0].uvalue;
 471
 472        /* Clears global counters, as the code below will sum it again */
 473        c->block_error.stat[0].uvalue = 0;
 474        c->block_count.stat[0].uvalue = 0;
 475        c->block_error.stat[0].scale = FE_SCALE_COUNTER;
 476        c->block_count.stat[0].scale = FE_SCALE_COUNTER;
 477        c->post_bit_error.stat[0].uvalue = 0;
 478        c->post_bit_count.stat[0].uvalue = 0;
 479        c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
 480        c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
 481
 482        c->post_bit_error.len = n_layers + 1;
 483        c->post_bit_count.len = n_layers + 1;
 484        c->block_error.len = n_layers + 1;
 485        c->block_count.len = n_layers + 1;
 486        for (i = 0; i < n_layers; i++) {
 487                lr = &p->layer_info[i];
 488
 489                /* Update per-layer transmission parameters */
 490                if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {
 491                        c->isdbt_layer_enabled |= 1 << i;
 492                        c->layer[i].segment_count = lr->number_of_segments;
 493                } else {
 494                        continue;
 495                }
 496                c->layer[i].modulation = sms_to_modulation(lr->constellation);
 497
 498                /* TS PER */
 499                c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;
 500                c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;
 501                c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;
 502                c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;
 503
 504                /* Update global PER counter */
 505                c->block_error.stat[0].uvalue += lr->error_ts_packets;
 506                c->block_count.stat[0].uvalue += lr->total_ts_packets;
 507
 508                /* ber */
 509                c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;
 510                c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;
 511                c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;
 512                c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;
 513
 514                /* Update global ber counter */
 515                c->post_bit_error.stat[0].uvalue += lr->ber_error_count;
 516                c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;
 517        }
 518}
 519
 520static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
 521{
 522        struct smsdvb_client_t *client = (struct smsdvb_client_t *) context;
 523        struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) (((u8 *) cb->p)
 524                        + cb->offset);
 525        void *p = phdr + 1;
 526        struct dvb_frontend *fe = &client->frontend;
 527        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 528        bool is_status_update = false;
 529
 530        switch (phdr->msg_type) {
 531        case MSG_SMS_DVBT_BDA_DATA:
 532                /*
 533                 * Only feed data to dvb demux if are there any feed listening
 534                 * to it and if the device has tuned
 535                 */
 536                if (client->feed_users && client->has_tuned)
 537                        dvb_dmx_swfilter(&client->demux, p,
 538                                         cb->size - sizeof(struct sms_msg_hdr));
 539                break;
 540
 541        case MSG_SMS_RF_TUNE_RES:
 542        case MSG_SMS_ISDBT_TUNE_RES:
 543                complete(&client->tune_done);
 544                break;
 545
 546        case MSG_SMS_SIGNAL_DETECTED_IND:
 547                client->fe_status = FE_HAS_SIGNAL  | FE_HAS_CARRIER |
 548                                    FE_HAS_VITERBI | FE_HAS_SYNC    |
 549                                    FE_HAS_LOCK;
 550
 551                is_status_update = true;
 552                break;
 553
 554        case MSG_SMS_NO_SIGNAL_IND:
 555                client->fe_status = 0;
 556
 557                is_status_update = true;
 558                break;
 559
 560        case MSG_SMS_TRANSMISSION_IND:
 561                smsdvb_update_tx_params(client, p);
 562
 563                is_status_update = true;
 564                break;
 565
 566        case MSG_SMS_HO_PER_SLICES_IND:
 567                smsdvb_update_per_slices(client, p);
 568
 569                is_status_update = true;
 570                break;
 571
 572        case MSG_SMS_GET_STATISTICS_RES:
 573                switch (smscore_get_device_mode(client->coredev)) {
 574                case DEVICE_MODE_ISDBT:
 575                case DEVICE_MODE_ISDBT_BDA:
 576                        smsdvb_update_isdbt_stats(client, p);
 577                        break;
 578                default:
 579                        /* Skip sms_msg_statistics_info:request_result field */
 580                        smsdvb_update_dvb_stats(client, p + sizeof(u32));
 581                }
 582
 583                is_status_update = true;
 584                break;
 585
 586        /* Only for ISDB-T */
 587        case MSG_SMS_GET_STATISTICS_EX_RES:
 588                /* Skip sms_msg_statistics_info:request_result field? */
 589                smsdvb_update_isdbt_stats_ex(client, p + sizeof(u32));
 590                is_status_update = true;
 591                break;
 592        default:
 593                sms_info("message not handled");
 594        }
 595        smscore_putbuffer(client->coredev, cb);
 596
 597        if (is_status_update) {
 598                if (client->fe_status & FE_HAS_LOCK) {
 599                        sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK);
 600                        if (client->last_per == c->block_error.stat[0].uvalue)
 601                                sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK);
 602                        else
 603                                sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR);
 604                        client->has_tuned = true;
 605                } else {
 606                        smsdvb_stats_not_ready(fe);
 607                        client->has_tuned = false;
 608                        sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK);
 609                }
 610                complete(&client->stats_done);
 611        }
 612
 613        return 0;
 614}
 615
 616static void smsdvb_unregister_client(struct smsdvb_client_t *client)
 617{
 618        /* must be called under clientslock */
 619
 620        list_del(&client->entry);
 621
 622        smsdvb_debugfs_release(client);
 623        smscore_unregister_client(client->smsclient);
 624        dvb_unregister_frontend(&client->frontend);
 625        dvb_dmxdev_release(&client->dmxdev);
 626        dvb_dmx_release(&client->demux);
 627        dvb_unregister_adapter(&client->adapter);
 628        kfree(client);
 629}
 630
 631static void smsdvb_onremove(void *context)
 632{
 633        kmutex_lock(&g_smsdvb_clientslock);
 634
 635        smsdvb_unregister_client((struct smsdvb_client_t *) context);
 636
 637        kmutex_unlock(&g_smsdvb_clientslock);
 638}
 639
 640static int smsdvb_start_feed(struct dvb_demux_feed *feed)
 641{
 642        struct smsdvb_client_t *client =
 643                container_of(feed->demux, struct smsdvb_client_t, demux);
 644        struct sms_msg_data pid_msg;
 645
 646        sms_debug("add pid %d(%x)",
 647                  feed->pid, feed->pid);
 648
 649        client->feed_users++;
 650
 651        pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
 652        pid_msg.x_msg_header.msg_dst_id = HIF_TASK;
 653        pid_msg.x_msg_header.msg_flags = 0;
 654        pid_msg.x_msg_header.msg_type  = MSG_SMS_ADD_PID_FILTER_REQ;
 655        pid_msg.x_msg_header.msg_length = sizeof(pid_msg);
 656        pid_msg.msg_data[0] = feed->pid;
 657
 658        return smsclient_sendrequest(client->smsclient,
 659                                     &pid_msg, sizeof(pid_msg));
 660}
 661
 662static int smsdvb_stop_feed(struct dvb_demux_feed *feed)
 663{
 664        struct smsdvb_client_t *client =
 665                container_of(feed->demux, struct smsdvb_client_t, demux);
 666        struct sms_msg_data pid_msg;
 667
 668        sms_debug("remove pid %d(%x)",
 669                  feed->pid, feed->pid);
 670
 671        client->feed_users--;
 672
 673        pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
 674        pid_msg.x_msg_header.msg_dst_id = HIF_TASK;
 675        pid_msg.x_msg_header.msg_flags = 0;
 676        pid_msg.x_msg_header.msg_type  = MSG_SMS_REMOVE_PID_FILTER_REQ;
 677        pid_msg.x_msg_header.msg_length = sizeof(pid_msg);
 678        pid_msg.msg_data[0] = feed->pid;
 679
 680        return smsclient_sendrequest(client->smsclient,
 681                                     &pid_msg, sizeof(pid_msg));
 682}
 683
 684static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client,
 685                                        void *buffer, size_t size,
 686                                        struct completion *completion)
 687{
 688        int rc;
 689
 690        rc = smsclient_sendrequest(client->smsclient, buffer, size);
 691        if (rc < 0)
 692                return rc;
 693
 694        return wait_for_completion_timeout(completion,
 695                                           msecs_to_jiffies(2000)) ?
 696                                                0 : -ETIME;
 697}
 698
 699static int smsdvb_send_statistics_request(struct smsdvb_client_t *client)
 700{
 701        int rc;
 702        struct sms_msg_hdr msg;
 703
 704        /* Don't request stats too fast */
 705        if (client->get_stats_jiffies &&
 706           (!time_after(jiffies, client->get_stats_jiffies)))
 707                return 0;
 708        client->get_stats_jiffies = jiffies + msecs_to_jiffies(100);
 709
 710        msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
 711        msg.msg_dst_id = HIF_TASK;
 712        msg.msg_flags = 0;
 713        msg.msg_length = sizeof(msg);
 714
 715        switch (smscore_get_device_mode(client->coredev)) {
 716        case DEVICE_MODE_ISDBT:
 717        case DEVICE_MODE_ISDBT_BDA:
 718                /*
 719                * Check for firmware version, to avoid breaking for old cards
 720                */
 721                if (client->coredev->fw_version >= 0x800)
 722                        msg.msg_type = MSG_SMS_GET_STATISTICS_EX_REQ;
 723                else
 724                        msg.msg_type = MSG_SMS_GET_STATISTICS_REQ;
 725                break;
 726        default:
 727                msg.msg_type = MSG_SMS_GET_STATISTICS_REQ;
 728        }
 729
 730        rc = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
 731                                         &client->stats_done);
 732
 733        return rc;
 734}
 735
 736static inline int led_feedback(struct smsdvb_client_t *client)
 737{
 738        if (!(client->fe_status & FE_HAS_LOCK))
 739                return sms_board_led_feedback(client->coredev, SMS_LED_OFF);
 740
 741        return sms_board_led_feedback(client->coredev,
 742                                     (client->legacy_ber == 0) ?
 743                                     SMS_LED_HI : SMS_LED_LO);
 744}
 745
 746static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
 747{
 748        int rc;
 749        struct smsdvb_client_t *client;
 750        client = container_of(fe, struct smsdvb_client_t, frontend);
 751
 752        rc = smsdvb_send_statistics_request(client);
 753
 754        *stat = client->fe_status;
 755
 756        led_feedback(client);
 757
 758        return rc;
 759}
 760
 761static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
 762{
 763        int rc;
 764        struct smsdvb_client_t *client;
 765
 766        client = container_of(fe, struct smsdvb_client_t, frontend);
 767
 768        rc = smsdvb_send_statistics_request(client);
 769
 770        *ber = client->legacy_ber;
 771
 772        led_feedback(client);
 773
 774        return rc;
 775}
 776
 777static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 778{
 779        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 780        int rc;
 781        s32 power = (s32) c->strength.stat[0].uvalue;
 782        struct smsdvb_client_t *client;
 783
 784        client = container_of(fe, struct smsdvb_client_t, frontend);
 785
 786        rc = smsdvb_send_statistics_request(client);
 787
 788        if (power < -95)
 789                *strength = 0;
 790                else if (power > -29)
 791                        *strength = 65535;
 792                else
 793                        *strength = (power + 95) * 65535 / 66;
 794
 795        led_feedback(client);
 796
 797        return rc;
 798}
 799
 800static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
 801{
 802        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 803        int rc;
 804        struct smsdvb_client_t *client;
 805
 806        client = container_of(fe, struct smsdvb_client_t, frontend);
 807
 808        rc = smsdvb_send_statistics_request(client);
 809
 810        /* Preferred scale for SNR with legacy API: 0.1 dB */
 811        *snr = ((u32)c->cnr.stat[0].svalue) / 100;
 812
 813        led_feedback(client);
 814
 815        return rc;
 816}
 817
 818static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 819{
 820        int rc;
 821        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 822        struct smsdvb_client_t *client;
 823
 824        client = container_of(fe, struct smsdvb_client_t, frontend);
 825
 826        rc = smsdvb_send_statistics_request(client);
 827
 828        *ucblocks = c->block_error.stat[0].uvalue;
 829
 830        led_feedback(client);
 831
 832        return rc;
 833}
 834
 835static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
 836                                    struct dvb_frontend_tune_settings *tune)
 837{
 838        sms_debug("");
 839
 840        tune->min_delay_ms = 400;
 841        tune->step_size = 250000;
 842        tune->max_drift = 0;
 843        return 0;
 844}
 845
 846static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe)
 847{
 848        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 849        struct smsdvb_client_t *client =
 850                container_of(fe, struct smsdvb_client_t, frontend);
 851
 852        struct {
 853                struct sms_msg_hdr      msg;
 854                u32             Data[3];
 855        } msg;
 856
 857        int ret;
 858
 859        client->fe_status = 0;
 860        client->event_fe_state = -1;
 861        client->event_unc_state = -1;
 862        fe->dtv_property_cache.delivery_system = SYS_DVBT;
 863
 864        msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
 865        msg.msg.msg_dst_id = HIF_TASK;
 866        msg.msg.msg_flags = 0;
 867        msg.msg.msg_type = MSG_SMS_RF_TUNE_REQ;
 868        msg.msg.msg_length = sizeof(msg);
 869        msg.Data[0] = c->frequency;
 870        msg.Data[2] = 12000000;
 871
 872        sms_info("%s: freq %d band %d", __func__, c->frequency,
 873                 c->bandwidth_hz);
 874
 875        switch (c->bandwidth_hz / 1000000) {
 876        case 8:
 877                msg.Data[1] = BW_8_MHZ;
 878                break;
 879        case 7:
 880                msg.Data[1] = BW_7_MHZ;
 881                break;
 882        case 6:
 883                msg.Data[1] = BW_6_MHZ;
 884                break;
 885        case 0:
 886                return -EOPNOTSUPP;
 887        default:
 888                return -EINVAL;
 889        }
 890        /* Disable LNA, if any. An error is returned if no LNA is present */
 891        ret = sms_board_lna_control(client->coredev, 0);
 892        if (ret == 0) {
 893                fe_status_t status;
 894
 895                /* tune with LNA off at first */
 896                ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
 897                                                  &client->tune_done);
 898
 899                smsdvb_read_status(fe, &status);
 900
 901                if (status & FE_HAS_LOCK)
 902                        return ret;
 903
 904                /* previous tune didn't lock - enable LNA and tune again */
 905                sms_board_lna_control(client->coredev, 1);
 906        }
 907
 908        return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
 909                                           &client->tune_done);
 910}
 911
 912static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe)
 913{
 914        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 915        struct smsdvb_client_t *client =
 916                container_of(fe, struct smsdvb_client_t, frontend);
 917        int board_id = smscore_get_board_id(client->coredev);
 918        struct sms_board *board = sms_get_board(board_id);
 919        enum sms_device_type_st type = board->type;
 920        int ret;
 921
 922        struct {
 923                struct sms_msg_hdr      msg;
 924                u32             Data[4];
 925        } msg;
 926
 927        fe->dtv_property_cache.delivery_system = SYS_ISDBT;
 928
 929        msg.msg.msg_src_id  = DVBT_BDA_CONTROL_MSG_ID;
 930        msg.msg.msg_dst_id  = HIF_TASK;
 931        msg.msg.msg_flags  = 0;
 932        msg.msg.msg_type   = MSG_SMS_ISDBT_TUNE_REQ;
 933        msg.msg.msg_length = sizeof(msg);
 934
 935        if (c->isdbt_sb_segment_idx == -1)
 936                c->isdbt_sb_segment_idx = 0;
 937
 938        if (!c->isdbt_layer_enabled)
 939                c->isdbt_layer_enabled = 7;
 940
 941        msg.Data[0] = c->frequency;
 942        msg.Data[1] = BW_ISDBT_1SEG;
 943        msg.Data[2] = 12000000;
 944        msg.Data[3] = c->isdbt_sb_segment_idx;
 945
 946        if (c->isdbt_partial_reception) {
 947                if ((type == SMS_PELE || type == SMS_RIO) &&
 948                    c->isdbt_sb_segment_count > 3)
 949                        msg.Data[1] = BW_ISDBT_13SEG;
 950                else if (c->isdbt_sb_segment_count > 1)
 951                        msg.Data[1] = BW_ISDBT_3SEG;
 952        } else if (type == SMS_PELE || type == SMS_RIO)
 953                msg.Data[1] = BW_ISDBT_13SEG;
 954
 955        c->bandwidth_hz = 6000000;
 956
 957        sms_info("%s: freq %d segwidth %d segindex %d", __func__,
 958                 c->frequency, c->isdbt_sb_segment_count,
 959                 c->isdbt_sb_segment_idx);
 960
 961        /* Disable LNA, if any. An error is returned if no LNA is present */
 962        ret = sms_board_lna_control(client->coredev, 0);
 963        if (ret == 0) {
 964                fe_status_t status;
 965
 966                /* tune with LNA off at first */
 967                ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
 968                                                  &client->tune_done);
 969
 970                smsdvb_read_status(fe, &status);
 971
 972                if (status & FE_HAS_LOCK)
 973                        return ret;
 974
 975                /* previous tune didn't lock - enable LNA and tune again */
 976                sms_board_lna_control(client->coredev, 1);
 977        }
 978        return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
 979                                           &client->tune_done);
 980}
 981
 982static int smsdvb_set_frontend(struct dvb_frontend *fe)
 983{
 984        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 985        struct smsdvb_client_t *client =
 986                container_of(fe, struct smsdvb_client_t, frontend);
 987        struct smscore_device_t *coredev = client->coredev;
 988
 989        smsdvb_stats_not_ready(fe);
 990        c->strength.stat[0].uvalue = 0;
 991        c->cnr.stat[0].uvalue = 0;
 992
 993        client->has_tuned = false;
 994
 995        switch (smscore_get_device_mode(coredev)) {
 996        case DEVICE_MODE_DVBT:
 997        case DEVICE_MODE_DVBT_BDA:
 998                return smsdvb_dvbt_set_frontend(fe);
 999        case DEVICE_MODE_ISDBT:
1000        case DEVICE_MODE_ISDBT_BDA:
1001                return smsdvb_isdbt_set_frontend(fe);
1002        default:
1003                return -EINVAL;
1004        }
1005}
1006
1007/* Nothing to do here, as stats are automatically updated */
1008static int smsdvb_get_frontend(struct dvb_frontend *fe)
1009{
1010        return 0;
1011}
1012
1013static int smsdvb_init(struct dvb_frontend *fe)
1014{
1015        struct smsdvb_client_t *client =
1016                container_of(fe, struct smsdvb_client_t, frontend);
1017
1018        sms_board_power(client->coredev, 1);
1019
1020        sms_board_dvb3_event(client, DVB3_EVENT_INIT);
1021        return 0;
1022}
1023
1024static int smsdvb_sleep(struct dvb_frontend *fe)
1025{
1026        struct smsdvb_client_t *client =
1027                container_of(fe, struct smsdvb_client_t, frontend);
1028
1029        sms_board_led_feedback(client->coredev, SMS_LED_OFF);
1030        sms_board_power(client->coredev, 0);
1031
1032        sms_board_dvb3_event(client, DVB3_EVENT_SLEEP);
1033
1034        return 0;
1035}
1036
1037static void smsdvb_release(struct dvb_frontend *fe)
1038{
1039        /* do nothing */
1040}
1041
1042static struct dvb_frontend_ops smsdvb_fe_ops = {
1043        .info = {
1044                .name                   = "Siano Mobile Digital MDTV Receiver",
1045                .frequency_min          = 44250000,
1046                .frequency_max          = 867250000,
1047                .frequency_stepsize     = 250000,
1048                .caps = FE_CAN_INVERSION_AUTO |
1049                        FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
1050                        FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
1051                        FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
1052                        FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
1053                        FE_CAN_GUARD_INTERVAL_AUTO |
1054                        FE_CAN_RECOVER |
1055                        FE_CAN_HIERARCHY_AUTO,
1056        },
1057
1058        .release = smsdvb_release,
1059
1060        .set_frontend = smsdvb_set_frontend,
1061        .get_frontend = smsdvb_get_frontend,
1062        .get_tune_settings = smsdvb_get_tune_settings,
1063
1064        .read_status = smsdvb_read_status,
1065        .read_ber = smsdvb_read_ber,
1066        .read_signal_strength = smsdvb_read_signal_strength,
1067        .read_snr = smsdvb_read_snr,
1068        .read_ucblocks = smsdvb_read_ucblocks,
1069
1070        .init = smsdvb_init,
1071        .sleep = smsdvb_sleep,
1072};
1073
1074static int smsdvb_hotplug(struct smscore_device_t *coredev,
1075                          struct device *device, int arrival)
1076{
1077        struct smsclient_params_t params;
1078        struct smsdvb_client_t *client;
1079        int rc;
1080
1081        /* device removal handled by onremove callback */
1082        if (!arrival)
1083                return 0;
1084        client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL);
1085        if (!client) {
1086                sms_err("kmalloc() failed");
1087                return -ENOMEM;
1088        }
1089
1090        /* register dvb adapter */
1091        rc = dvb_register_adapter(&client->adapter,
1092                                  sms_get_board(
1093                                        smscore_get_board_id(coredev))->name,
1094                                  THIS_MODULE, device, adapter_nr);
1095        if (rc < 0) {
1096                sms_err("dvb_register_adapter() failed %d", rc);
1097                goto adapter_error;
1098        }
1099
1100        /* init dvb demux */
1101        client->demux.dmx.capabilities = DMX_TS_FILTERING;
1102        client->demux.filternum = 32; /* todo: nova ??? */
1103        client->demux.feednum = 32;
1104        client->demux.start_feed = smsdvb_start_feed;
1105        client->demux.stop_feed = smsdvb_stop_feed;
1106
1107        rc = dvb_dmx_init(&client->demux);
1108        if (rc < 0) {
1109                sms_err("dvb_dmx_init failed %d", rc);
1110                goto dvbdmx_error;
1111        }
1112
1113        /* init dmxdev */
1114        client->dmxdev.filternum = 32;
1115        client->dmxdev.demux = &client->demux.dmx;
1116        client->dmxdev.capabilities = 0;
1117
1118        rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);
1119        if (rc < 0) {
1120                sms_err("dvb_dmxdev_init failed %d", rc);
1121                goto dmxdev_error;
1122        }
1123
1124        /* init and register frontend */
1125        memcpy(&client->frontend.ops, &smsdvb_fe_ops,
1126               sizeof(struct dvb_frontend_ops));
1127
1128        switch (smscore_get_device_mode(coredev)) {
1129        case DEVICE_MODE_DVBT:
1130        case DEVICE_MODE_DVBT_BDA:
1131                client->frontend.ops.delsys[0] = SYS_DVBT;
1132                break;
1133        case DEVICE_MODE_ISDBT:
1134        case DEVICE_MODE_ISDBT_BDA:
1135                client->frontend.ops.delsys[0] = SYS_ISDBT;
1136                break;
1137        }
1138
1139        rc = dvb_register_frontend(&client->adapter, &client->frontend);
1140        if (rc < 0) {
1141                sms_err("frontend registration failed %d", rc);
1142                goto frontend_error;
1143        }
1144
1145        params.initial_id = 1;
1146        params.data_type = MSG_SMS_DVBT_BDA_DATA;
1147        params.onresponse_handler = smsdvb_onresponse;
1148        params.onremove_handler = smsdvb_onremove;
1149        params.context = client;
1150
1151        rc = smscore_register_client(coredev, &params, &client->smsclient);
1152        if (rc < 0) {
1153                sms_err("smscore_register_client() failed %d", rc);
1154                goto client_error;
1155        }
1156
1157        client->coredev = coredev;
1158
1159        init_completion(&client->tune_done);
1160        init_completion(&client->stats_done);
1161
1162        kmutex_lock(&g_smsdvb_clientslock);
1163
1164        list_add(&client->entry, &g_smsdvb_clients);
1165
1166        kmutex_unlock(&g_smsdvb_clientslock);
1167
1168        client->event_fe_state = -1;
1169        client->event_unc_state = -1;
1170        sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG);
1171
1172        sms_info("success");
1173        sms_board_setup(coredev);
1174
1175        if (smsdvb_debugfs_create(client) < 0)
1176                sms_info("failed to create debugfs node");
1177
1178        return 0;
1179
1180client_error:
1181        dvb_unregister_frontend(&client->frontend);
1182
1183frontend_error:
1184        dvb_dmxdev_release(&client->dmxdev);
1185
1186dmxdev_error:
1187        dvb_dmx_release(&client->demux);
1188
1189dvbdmx_error:
1190        dvb_unregister_adapter(&client->adapter);
1191
1192adapter_error:
1193        kfree(client);
1194        return rc;
1195}
1196
1197static int __init smsdvb_module_init(void)
1198{
1199        int rc;
1200
1201        INIT_LIST_HEAD(&g_smsdvb_clients);
1202        kmutex_init(&g_smsdvb_clientslock);
1203
1204        smsdvb_debugfs_register();
1205
1206        rc = smscore_register_hotplug(smsdvb_hotplug);
1207
1208        sms_debug("");
1209
1210        return rc;
1211}
1212
1213static void __exit smsdvb_module_exit(void)
1214{
1215        smscore_unregister_hotplug(smsdvb_hotplug);
1216
1217        kmutex_lock(&g_smsdvb_clientslock);
1218
1219        while (!list_empty(&g_smsdvb_clients))
1220                smsdvb_unregister_client((struct smsdvb_client_t *)g_smsdvb_clients.next);
1221
1222        smsdvb_debugfs_unregister();
1223
1224        kmutex_unlock(&g_smsdvb_clientslock);
1225}
1226
1227module_init(smsdvb_module_init);
1228module_exit(smsdvb_module_exit);
1229
1230MODULE_DESCRIPTION("SMS DVB subsystem adaptation module");
1231MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)");
1232MODULE_LICENSE("GPL");
1233