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