linux/drivers/media/usb/as102/as102_drv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Abilis Systems Single DVB-T Receiver
   4 * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
   5 * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
   6 */
   7#include <linux/kernel.h>
   8#include <linux/errno.h>
   9#include <linux/slab.h>
  10#include <linux/module.h>
  11#include <linux/mm.h>
  12#include <linux/kref.h>
  13#include <linux/uaccess.h>
  14#include <linux/usb.h>
  15
  16/* header file for usb device driver*/
  17#include "as102_drv.h"
  18#include "as10x_cmd.h"
  19#include "as102_fe.h"
  20#include "as102_fw.h"
  21#include <media/dvbdev.h>
  22
  23int dual_tuner;
  24module_param_named(dual_tuner, dual_tuner, int, 0644);
  25MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)");
  26
  27static int fw_upload = 1;
  28module_param_named(fw_upload, fw_upload, int, 0644);
  29MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)");
  30
  31static int pid_filtering;
  32module_param_named(pid_filtering, pid_filtering, int, 0644);
  33MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)");
  34
  35static int ts_auto_disable;
  36module_param_named(ts_auto_disable, ts_auto_disable, int, 0644);
  37MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)");
  38
  39int elna_enable = 1;
  40module_param_named(elna_enable, elna_enable, int, 0644);
  41MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)");
  42
  43DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
  44
  45static void as102_stop_stream(struct as102_dev_t *dev)
  46{
  47        struct as10x_bus_adapter_t *bus_adap;
  48
  49        if (dev != NULL)
  50                bus_adap = &dev->bus_adap;
  51        else
  52                return;
  53
  54        if (bus_adap->ops->stop_stream != NULL)
  55                bus_adap->ops->stop_stream(dev);
  56
  57        if (ts_auto_disable) {
  58                if (mutex_lock_interruptible(&dev->bus_adap.lock))
  59                        return;
  60
  61                if (as10x_cmd_stop_streaming(bus_adap) < 0)
  62                        dev_dbg(&dev->bus_adap.usb_dev->dev,
  63                                "as10x_cmd_stop_streaming failed\n");
  64
  65                mutex_unlock(&dev->bus_adap.lock);
  66        }
  67}
  68
  69static int as102_start_stream(struct as102_dev_t *dev)
  70{
  71        struct as10x_bus_adapter_t *bus_adap;
  72        int ret = -EFAULT;
  73
  74        if (dev != NULL)
  75                bus_adap = &dev->bus_adap;
  76        else
  77                return ret;
  78
  79        if (bus_adap->ops->start_stream != NULL)
  80                ret = bus_adap->ops->start_stream(dev);
  81
  82        if (ts_auto_disable) {
  83                if (mutex_lock_interruptible(&dev->bus_adap.lock))
  84                        return -EFAULT;
  85
  86                ret = as10x_cmd_start_streaming(bus_adap);
  87
  88                mutex_unlock(&dev->bus_adap.lock);
  89        }
  90
  91        return ret;
  92}
  93
  94static int as10x_pid_filter(struct as102_dev_t *dev,
  95                            int index, u16 pid, int onoff) {
  96
  97        struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap;
  98        int ret = -EFAULT;
  99
 100        if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
 101                dev_dbg(&dev->bus_adap.usb_dev->dev,
 102                        "amutex_lock_interruptible(lock) failed !\n");
 103                return -EBUSY;
 104        }
 105
 106        switch (onoff) {
 107        case 0:
 108                ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
 109                dev_dbg(&dev->bus_adap.usb_dev->dev,
 110                        "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
 111                        index, pid, ret);
 112                break;
 113        case 1:
 114        {
 115                struct as10x_ts_filter filter;
 116
 117                filter.type = TS_PID_TYPE_TS;
 118                filter.idx = 0xFF;
 119                filter.pid = pid;
 120
 121                ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
 122                dev_dbg(&dev->bus_adap.usb_dev->dev,
 123                        "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
 124                        index, filter.idx, filter.pid, ret);
 125                break;
 126        }
 127        }
 128
 129        mutex_unlock(&dev->bus_adap.lock);
 130        return ret;
 131}
 132
 133static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
 134{
 135        int ret = 0;
 136        struct dvb_demux *demux = dvbdmxfeed->demux;
 137        struct as102_dev_t *as102_dev = demux->priv;
 138
 139        if (mutex_lock_interruptible(&as102_dev->sem))
 140                return -ERESTARTSYS;
 141
 142        if (pid_filtering)
 143                as10x_pid_filter(as102_dev, dvbdmxfeed->index,
 144                                 dvbdmxfeed->pid, 1);
 145
 146        if (as102_dev->streaming++ == 0)
 147                ret = as102_start_stream(as102_dev);
 148
 149        mutex_unlock(&as102_dev->sem);
 150        return ret;
 151}
 152
 153static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
 154{
 155        struct dvb_demux *demux = dvbdmxfeed->demux;
 156        struct as102_dev_t *as102_dev = demux->priv;
 157
 158        if (mutex_lock_interruptible(&as102_dev->sem))
 159                return -ERESTARTSYS;
 160
 161        if (--as102_dev->streaming == 0)
 162                as102_stop_stream(as102_dev);
 163
 164        if (pid_filtering)
 165                as10x_pid_filter(as102_dev, dvbdmxfeed->index,
 166                                 dvbdmxfeed->pid, 0);
 167
 168        mutex_unlock(&as102_dev->sem);
 169        return 0;
 170}
 171
 172static int as102_set_tune(void *priv, struct as10x_tune_args *tune_args)
 173{
 174        struct as10x_bus_adapter_t *bus_adap = priv;
 175        int ret;
 176
 177        /* Set frontend arguments */
 178        if (mutex_lock_interruptible(&bus_adap->lock))
 179                return -EBUSY;
 180
 181        ret =  as10x_cmd_set_tune(bus_adap, tune_args);
 182        if (ret != 0)
 183                dev_dbg(&bus_adap->usb_dev->dev,
 184                        "as10x_cmd_set_tune failed. (err = %d)\n", ret);
 185
 186        mutex_unlock(&bus_adap->lock);
 187
 188        return ret;
 189}
 190
 191static int as102_get_tps(void *priv, struct as10x_tps *tps)
 192{
 193        struct as10x_bus_adapter_t *bus_adap = priv;
 194        int ret;
 195
 196        if (mutex_lock_interruptible(&bus_adap->lock))
 197                return -EBUSY;
 198
 199        /* send abilis command: GET_TPS */
 200        ret = as10x_cmd_get_tps(bus_adap, tps);
 201
 202        mutex_unlock(&bus_adap->lock);
 203
 204        return ret;
 205}
 206
 207static int as102_get_status(void *priv, struct as10x_tune_status *tstate)
 208{
 209        struct as10x_bus_adapter_t *bus_adap = priv;
 210        int ret;
 211
 212        if (mutex_lock_interruptible(&bus_adap->lock))
 213                return -EBUSY;
 214
 215        /* send abilis command: GET_TUNE_STATUS */
 216        ret = as10x_cmd_get_tune_status(bus_adap, tstate);
 217        if (ret < 0) {
 218                dev_dbg(&bus_adap->usb_dev->dev,
 219                        "as10x_cmd_get_tune_status failed (err = %d)\n",
 220                        ret);
 221        }
 222
 223        mutex_unlock(&bus_adap->lock);
 224
 225        return ret;
 226}
 227
 228static int as102_get_stats(void *priv, struct as10x_demod_stats *demod_stats)
 229{
 230        struct as10x_bus_adapter_t *bus_adap = priv;
 231        int ret;
 232
 233        if (mutex_lock_interruptible(&bus_adap->lock))
 234                return -EBUSY;
 235
 236        /* send abilis command: GET_TUNE_STATUS */
 237        ret = as10x_cmd_get_demod_stats(bus_adap, demod_stats);
 238        if (ret < 0) {
 239                dev_dbg(&bus_adap->usb_dev->dev,
 240                        "as10x_cmd_get_demod_stats failed (probably not tuned)\n");
 241        } else {
 242                dev_dbg(&bus_adap->usb_dev->dev,
 243                        "demod status: fc: 0x%08x, bad fc: 0x%08x, bytes corrected: 0x%08x , MER: 0x%04x\n",
 244                        demod_stats->frame_count,
 245                        demod_stats->bad_frame_count,
 246                        demod_stats->bytes_fixed_by_rs,
 247                        demod_stats->mer);
 248        }
 249        mutex_unlock(&bus_adap->lock);
 250
 251        return ret;
 252}
 253
 254static int as102_stream_ctrl(void *priv, int acquire, uint32_t elna_cfg)
 255{
 256        struct as10x_bus_adapter_t *bus_adap = priv;
 257        int ret;
 258
 259        if (mutex_lock_interruptible(&bus_adap->lock))
 260                return -EBUSY;
 261
 262        if (acquire) {
 263                if (elna_enable)
 264                        as10x_cmd_set_context(bus_adap,
 265                                              CONTEXT_LNA, elna_cfg);
 266
 267                ret = as10x_cmd_turn_on(bus_adap);
 268        } else {
 269                ret = as10x_cmd_turn_off(bus_adap);
 270        }
 271
 272        mutex_unlock(&bus_adap->lock);
 273
 274        return ret;
 275}
 276
 277static const struct as102_fe_ops as102_fe_ops = {
 278        .set_tune = as102_set_tune,
 279        .get_tps  = as102_get_tps,
 280        .get_status = as102_get_status,
 281        .get_stats = as102_get_stats,
 282        .stream_ctrl = as102_stream_ctrl,
 283};
 284
 285int as102_dvb_register(struct as102_dev_t *as102_dev)
 286{
 287        struct device *dev = &as102_dev->bus_adap.usb_dev->dev;
 288        int ret;
 289
 290        ret = dvb_register_adapter(&as102_dev->dvb_adap,
 291                           as102_dev->name, THIS_MODULE,
 292                           dev, adapter_nr);
 293        if (ret < 0) {
 294                dev_err(dev, "%s: dvb_register_adapter() failed: %d\n",
 295                        __func__, ret);
 296                return ret;
 297        }
 298
 299        as102_dev->dvb_dmx.priv = as102_dev;
 300        as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256;
 301        as102_dev->dvb_dmx.feednum = 256;
 302        as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed;
 303        as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed;
 304
 305        as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING |
 306                                              DMX_SECTION_FILTERING;
 307
 308        as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum;
 309        as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx;
 310        as102_dev->dvb_dmxdev.capabilities = 0;
 311
 312        ret = dvb_dmx_init(&as102_dev->dvb_dmx);
 313        if (ret < 0) {
 314                dev_err(dev, "%s: dvb_dmx_init() failed: %d\n", __func__, ret);
 315                goto edmxinit;
 316        }
 317
 318        ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap);
 319        if (ret < 0) {
 320                dev_err(dev, "%s: dvb_dmxdev_init() failed: %d\n",
 321                        __func__, ret);
 322                goto edmxdinit;
 323        }
 324
 325        /* Attach the frontend */
 326        as102_dev->dvb_fe = dvb_attach(as102_attach, as102_dev->name,
 327                                       &as102_fe_ops,
 328                                       &as102_dev->bus_adap,
 329                                       as102_dev->elna_cfg);
 330        if (!as102_dev->dvb_fe) {
 331                ret = -ENODEV;
 332                dev_err(dev, "%s: as102_attach() failed: %d",
 333                    __func__, ret);
 334                goto efereg;
 335        }
 336
 337        ret =  dvb_register_frontend(&as102_dev->dvb_adap, as102_dev->dvb_fe);
 338        if (ret < 0) {
 339                dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d",
 340                    __func__, ret);
 341                goto efereg;
 342        }
 343
 344        /* init bus mutex for token locking */
 345        mutex_init(&as102_dev->bus_adap.lock);
 346
 347        /* init start / stop stream mutex */
 348        mutex_init(&as102_dev->sem);
 349
 350        /*
 351         * try to load as102 firmware. If firmware upload failed, we'll be
 352         * able to upload it later.
 353         */
 354        if (fw_upload)
 355                try_then_request_module(as102_fw_upload(&as102_dev->bus_adap),
 356                                "firmware_class");
 357
 358        pr_info("Registered device %s", as102_dev->name);
 359        return 0;
 360
 361efereg:
 362        dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
 363edmxdinit:
 364        dvb_dmx_release(&as102_dev->dvb_dmx);
 365edmxinit:
 366        dvb_unregister_adapter(&as102_dev->dvb_adap);
 367        return ret;
 368}
 369
 370void as102_dvb_unregister(struct as102_dev_t *as102_dev)
 371{
 372        /* unregister as102 frontend */
 373        dvb_unregister_frontend(as102_dev->dvb_fe);
 374
 375        /* detach frontend */
 376        dvb_frontend_detach(as102_dev->dvb_fe);
 377
 378        /* unregister demux device */
 379        dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
 380        dvb_dmx_release(&as102_dev->dvb_dmx);
 381
 382        /* unregister dvb adapter */
 383        dvb_unregister_adapter(&as102_dev->dvb_adap);
 384
 385        pr_info("Unregistered device %s", as102_dev->name);
 386}
 387
 388module_usb_driver(as102_usb_driver);
 389
 390/* modinfo details */
 391MODULE_DESCRIPTION(DRIVER_FULL_NAME);
 392MODULE_LICENSE("GPL");
 393MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");
 394