linux/drivers/media/common/siano/smsdvb-main.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/****************************************************************
   3
   4Siano Mobile Silicon, Inc.
   5MDTV receiver kernel modules.
   6Copyright (C) 2006-2008, Uri Shkolnik
   7
   8
   9****************************************************************/
  10
  11#include "smscoreapi.h"
  12
  13#include <linux/module.h>
  14#include <linux/slab.h>
  15#include <linux/init.h>
  16#include <asm/div64.h>
  17
  18#include <media/dmxdev.h>
  19#include <media/dvbdev.h>
  20#include <media/dvb_demux.h>
  21#include <media/dvb_frontend.h>
  22
  23#include "sms-cards.h"
  24
  25#include "smsdvb.h"
  26
  27DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
  28
  29static struct list_head g_smsdvb_clients;
  30static struct mutex g_smsdvb_clientslock;
  31
  32static u32 sms_to_guard_interval_table[] = {
  33        [0] = GUARD_INTERVAL_1_32,
  34        [1] = GUARD_INTERVAL_1_16,
  35        [2] = GUARD_INTERVAL_1_8,
  36        [3] = GUARD_INTERVAL_1_4,
  37};
  38
  39static u32 sms_to_code_rate_table[] = {
  40        [0] = FEC_1_2,
  41        [1] = FEC_2_3,
  42        [2] = FEC_3_4,
  43        [3] = FEC_5_6,
  44        [4] = FEC_7_8,
  45};
  46
  47
  48static u32 sms_to_hierarchy_table[] = {
  49        [0] = HIERARCHY_NONE,
  50        [1] = HIERARCHY_1,
  51        [2] = HIERARCHY_2,
  52        [3] = HIERARCHY_4,
  53};
  54
  55static u32 sms_to_modulation_table[] = {
  56        [0] = QPSK,
  57        [1] = QAM_16,
  58        [2] = QAM_64,
  59        [3] = DQPSK,
  60};
  61
  62
  63/* Events that may come from DVB v3 adapter */
  64static void sms_board_dvb3_event(struct smsdvb_client_t *client,
  65                enum SMS_DVB3_EVENTS event) {
  66
  67        struct smscore_device_t *coredev = client->coredev;
  68        switch (event) {
  69        case DVB3_EVENT_INIT:
  70                pr_debug("DVB3_EVENT_INIT\n");
  71                sms_board_event(coredev, BOARD_EVENT_BIND);
  72                break;
  73        case DVB3_EVENT_SLEEP:
  74                pr_debug("DVB3_EVENT_SLEEP\n");
  75                sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND);
  76                break;
  77        case DVB3_EVENT_HOTPLUG:
  78                pr_debug("DVB3_EVENT_HOTPLUG\n");
  79                sms_board_event(coredev, BOARD_EVENT_POWER_INIT);
  80                break;
  81        case DVB3_EVENT_FE_LOCK:
  82                if (client->event_fe_state != DVB3_EVENT_FE_LOCK) {
  83                        client->event_fe_state = DVB3_EVENT_FE_LOCK;
  84                        pr_debug("DVB3_EVENT_FE_LOCK\n");
  85                        sms_board_event(coredev, BOARD_EVENT_FE_LOCK);
  86                }
  87                break;
  88        case DVB3_EVENT_FE_UNLOCK:
  89                if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) {
  90                        client->event_fe_state = DVB3_EVENT_FE_UNLOCK;
  91                        pr_debug("DVB3_EVENT_FE_UNLOCK\n");
  92                        sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK);
  93                }
  94                break;
  95        case DVB3_EVENT_UNC_OK:
  96                if (client->event_unc_state != DVB3_EVENT_UNC_OK) {
  97                        client->event_unc_state = DVB3_EVENT_UNC_OK;
  98                        pr_debug("DVB3_EVENT_UNC_OK\n");
  99                        sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK);
 100                }
 101                break;
 102        case DVB3_EVENT_UNC_ERR:
 103                if (client->event_unc_state != DVB3_EVENT_UNC_ERR) {
 104                        client->event_unc_state = DVB3_EVENT_UNC_ERR;
 105                        pr_debug("DVB3_EVENT_UNC_ERR\n");
 106                        sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS);
 107                }
 108                break;
 109
 110        default:
 111                pr_err("Unknown dvb3 api event\n");
 112                break;
 113        }
 114}
 115
 116static void smsdvb_stats_not_ready(struct dvb_frontend *fe)
 117{
 118        struct smsdvb_client_t *client =
 119                container_of(fe, struct smsdvb_client_t, frontend);
 120        struct smscore_device_t *coredev = client->coredev;
 121        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 122        int i, n_layers;
 123
 124        switch (smscore_get_device_mode(coredev)) {
 125        case DEVICE_MODE_ISDBT:
 126        case DEVICE_MODE_ISDBT_BDA:
 127                n_layers = 4;
 128                break;
 129        default:
 130                n_layers = 1;
 131        }
 132
 133        /* Global stats */
 134        c->strength.len = 1;
 135        c->cnr.len = 1;
 136        c->strength.stat[0].scale = FE_SCALE_DECIBEL;
 137        c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
 138
 139        /* Per-layer stats */
 140        c->post_bit_error.len = n_layers;
 141        c->post_bit_count.len = n_layers;
 142        c->block_error.len = n_layers;
 143        c->block_count.len = n_layers;
 144
 145        /*
 146         * Put all of them at FE_SCALE_NOT_AVAILABLE. They're dynamically
 147         * changed when the stats become available.
 148         */
 149        for (i = 0; i < n_layers; i++) {
 150                c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
 151                c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
 152                c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
 153                c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
 154        }
 155}
 156
 157static inline int sms_to_mode(u32 mode)
 158{
 159        switch (mode) {
 160        case 2:
 161                return TRANSMISSION_MODE_2K;
 162        case 4:
 163                return TRANSMISSION_MODE_4K;
 164        case 8:
 165                return TRANSMISSION_MODE_8K;
 166        }
 167        return TRANSMISSION_MODE_AUTO;
 168}
 169
 170static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked)
 171{
 172        if (is_demod_locked)
 173                return FE_HAS_SIGNAL  | FE_HAS_CARRIER | FE_HAS_VITERBI |
 174                       FE_HAS_SYNC    | FE_HAS_LOCK;
 175
 176        if (is_rf_locked)
 177                return FE_HAS_SIGNAL | FE_HAS_CARRIER;
 178
 179        return 0;
 180}
 181
 182static inline u32 sms_to_bw(u32 value)
 183{
 184        return value * 1000000;
 185}
 186
 187#define convert_from_table(value, table, defval) ({                     \
 188        u32 __ret;                                                      \
 189        if (value < ARRAY_SIZE(table))                                  \
 190                __ret = table[value];                                   \
 191        else                                                            \
 192                __ret = defval;                                         \
 193        __ret;                                                          \
 194})
 195
 196#define sms_to_guard_interval(value)                                    \
 197        convert_from_table(value, sms_to_guard_interval_table,          \
 198                           GUARD_INTERVAL_AUTO);
 199
 200#define sms_to_code_rate(value)                                         \
 201        convert_from_table(value, sms_to_code_rate_table,               \
 202                           FEC_NONE);
 203
 204#define sms_to_hierarchy(value)                                         \
 205        convert_from_table(value, sms_to_hierarchy_table,               \
 206                           FEC_NONE);
 207
 208#define sms_to_modulation(value)                                        \
 209        convert_from_table(value, sms_to_modulation_table,              \
 210                           FEC_NONE);
 211
 212static void smsdvb_update_tx_params(struct smsdvb_client_t *client,
 213                                    struct sms_tx_stats *p)
 214{
 215        struct dvb_frontend *fe = &client->frontend;
 216        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 217
 218        c->frequency = p->frequency;
 219        client->fe_status = sms_to_status(p->is_demod_locked, 0);
 220        c->bandwidth_hz = sms_to_bw(p->bandwidth);
 221        c->transmission_mode = sms_to_mode(p->transmission_mode);
 222        c->guard_interval = sms_to_guard_interval(p->guard_interval);
 223        c->code_rate_HP = sms_to_code_rate(p->code_rate);
 224        c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);
 225        c->hierarchy = sms_to_hierarchy(p->hierarchy);
 226        c->modulation = sms_to_modulation(p->constellation);
 227}
 228
 229static void smsdvb_update_per_slices(struct smsdvb_client_t *client,
 230                                     struct RECEPTION_STATISTICS_PER_SLICES_S *p)
 231{
 232        struct dvb_frontend *fe = &client->frontend;
 233        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 234        u64 tmp;
 235
 236        client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
 237        c->modulation = sms_to_modulation(p->constellation);
 238
 239        /* signal Strength, in DBm */
 240        c->strength.stat[0].uvalue = p->in_band_power * 1000;
 241
 242        /* Carrier to noise ratio, in DB */
 243        c->cnr.stat[0].svalue = p->snr * 1000;
 244
 245        /* PER/BER requires demod lock */
 246        if (!p->is_demod_locked)
 247                return;
 248
 249        /* TS PER */
 250        client->last_per = c->block_error.stat[0].uvalue;
 251        c->block_error.stat[0].scale = FE_SCALE_COUNTER;
 252        c->block_count.stat[0].scale = FE_SCALE_COUNTER;
 253        c->block_error.stat[0].uvalue += p->ets_packets;
 254        c->block_count.stat[0].uvalue += p->ets_packets + p->ts_packets;
 255
 256        /* ber */
 257        c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
 258        c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
 259        c->post_bit_error.stat[0].uvalue += p->ber_error_count;
 260        c->post_bit_count.stat[0].uvalue += p->ber_bit_count;
 261
 262        /* Legacy PER/BER */
 263        tmp = p->ets_packets * 65535ULL;
 264        if (p->ts_packets + p->ets_packets)
 265                do_div(tmp, p->ts_packets + p->ets_packets);
 266        client->legacy_per = tmp;
 267}
 268
 269static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client,
 270                                    struct sms_stats *p)
 271{
 272        struct dvb_frontend *fe = &client->frontend;
 273        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 274
 275        if (client->prt_dvb_stats)
 276                client->prt_dvb_stats(client->debug_data, p);
 277
 278        client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
 279
 280        /* Update DVB modulation parameters */
 281        c->frequency = p->frequency;
 282        client->fe_status = sms_to_status(p->is_demod_locked, 0);
 283        c->bandwidth_hz = sms_to_bw(p->bandwidth);
 284        c->transmission_mode = sms_to_mode(p->transmission_mode);
 285        c->guard_interval = sms_to_guard_interval(p->guard_interval);
 286        c->code_rate_HP = sms_to_code_rate(p->code_rate);
 287        c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);
 288        c->hierarchy = sms_to_hierarchy(p->hierarchy);
 289        c->modulation = sms_to_modulation(p->constellation);
 290
 291        /* update reception data */
 292        c->lna = p->is_external_lna_on ? 1 : 0;
 293
 294        /* Carrier to noise ratio, in DB */
 295        c->cnr.stat[0].svalue = p->SNR * 1000;
 296
 297        /* signal Strength, in DBm */
 298        c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
 299
 300        /* PER/BER requires demod lock */
 301        if (!p->is_demod_locked)
 302                return;
 303
 304        /* TS PER */
 305        client->last_per = c->block_error.stat[0].uvalue;
 306        c->block_error.stat[0].scale = FE_SCALE_COUNTER;
 307        c->block_count.stat[0].scale = FE_SCALE_COUNTER;
 308        c->block_error.stat[0].uvalue += p->error_ts_packets;
 309        c->block_count.stat[0].uvalue += p->total_ts_packets;
 310
 311        /* ber */
 312        c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
 313        c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
 314        c->post_bit_error.stat[0].uvalue += p->ber_error_count;
 315        c->post_bit_count.stat[0].uvalue += p->ber_bit_count;
 316
 317        /* Legacy PER/BER */
 318        client->legacy_ber = p->ber;
 319};
 320
 321static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client,
 322                                      struct sms_isdbt_stats *p)
 323{
 324        struct dvb_frontend *fe = &client->frontend;
 325        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 326        struct sms_isdbt_layer_stats *lr;
 327        int i, n_layers;
 328
 329        if (client->prt_isdb_stats)
 330                client->prt_isdb_stats(client->debug_data, p);
 331
 332        client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
 333
 334        /*
 335         * Firmware 2.1 seems to report only lock status and
 336         * signal strength. The signal strength indicator is at the
 337         * wrong field.
 338         */
 339        if (p->statistics_type == 0) {
 340                c->strength.stat[0].uvalue = ((s32)p->transmission_mode) * 1000;
 341                c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
 342                return;
 343        }
 344
 345        /* Update ISDB-T transmission parameters */
 346        c->frequency = p->frequency;
 347        c->bandwidth_hz = sms_to_bw(p->bandwidth);
 348        c->transmission_mode = sms_to_mode(p->transmission_mode);
 349        c->guard_interval = sms_to_guard_interval(p->guard_interval);
 350        c->isdbt_partial_reception = p->partial_reception ? 1 : 0;
 351        n_layers = p->num_of_layers;
 352        if (n_layers < 1)
 353                n_layers = 1;
 354        if (n_layers > 3)
 355                n_layers = 3;
 356        c->isdbt_layer_enabled = 0;
 357
 358        /* update reception data */
 359        c->lna = p->is_external_lna_on ? 1 : 0;
 360
 361        /* Carrier to noise ratio, in DB */
 362        c->cnr.stat[0].svalue = p->SNR * 1000;
 363
 364        /* signal Strength, in DBm */
 365        c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
 366
 367        /* PER/BER and per-layer stats require demod lock */
 368        if (!p->is_demod_locked)
 369                return;
 370
 371        client->last_per = c->block_error.stat[0].uvalue;
 372
 373        /* Clears global counters, as the code below will sum it again */
 374        c->block_error.stat[0].uvalue = 0;
 375        c->block_count.stat[0].uvalue = 0;
 376        c->block_error.stat[0].scale = FE_SCALE_COUNTER;
 377        c->block_count.stat[0].scale = FE_SCALE_COUNTER;
 378        c->post_bit_error.stat[0].uvalue = 0;
 379        c->post_bit_count.stat[0].uvalue = 0;
 380        c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
 381        c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
 382
 383        for (i = 0; i < n_layers; i++) {
 384                lr = &p->layer_info[i];
 385
 386                /* Update per-layer transmission parameters */
 387                if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {
 388                        c->isdbt_layer_enabled |= 1 << i;
 389                        c->layer[i].segment_count = lr->number_of_segments;
 390                } else {
 391                        continue;
 392                }
 393                c->layer[i].modulation = sms_to_modulation(lr->constellation);
 394
 395                /* TS PER */
 396                c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;
 397                c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;
 398                c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;
 399                c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;
 400
 401                /* Update global PER counter */
 402                c->block_error.stat[0].uvalue += lr->error_ts_packets;
 403                c->block_count.stat[0].uvalue += lr->total_ts_packets;
 404
 405                /* BER */
 406                c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;
 407                c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;
 408                c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;
 409                c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;
 410
 411                /* Update global BER counter */
 412                c->post_bit_error.stat[0].uvalue += lr->ber_error_count;
 413                c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;
 414        }
 415}
 416
 417static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client,
 418                                         struct sms_isdbt_stats_ex *p)
 419{
 420        struct dvb_frontend *fe = &client->frontend;
 421        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 422        struct sms_isdbt_layer_stats *lr;
 423        int i, n_layers;
 424
 425        if (client->prt_isdb_stats_ex)
 426                client->prt_isdb_stats_ex(client->debug_data, p);
 427
 428        /* Update ISDB-T transmission parameters */
 429        c->frequency = p->frequency;
 430        client->fe_status = sms_to_status(p->is_demod_locked, 0);
 431        c->bandwidth_hz = sms_to_bw(p->bandwidth);
 432        c->transmission_mode = sms_to_mode(p->transmission_mode);
 433        c->guard_interval = sms_to_guard_interval(p->guard_interval);
 434        c->isdbt_partial_reception = p->partial_reception ? 1 : 0;
 435        n_layers = p->num_of_layers;
 436        if (n_layers < 1)
 437                n_layers = 1;
 438        if (n_layers > 3)
 439                n_layers = 3;
 440        c->isdbt_layer_enabled = 0;
 441
 442        /* update reception data */
 443        c->lna = p->is_external_lna_on ? 1 : 0;
 444
 445        /* Carrier to noise ratio, in DB */
 446        c->cnr.stat[0].svalue = p->SNR * 1000;
 447
 448        /* signal Strength, in DBm */
 449        c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
 450
 451        /* PER/BER and per-layer stats require demod lock */
 452        if (!p->is_demod_locked)
 453                return;
 454
 455        client->last_per = c->block_error.stat[0].uvalue;
 456
 457        /* Clears global counters, as the code below will sum it again */
 458        c->block_error.stat[0].uvalue = 0;
 459        c->block_count.stat[0].uvalue = 0;
 460        c->block_error.stat[0].scale = FE_SCALE_COUNTER;
 461        c->block_count.stat[0].scale = FE_SCALE_COUNTER;
 462        c->post_bit_error.stat[0].uvalue = 0;
 463        c->post_bit_count.stat[0].uvalue = 0;
 464        c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
 465        c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
 466
 467        c->post_bit_error.len = n_layers + 1;
 468        c->post_bit_count.len = n_layers + 1;
 469        c->block_error.len = n_layers + 1;
 470        c->block_count.len = n_layers + 1;
 471        for (i = 0; i < n_layers; i++) {
 472                lr = &p->layer_info[i];
 473
 474                /* Update per-layer transmission parameters */
 475                if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {
 476                        c->isdbt_layer_enabled |= 1 << i;
 477                        c->layer[i].segment_count = lr->number_of_segments;
 478                } else {
 479                        continue;
 480                }
 481                c->layer[i].modulation = sms_to_modulation(lr->constellation);
 482
 483                /* TS PER */
 484                c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;
 485                c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;
 486                c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;
 487                c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;
 488
 489                /* Update global PER counter */
 490                c->block_error.stat[0].uvalue += lr->error_ts_packets;
 491                c->block_count.stat[0].uvalue += lr->total_ts_packets;
 492
 493                /* ber */
 494                c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;
 495                c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;
 496                c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;
 497                c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;
 498
 499                /* Update global ber counter */
 500                c->post_bit_error.stat[0].uvalue += lr->ber_error_count;
 501                c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;
 502        }
 503}
 504
 505static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
 506{
 507        struct smsdvb_client_t *client = (struct smsdvb_client_t *) context;
 508        struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) (((u8 *) cb->p)
 509                        + cb->offset);
 510        void *p = phdr + 1;
 511        struct dvb_frontend *fe = &client->frontend;
 512        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 513        bool is_status_update = false;
 514
 515        switch (phdr->msg_type) {
 516        case MSG_SMS_DVBT_BDA_DATA:
 517                /*
 518                 * Only feed data to dvb demux if are there any feed listening
 519                 * to it and if the device has tuned
 520                 */
 521                if (client->feed_users && client->has_tuned)
 522                        dvb_dmx_swfilter(&client->demux, p,
 523                                         cb->size - sizeof(struct sms_msg_hdr));
 524                break;
 525
 526        case MSG_SMS_RF_TUNE_RES:
 527        case MSG_SMS_ISDBT_TUNE_RES:
 528                complete(&client->tune_done);
 529                break;
 530
 531        case MSG_SMS_SIGNAL_DETECTED_IND:
 532                client->fe_status = FE_HAS_SIGNAL  | FE_HAS_CARRIER |
 533                                    FE_HAS_VITERBI | FE_HAS_SYNC    |
 534                                    FE_HAS_LOCK;
 535
 536                is_status_update = true;
 537                break;
 538
 539        case MSG_SMS_NO_SIGNAL_IND:
 540                client->fe_status = 0;
 541
 542                is_status_update = true;
 543                break;
 544
 545        case MSG_SMS_TRANSMISSION_IND:
 546                smsdvb_update_tx_params(client, p);
 547
 548                is_status_update = true;
 549                break;
 550
 551        case MSG_SMS_HO_PER_SLICES_IND:
 552                smsdvb_update_per_slices(client, p);
 553
 554                is_status_update = true;
 555                break;
 556
 557        case MSG_SMS_GET_STATISTICS_RES:
 558                switch (smscore_get_device_mode(client->coredev)) {
 559                case DEVICE_MODE_ISDBT:
 560                case DEVICE_MODE_ISDBT_BDA:
 561                        smsdvb_update_isdbt_stats(client, p);
 562                        break;
 563                default:
 564                        /* Skip sms_msg_statistics_info:request_result field */
 565                        smsdvb_update_dvb_stats(client, p + sizeof(u32));
 566                }
 567
 568                is_status_update = true;
 569                break;
 570
 571        /* Only for ISDB-T */
 572        case MSG_SMS_GET_STATISTICS_EX_RES:
 573                /* Skip sms_msg_statistics_info:request_result field? */
 574                smsdvb_update_isdbt_stats_ex(client, p + sizeof(u32));
 575                is_status_update = true;
 576                break;
 577        default:
 578                pr_debug("message not handled\n");
 579        }
 580        smscore_putbuffer(client->coredev, cb);
 581
 582        if (is_status_update) {
 583                if (client->fe_status & FE_HAS_LOCK) {
 584                        sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK);
 585                        if (client->last_per == c->block_error.stat[0].uvalue)
 586                                sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK);
 587                        else
 588                                sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR);
 589                        client->has_tuned = true;
 590                } else {
 591                        smsdvb_stats_not_ready(fe);
 592                        client->has_tuned = false;
 593                        sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK);
 594                }
 595                complete(&client->stats_done);
 596        }
 597
 598        return 0;
 599}
 600
 601static void smsdvb_media_device_unregister(struct smsdvb_client_t *client)
 602{
 603#ifdef CONFIG_MEDIA_CONTROLLER_DVB
 604        struct smscore_device_t *coredev = client->coredev;
 605
 606        if (!coredev->media_dev)
 607                return;
 608        media_device_unregister(coredev->media_dev);
 609        media_device_cleanup(coredev->media_dev);
 610        kfree(coredev->media_dev);
 611        coredev->media_dev = NULL;
 612#endif
 613}
 614
 615static void smsdvb_unregister_client(struct smsdvb_client_t *client)
 616{
 617        /* must be called under clientslock */
 618
 619        list_del(&client->entry);
 620
 621        smsdvb_debugfs_release(client);
 622        smscore_unregister_client(client->smsclient);
 623        dvb_unregister_frontend(&client->frontend);
 624        dvb_dmxdev_release(&client->dmxdev);
 625        dvb_dmx_release(&client->demux);
 626        smsdvb_media_device_unregister(client);
 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        pr_debug("add pid %d(%x)\n",
 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        pr_debug("remove pid %d(%x)\n",
 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, enum fe_status *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        pr_debug("\n");
 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        pr_debug("%s: freq %d band %d\n", __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                enum fe_status 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        pr_debug("freq %d segwidth %d segindex %d\n",
 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                enum fe_status 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
1007static int smsdvb_init(struct dvb_frontend *fe)
1008{
1009        struct smsdvb_client_t *client =
1010                container_of(fe, struct smsdvb_client_t, frontend);
1011
1012        sms_board_power(client->coredev, 1);
1013
1014        sms_board_dvb3_event(client, DVB3_EVENT_INIT);
1015        return 0;
1016}
1017
1018static int smsdvb_sleep(struct dvb_frontend *fe)
1019{
1020        struct smsdvb_client_t *client =
1021                container_of(fe, struct smsdvb_client_t, frontend);
1022
1023        sms_board_led_feedback(client->coredev, SMS_LED_OFF);
1024        sms_board_power(client->coredev, 0);
1025
1026        sms_board_dvb3_event(client, DVB3_EVENT_SLEEP);
1027
1028        return 0;
1029}
1030
1031static void smsdvb_release(struct dvb_frontend *fe)
1032{
1033        /* do nothing */
1034}
1035
1036static const struct dvb_frontend_ops smsdvb_fe_ops = {
1037        .info = {
1038                .name                   = "Siano Mobile Digital MDTV Receiver",
1039                .frequency_min_hz       =  44250 * kHz,
1040                .frequency_max_hz       = 867250 * kHz,
1041                .frequency_stepsize_hz  =    250 * kHz,
1042                .caps = FE_CAN_INVERSION_AUTO |
1043                        FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
1044                        FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
1045                        FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
1046                        FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
1047                        FE_CAN_GUARD_INTERVAL_AUTO |
1048                        FE_CAN_RECOVER |
1049                        FE_CAN_HIERARCHY_AUTO,
1050        },
1051
1052        .release = smsdvb_release,
1053
1054        .set_frontend = smsdvb_set_frontend,
1055        .get_tune_settings = smsdvb_get_tune_settings,
1056
1057        .read_status = smsdvb_read_status,
1058        .read_ber = smsdvb_read_ber,
1059        .read_signal_strength = smsdvb_read_signal_strength,
1060        .read_snr = smsdvb_read_snr,
1061        .read_ucblocks = smsdvb_read_ucblocks,
1062
1063        .init = smsdvb_init,
1064        .sleep = smsdvb_sleep,
1065};
1066
1067static int smsdvb_hotplug(struct smscore_device_t *coredev,
1068                          struct device *device, int arrival)
1069{
1070        struct smsclient_params_t params;
1071        struct smsdvb_client_t *client;
1072        int rc;
1073
1074        /* device removal handled by onremove callback */
1075        if (!arrival)
1076                return 0;
1077        client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL);
1078        if (!client)
1079                return -ENOMEM;
1080
1081        /* register dvb adapter */
1082        rc = dvb_register_adapter(&client->adapter,
1083                                  sms_get_board(
1084                                        smscore_get_board_id(coredev))->name,
1085                                  THIS_MODULE, device, adapter_nr);
1086        if (rc < 0) {
1087                pr_err("dvb_register_adapter() failed %d\n", rc);
1088                goto adapter_error;
1089        }
1090        dvb_register_media_controller(&client->adapter, coredev->media_dev);
1091
1092        /* init dvb demux */
1093        client->demux.dmx.capabilities = DMX_TS_FILTERING;
1094        client->demux.filternum = 32; /* todo: nova ??? */
1095        client->demux.feednum = 32;
1096        client->demux.start_feed = smsdvb_start_feed;
1097        client->demux.stop_feed = smsdvb_stop_feed;
1098
1099        rc = dvb_dmx_init(&client->demux);
1100        if (rc < 0) {
1101                pr_err("dvb_dmx_init failed %d\n", rc);
1102                goto dvbdmx_error;
1103        }
1104
1105        /* init dmxdev */
1106        client->dmxdev.filternum = 32;
1107        client->dmxdev.demux = &client->demux.dmx;
1108        client->dmxdev.capabilities = 0;
1109
1110        rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);
1111        if (rc < 0) {
1112                pr_err("dvb_dmxdev_init failed %d\n", rc);
1113                goto dmxdev_error;
1114        }
1115
1116        /* init and register frontend */
1117        memcpy(&client->frontend.ops, &smsdvb_fe_ops,
1118               sizeof(struct dvb_frontend_ops));
1119
1120        switch (smscore_get_device_mode(coredev)) {
1121        case DEVICE_MODE_DVBT:
1122        case DEVICE_MODE_DVBT_BDA:
1123                client->frontend.ops.delsys[0] = SYS_DVBT;
1124                break;
1125        case DEVICE_MODE_ISDBT:
1126        case DEVICE_MODE_ISDBT_BDA:
1127                client->frontend.ops.delsys[0] = SYS_ISDBT;
1128                break;
1129        }
1130
1131        rc = dvb_register_frontend(&client->adapter, &client->frontend);
1132        if (rc < 0) {
1133                pr_err("frontend registration failed %d\n", rc);
1134                goto frontend_error;
1135        }
1136
1137        params.initial_id = 1;
1138        params.data_type = MSG_SMS_DVBT_BDA_DATA;
1139        params.onresponse_handler = smsdvb_onresponse;
1140        params.onremove_handler = smsdvb_onremove;
1141        params.context = client;
1142
1143        rc = smscore_register_client(coredev, &params, &client->smsclient);
1144        if (rc < 0) {
1145                pr_err("smscore_register_client() failed %d\n", rc);
1146                goto client_error;
1147        }
1148
1149        client->coredev = coredev;
1150
1151        init_completion(&client->tune_done);
1152        init_completion(&client->stats_done);
1153
1154        kmutex_lock(&g_smsdvb_clientslock);
1155
1156        list_add(&client->entry, &g_smsdvb_clients);
1157
1158        kmutex_unlock(&g_smsdvb_clientslock);
1159
1160        client->event_fe_state = -1;
1161        client->event_unc_state = -1;
1162        sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG);
1163
1164        sms_board_setup(coredev);
1165
1166        if (smsdvb_debugfs_create(client) < 0)
1167                pr_info("failed to create debugfs node\n");
1168
1169        rc = dvb_create_media_graph(&client->adapter, true);
1170        if (rc < 0) {
1171                pr_err("dvb_create_media_graph failed %d\n", rc);
1172                goto client_error;
1173        }
1174
1175        pr_info("DVB interface registered.\n");
1176        return 0;
1177
1178client_error:
1179        dvb_unregister_frontend(&client->frontend);
1180
1181frontend_error:
1182        dvb_dmxdev_release(&client->dmxdev);
1183
1184dmxdev_error:
1185        dvb_dmx_release(&client->demux);
1186
1187dvbdmx_error:
1188        smsdvb_media_device_unregister(client);
1189        dvb_unregister_adapter(&client->adapter);
1190
1191adapter_error:
1192        kfree(client);
1193        return rc;
1194}
1195
1196static int __init smsdvb_module_init(void)
1197{
1198        int rc;
1199
1200        INIT_LIST_HEAD(&g_smsdvb_clients);
1201        kmutex_init(&g_smsdvb_clientslock);
1202
1203        smsdvb_debugfs_register();
1204
1205        rc = smscore_register_hotplug(smsdvb_hotplug);
1206
1207        pr_debug("\n");
1208
1209        return rc;
1210}
1211
1212static void __exit smsdvb_module_exit(void)
1213{
1214        smscore_unregister_hotplug(smsdvb_hotplug);
1215
1216        kmutex_lock(&g_smsdvb_clientslock);
1217
1218        while (!list_empty(&g_smsdvb_clients))
1219                smsdvb_unregister_client((struct smsdvb_client_t *)g_smsdvb_clients.next);
1220
1221        smsdvb_debugfs_unregister();
1222
1223        kmutex_unlock(&g_smsdvb_clientslock);
1224}
1225
1226module_init(smsdvb_module_init);
1227module_exit(smsdvb_module_exit);
1228
1229MODULE_DESCRIPTION("SMS DVB subsystem adaptation module");
1230MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)");
1231MODULE_LICENSE("GPL");
1232