2012-05-23 13:06:09 +00:00
|
|
|
/*
|
|
|
|
* DVB USB library - provides a generic interface for a DVB USB device driver.
|
|
|
|
*
|
|
|
|
* dvb-usb-init.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
2012-05-24 17:44:21 +00:00
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, version 2.
|
2012-05-23 13:06:09 +00:00
|
|
|
*
|
|
|
|
* see Documentation/dvb/README.dvb-usb for more information
|
|
|
|
*/
|
|
|
|
#include "dvb_usb_common.h"
|
2012-06-13 02:19:40 +00:00
|
|
|
#include <linux/usb/input.h>
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
int dvb_usbv2_disable_rc_polling;
|
|
|
|
module_param_named(disable_rc_polling, dvb_usbv2_disable_rc_polling, int, 0644);
|
2012-05-24 17:44:21 +00:00
|
|
|
MODULE_PARM_DESC(disable_rc_polling,
|
|
|
|
"disable remote control polling (default: 0).");
|
2012-05-23 13:06:09 +00:00
|
|
|
static int dvb_usb_force_pid_filter_usage;
|
2012-05-24 17:44:21 +00:00
|
|
|
module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage,
|
|
|
|
int, 0444);
|
|
|
|
MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a" \
|
|
|
|
" PID filter, if any (default: 0).");
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-06-10 02:24:29 +00:00
|
|
|
static int dvb_usbv2_download_firmware(struct dvb_usb_device *d)
|
2012-06-04 19:36:09 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const struct firmware *fw = NULL;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
/* resolve firmware name */
|
2012-06-12 19:25:01 +00:00
|
|
|
name = d->props->firmware;
|
|
|
|
if (d->props->get_firmware_name) {
|
|
|
|
ret = d->props->get_firmware_name(d, &name);
|
2012-06-04 19:36:09 +00:00
|
|
|
if (ret < 0)
|
2012-06-10 02:24:29 +00:00
|
|
|
goto err;
|
2012-06-04 19:36:09 +00:00
|
|
|
}
|
|
|
|
|
2012-06-12 19:25:01 +00:00
|
|
|
if (!d->props->download_firmware) {
|
2012-06-04 19:36:09 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = request_firmware(&fw, name, &d->udev->dev);
|
|
|
|
if (ret < 0) {
|
2012-06-07 01:46:38 +00:00
|
|
|
pr_err("%s: did not find the firmware file. (%s) " \
|
|
|
|
"Please see linux/Documentation/dvb/ for " \
|
2012-06-07 20:52:06 +00:00
|
|
|
"more details on firmware-problems. (%d)\n",
|
2012-06-07 01:46:38 +00:00
|
|
|
KBUILD_MODNAME, name, ret);
|
2012-06-04 19:36:09 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-06-07 20:52:06 +00:00
|
|
|
pr_info("%s: downloading firmware from file '%s'\n", KBUILD_MODNAME,
|
2012-06-07 01:46:38 +00:00
|
|
|
name);
|
2012-06-04 19:36:09 +00:00
|
|
|
|
2012-06-12 19:25:01 +00:00
|
|
|
ret = d->props->download_firmware(d, fw);
|
2012-06-04 19:36:09 +00:00
|
|
|
|
|
|
|
release_firmware(fw);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
|
2012-06-13 21:24:32 +00:00
|
|
|
return ret;
|
2012-06-04 19:36:09 +00:00
|
|
|
err:
|
|
|
|
pr_debug("%s: failed=%d\n", __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-06-10 02:24:29 +00:00
|
|
|
static int dvb_usbv2_i2c_init(struct dvb_usb_device *d)
|
2012-06-07 18:40:25 +00:00
|
|
|
{
|
2012-06-10 00:37:11 +00:00
|
|
|
int ret;
|
2012-06-07 18:40:25 +00:00
|
|
|
|
2012-06-12 19:25:01 +00:00
|
|
|
if (!d->props->i2c_algo) {
|
2012-06-10 00:37:11 +00:00
|
|
|
ret = 0;
|
|
|
|
goto err;
|
|
|
|
}
|
2012-06-07 18:40:25 +00:00
|
|
|
|
|
|
|
strlcpy(d->i2c_adap.name, d->name, sizeof(d->i2c_adap.name));
|
2012-06-12 19:25:01 +00:00
|
|
|
d->i2c_adap.algo = d->props->i2c_algo;
|
2012-06-07 18:40:25 +00:00
|
|
|
d->i2c_adap.algo_data = NULL;
|
|
|
|
d->i2c_adap.dev.parent = &d->udev->dev;
|
|
|
|
|
|
|
|
i2c_set_adapdata(&d->i2c_adap, d);
|
|
|
|
|
|
|
|
ret = i2c_add_adapter(&d->i2c_adap);
|
2012-06-10 00:37:11 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("%s: i2c_add_adapter() failed\n", KBUILD_MODNAME);
|
|
|
|
goto err;
|
|
|
|
}
|
2012-06-07 18:40:25 +00:00
|
|
|
|
|
|
|
d->state |= DVB_USB_STATE_I2C;
|
|
|
|
|
2012-06-10 00:37:11 +00:00
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
pr_debug("%s: failed=%d\n", __func__, ret);
|
2012-06-07 18:40:25 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-06-10 02:24:29 +00:00
|
|
|
static int dvb_usbv2_i2c_exit(struct dvb_usb_device *d)
|
2012-06-07 18:40:25 +00:00
|
|
|
{
|
|
|
|
if (d->state & DVB_USB_STATE_I2C)
|
|
|
|
i2c_del_adapter(&d->i2c_adap);
|
2012-06-10 00:37:11 +00:00
|
|
|
|
2012-06-07 18:40:25 +00:00
|
|
|
d->state &= ~DVB_USB_STATE_I2C;
|
2012-06-10 00:37:11 +00:00
|
|
|
|
2012-06-07 18:40:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-13 02:19:40 +00:00
|
|
|
static void dvb_usb_read_remote_control(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct dvb_usb_device *d = container_of(work,
|
|
|
|
struct dvb_usb_device, rc_query_work.work);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* TODO: need a lock here. We can simply skip checking for the remote
|
|
|
|
control if we're busy. */
|
|
|
|
|
|
|
|
/* when the parameter has been set to 1 via sysfs while the
|
|
|
|
* driver was running, or when bulk mode is enabled after IR init
|
|
|
|
*/
|
|
|
|
if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ret = d->rc.query(d);
|
|
|
|
if (ret < 0)
|
|
|
|
pr_err("%s: error %d while querying for an remote control " \
|
|
|
|
"event\n", KBUILD_MODNAME, ret);
|
|
|
|
|
|
|
|
schedule_delayed_work(&d->rc_query_work,
|
|
|
|
msecs_to_jiffies(d->rc.interval));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dvb_usbv2_remote_init(struct dvb_usb_device *d)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct rc_dev *dev;
|
|
|
|
|
2012-06-15 03:52:42 +00:00
|
|
|
pr_debug("%s:\n", __func__);
|
|
|
|
|
2012-06-13 02:19:40 +00:00
|
|
|
if (dvb_usbv2_disable_rc_polling || !d->props->get_rc_config)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = d->props->get_rc_config(d, &d->rc);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
dev = rc_allocate_device();
|
|
|
|
if (!dev) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->dev.parent = &d->udev->dev;
|
|
|
|
dev->input_name = "IR-receiver inside an USB DVB receiver";
|
|
|
|
usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
|
|
|
|
strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
|
|
|
|
dev->input_phys = d->rc_phys;
|
|
|
|
usb_to_input_id(d->udev, &dev->input_id);
|
|
|
|
/* TODO: likely RC-core should took const char * */
|
|
|
|
dev->driver_name = (char *) d->props->driver_name;
|
|
|
|
dev->driver_type = d->rc.driver_type;
|
|
|
|
dev->allowed_protos = d->rc.allowed_protos;
|
|
|
|
dev->change_protocol = d->rc.change_protocol;
|
|
|
|
dev->priv = d;
|
|
|
|
/* select used keymap */
|
|
|
|
if (d->rc.map_name)
|
|
|
|
dev->map_name = d->rc.map_name;
|
|
|
|
else if (d->rc_map)
|
|
|
|
dev->map_name = d->rc_map;
|
|
|
|
else
|
|
|
|
dev->map_name = RC_MAP_EMPTY; /* keep rc enabled */
|
|
|
|
|
|
|
|
ret = rc_register_device(dev);
|
|
|
|
if (ret < 0) {
|
|
|
|
rc_free_device(dev);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->input_dev = NULL;
|
|
|
|
d->rc_dev = dev;
|
|
|
|
|
|
|
|
/* start polling if needed */
|
|
|
|
if (d->rc.query && !d->rc.bulk_mode) {
|
|
|
|
/* initialize a work queue for handling polling */
|
|
|
|
INIT_DELAYED_WORK(&d->rc_query_work,
|
|
|
|
dvb_usb_read_remote_control);
|
|
|
|
pr_info("%s: schedule remote query interval to %d msecs\n",
|
|
|
|
KBUILD_MODNAME, d->rc.interval);
|
|
|
|
schedule_delayed_work(&d->rc_query_work,
|
|
|
|
msecs_to_jiffies(d->rc.interval));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
pr_debug("%s: failed=%d\n", __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dvb_usbv2_remote_exit(struct dvb_usb_device *d)
|
|
|
|
{
|
2012-06-15 03:52:42 +00:00
|
|
|
pr_debug("%s:\n", __func__);
|
|
|
|
|
|
|
|
if (d->rc_dev) {
|
2012-06-13 02:19:40 +00:00
|
|
|
cancel_delayed_work_sync(&d->rc_query_work);
|
|
|
|
rc_unregister_device(d->rc_dev);
|
2012-06-15 03:52:42 +00:00
|
|
|
d->rc_dev = NULL;
|
2012-06-13 02:19:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
static int dvb_usbv2_adapter_init(struct dvb_usb_device *d)
|
2012-05-23 13:06:09 +00:00
|
|
|
{
|
|
|
|
struct dvb_usb_adapter *adap;
|
2012-06-10 02:24:29 +00:00
|
|
|
int ret, i, adapter_count;
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-05-25 00:11:42 +00:00
|
|
|
/* resolve adapter count */
|
2012-06-12 19:25:01 +00:00
|
|
|
adapter_count = d->props->num_adapters;
|
|
|
|
if (d->props->get_adapter_count) {
|
|
|
|
ret = d->props->get_adapter_count(d);
|
2012-05-25 00:11:42 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
adapter_count = ret;
|
|
|
|
}
|
|
|
|
|
2012-06-10 02:24:29 +00:00
|
|
|
for (i = 0; i < adapter_count; i++) {
|
|
|
|
adap = &d->adapter[i];
|
2012-05-23 13:06:09 +00:00
|
|
|
adap->dev = d;
|
2012-06-12 19:25:01 +00:00
|
|
|
adap->id = i;
|
|
|
|
adap->props = &d->props->adapter[i];
|
2012-05-24 17:44:21 +00:00
|
|
|
|
2012-05-29 21:05:40 +00:00
|
|
|
/* speed - when running at FULL speed we need a HW PID filter */
|
|
|
|
if (d->udev->speed == USB_SPEED_FULL &&
|
2012-06-12 19:25:01 +00:00
|
|
|
!(adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) {
|
2012-06-07 01:46:38 +00:00
|
|
|
pr_err("%s: this USB2.0 device cannot be run on a " \
|
|
|
|
"USB1.1 port (it lacks a hardware " \
|
2012-06-07 20:52:06 +00:00
|
|
|
"PID filter)\n", KBUILD_MODNAME);
|
2012-06-10 02:24:29 +00:00
|
|
|
ret = -ENODEV;
|
|
|
|
goto err;
|
2012-05-29 21:05:40 +00:00
|
|
|
} else if ((d->udev->speed == USB_SPEED_FULL &&
|
2012-06-12 19:25:01 +00:00
|
|
|
adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER) ||
|
|
|
|
(adap->props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) {
|
2012-06-07 01:46:38 +00:00
|
|
|
pr_info("%s: will use the device's hardware PID " \
|
2012-06-07 20:52:06 +00:00
|
|
|
"filter (table count: %d)\n",
|
2012-06-07 01:46:38 +00:00
|
|
|
KBUILD_MODNAME,
|
2012-06-12 19:25:01 +00:00
|
|
|
adap->props->pid_filter_count);
|
2012-05-29 21:05:40 +00:00
|
|
|
adap->pid_filtering = 1;
|
2012-06-12 19:25:01 +00:00
|
|
|
adap->max_feed_count = adap->props->pid_filter_count;
|
2012-05-29 21:05:40 +00:00
|
|
|
} else {
|
2012-06-07 01:46:38 +00:00
|
|
|
pr_info("%s: will pass the complete MPEG2 transport " \
|
2012-06-07 20:52:06 +00:00
|
|
|
"stream to the software demuxer\n",
|
2012-06-07 01:46:38 +00:00
|
|
|
KBUILD_MODNAME);
|
2012-05-29 21:05:40 +00:00
|
|
|
adap->pid_filtering = 0;
|
|
|
|
adap->max_feed_count = 255;
|
|
|
|
}
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-05-29 21:05:40 +00:00
|
|
|
if (!adap->pid_filtering && dvb_usb_force_pid_filter_usage &&
|
2012-06-12 19:25:01 +00:00
|
|
|
adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER) {
|
2012-06-07 20:52:06 +00:00
|
|
|
pr_info("%s: pid filter enabled by module option\n",
|
2012-06-07 01:46:38 +00:00
|
|
|
KBUILD_MODNAME);
|
2012-05-29 21:05:40 +00:00
|
|
|
adap->pid_filtering = 1;
|
2012-06-12 19:25:01 +00:00
|
|
|
adap->max_feed_count = adap->props->pid_filter_count;
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
ret = dvb_usbv2_adapter_stream_init(adap);
|
2012-05-24 17:44:21 +00:00
|
|
|
if (ret)
|
2012-06-10 02:24:29 +00:00
|
|
|
goto err;
|
2012-05-24 17:44:21 +00:00
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
ret = dvb_usbv2_adapter_dvb_init(adap);
|
2012-05-24 17:44:21 +00:00
|
|
|
if (ret)
|
2012-06-10 02:24:29 +00:00
|
|
|
goto err;
|
2012-05-24 17:44:21 +00:00
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
ret = dvb_usbv2_adapter_frontend_init(adap);
|
2012-05-24 17:44:21 +00:00
|
|
|
if (ret)
|
2012-06-10 02:24:29 +00:00
|
|
|
goto err;
|
2012-05-23 13:06:09 +00:00
|
|
|
|
|
|
|
/* use exclusive FE lock if there is multiple shared FEs */
|
2012-05-30 01:20:24 +00:00
|
|
|
if (adap->fe[1])
|
2012-05-23 13:06:09 +00:00
|
|
|
adap->dvb_adap.mfe_shared = 1;
|
|
|
|
|
|
|
|
d->num_adapters_initialized++;
|
|
|
|
d->state |= DVB_USB_STATE_DVB;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2012-05-25 00:11:42 +00:00
|
|
|
err:
|
|
|
|
pr_debug("%s: failed=%d\n", __func__, ret);
|
|
|
|
return ret;
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d)
|
2012-05-23 13:06:09 +00:00
|
|
|
{
|
2012-06-10 02:24:29 +00:00
|
|
|
int i;
|
2012-06-05 01:18:34 +00:00
|
|
|
|
2012-06-15 03:29:36 +00:00
|
|
|
pr_debug("%s:\n", __func__);
|
|
|
|
|
|
|
|
for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) {
|
2012-06-10 02:24:29 +00:00
|
|
|
dvb_usbv2_adapter_frontend_exit(&d->adapter[i]);
|
|
|
|
dvb_usbv2_adapter_dvb_exit(&d->adapter[i]);
|
|
|
|
dvb_usbv2_adapter_stream_exit(&d->adapter[i]);
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
2012-06-10 02:24:29 +00:00
|
|
|
|
2012-05-23 13:06:09 +00:00
|
|
|
d->num_adapters_initialized = 0;
|
|
|
|
d->state &= ~DVB_USB_STATE_DVB;
|
2012-06-10 02:24:29 +00:00
|
|
|
|
2012-05-23 13:06:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* general initialization functions */
|
2012-08-01 17:44:06 +00:00
|
|
|
static int dvb_usbv2_exit(struct dvb_usb_device *d)
|
2012-05-23 13:06:09 +00:00
|
|
|
{
|
2012-06-04 23:55:34 +00:00
|
|
|
pr_debug("%s: state before exiting everything: %x\n", __func__, d->state);
|
2012-08-01 17:44:06 +00:00
|
|
|
dvb_usbv2_remote_exit(d);
|
|
|
|
dvb_usbv2_adapter_exit(d);
|
|
|
|
dvb_usbv2_i2c_exit(d);
|
2012-06-04 23:55:34 +00:00
|
|
|
pr_debug("%s: state should be zero now: %x\n", __func__, d->state);
|
2012-05-23 13:06:09 +00:00
|
|
|
d->state = DVB_USB_STATE_INIT;
|
|
|
|
kfree(d->priv);
|
|
|
|
kfree(d);
|
2012-06-10 02:24:29 +00:00
|
|
|
|
2012-05-23 13:06:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
static int dvb_usbv2_init(struct dvb_usb_device *d)
|
2012-05-23 13:06:09 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
d->state = DVB_USB_STATE_INIT;
|
|
|
|
|
|
|
|
/* check the capabilities and set appropriate variables */
|
2012-08-01 17:44:06 +00:00
|
|
|
dvb_usbv2_device_power_ctrl(d, 1);
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-06-12 19:25:01 +00:00
|
|
|
if (d->props->read_config) {
|
|
|
|
ret = d->props->read_config(d);
|
2012-05-25 01:18:37 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
ret = dvb_usbv2_i2c_init(d);
|
2012-06-10 02:24:29 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
2012-05-24 17:44:21 +00:00
|
|
|
|
2012-06-10 02:24:29 +00:00
|
|
|
ret = dvb_usbv2_adapter_init(d);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-06-12 19:25:01 +00:00
|
|
|
if (d->props->init) {
|
|
|
|
ret = d->props->init(d);
|
2012-06-10 02:24:29 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
}
|
2012-05-23 13:44:15 +00:00
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
ret = dvb_usbv2_remote_init(d);
|
2012-06-10 02:24:29 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
dvb_usbv2_device_power_ctrl(d, 0);
|
2012-05-23 13:06:09 +00:00
|
|
|
|
|
|
|
return 0;
|
2012-05-25 01:18:37 +00:00
|
|
|
err:
|
2012-06-10 02:24:29 +00:00
|
|
|
dvb_usbv2_device_power_ctrl(d, 0);
|
2012-05-25 01:18:37 +00:00
|
|
|
pr_debug("%s: failed=%d\n", __func__, ret);
|
|
|
|
return ret;
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
int dvb_usbv2_device_power_ctrl(struct dvb_usb_device *d, int onoff)
|
2012-05-23 13:06:09 +00:00
|
|
|
{
|
2012-06-10 02:24:29 +00:00
|
|
|
int ret;
|
|
|
|
|
2012-05-23 13:06:09 +00:00
|
|
|
if (onoff)
|
|
|
|
d->powered++;
|
|
|
|
else
|
|
|
|
d->powered--;
|
|
|
|
|
2012-05-24 17:44:21 +00:00
|
|
|
if (d->powered == 0 || (onoff && d->powered == 1)) {
|
|
|
|
/* when switching from 1 to 0 or from 0 to 1 */
|
2012-06-10 02:24:29 +00:00
|
|
|
pr_debug("%s: power control=%d\n", __func__, onoff);
|
2012-06-12 19:25:01 +00:00
|
|
|
if (d->props->power_ctrl) {
|
|
|
|
ret = d->props->power_ctrl(d, onoff);
|
2012-06-10 02:24:29 +00:00
|
|
|
goto err;
|
|
|
|
}
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
2012-06-10 02:24:29 +00:00
|
|
|
|
2012-05-23 13:06:09 +00:00
|
|
|
return 0;
|
2012-06-10 02:24:29 +00:00
|
|
|
err:
|
|
|
|
pr_debug("%s: failed=%d\n", __func__, ret);
|
|
|
|
return ret;
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
|
|
|
|
2012-06-06 03:05:06 +00:00
|
|
|
/*
|
|
|
|
* udev, which is used for the firmware downloading, requires we cannot
|
|
|
|
* block during module_init(). module_init() calls USB probe() which
|
|
|
|
* is this routine. Due to that we delay actual operation using workqueue
|
|
|
|
* and return always success here.
|
|
|
|
*/
|
2012-06-05 23:37:46 +00:00
|
|
|
|
2012-06-06 03:05:06 +00:00
|
|
|
static void dvb_usbv2_init_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct dvb_usb_device *d =
|
|
|
|
container_of(work, struct dvb_usb_device, probe_work);
|
|
|
|
bool cold = false;
|
2012-05-23 23:45:05 +00:00
|
|
|
|
2012-06-10 00:11:28 +00:00
|
|
|
d->work_pid = current->pid;
|
|
|
|
|
|
|
|
pr_debug("%s: work_pid=%d\n", __func__, d->work_pid);
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-06-12 19:25:01 +00:00
|
|
|
if (d->props->size_of_priv) {
|
|
|
|
d->priv = kzalloc(d->props->size_of_priv, GFP_KERNEL);
|
2012-06-06 03:05:06 +00:00
|
|
|
if (!d->priv) {
|
|
|
|
pr_err("%s: kzalloc() failed\n", KBUILD_MODNAME);
|
2012-05-23 23:45:05 +00:00
|
|
|
ret = -ENOMEM;
|
2012-06-06 03:05:06 +00:00
|
|
|
goto err_usb_driver_release_interface;
|
2012-05-23 23:45:05 +00:00
|
|
|
}
|
|
|
|
}
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-06-12 19:25:01 +00:00
|
|
|
if (d->props->identify_state) {
|
|
|
|
ret = d->props->identify_state(d);
|
2012-05-23 23:45:05 +00:00
|
|
|
if (ret == 0) {
|
|
|
|
;
|
|
|
|
} else if (ret == COLD) {
|
|
|
|
cold = true;
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
2012-06-06 03:05:06 +00:00
|
|
|
goto err_usb_driver_release_interface;
|
2012-05-23 23:45:05 +00:00
|
|
|
}
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cold) {
|
2012-06-06 03:05:06 +00:00
|
|
|
pr_info("%s: found a '%s' in cold state\n",
|
|
|
|
KBUILD_MODNAME, d->name);
|
2012-08-01 17:44:06 +00:00
|
|
|
ret = dvb_usbv2_download_firmware(d);
|
2012-05-23 23:45:05 +00:00
|
|
|
if (ret == 0) {
|
2012-06-14 23:07:02 +00:00
|
|
|
/* device is warm, continue initialization */
|
2012-05-23 23:45:05 +00:00
|
|
|
;
|
|
|
|
} else if (ret == RECONNECTS_USB) {
|
2012-06-14 23:07:02 +00:00
|
|
|
/*
|
|
|
|
* USB core will call disconnect() and then probe()
|
|
|
|
* as device reconnects itself from the USB bus.
|
|
|
|
* disconnect() will release all driver resources
|
|
|
|
* and probe() is called for 'new' device. As 'new'
|
|
|
|
* device is warm we should never go here again.
|
|
|
|
*/
|
|
|
|
return;
|
2012-05-23 23:45:05 +00:00
|
|
|
} else {
|
2012-06-14 23:07:02 +00:00
|
|
|
/* Unexpected fatal error. We must unregister driver
|
|
|
|
* manually from the device, because device is already
|
|
|
|
* register by returning from probe() with success.
|
|
|
|
* usb_driver_release_interface() finally calls
|
|
|
|
* disconnect() in order to free resources.
|
|
|
|
*/
|
2012-06-06 03:05:06 +00:00
|
|
|
goto err_usb_driver_release_interface;
|
2012-05-23 23:45:05 +00:00
|
|
|
}
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
|
|
|
|
2012-06-06 03:05:06 +00:00
|
|
|
pr_info("%s: found a '%s' in warm state\n", KBUILD_MODNAME, d->name);
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-08-01 17:44:06 +00:00
|
|
|
ret = dvb_usbv2_init(d);
|
2012-06-06 03:05:06 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err_usb_driver_release_interface;
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-06-06 03:05:06 +00:00
|
|
|
pr_info("%s: '%s' successfully initialized and connected\n",
|
|
|
|
KBUILD_MODNAME, d->name);
|
2012-06-03 18:49:45 +00:00
|
|
|
|
|
|
|
return;
|
2012-06-06 03:05:06 +00:00
|
|
|
err_usb_driver_release_interface:
|
|
|
|
pr_info("%s: '%s' error while loading driver (%d)\n", KBUILD_MODNAME,
|
|
|
|
d->name, ret);
|
2012-06-14 23:07:02 +00:00
|
|
|
/* it finally calls disconnect() which frees mem */
|
2012-06-06 03:05:06 +00:00
|
|
|
usb_driver_release_interface(to_usb_driver(d->intf->dev.driver),
|
|
|
|
d->intf);
|
2012-06-03 18:49:45 +00:00
|
|
|
pr_debug("%s: failed=%d\n", __func__, ret);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-06-07 19:23:40 +00:00
|
|
|
int dvb_usbv2_probe(struct usb_interface *intf,
|
2012-06-03 18:49:45 +00:00
|
|
|
const struct usb_device_id *id)
|
|
|
|
{
|
|
|
|
int ret;
|
2012-06-06 03:05:06 +00:00
|
|
|
struct dvb_usb_device *d;
|
|
|
|
struct dvb_usb_driver_info *driver_info =
|
|
|
|
(struct dvb_usb_driver_info *) id->driver_info;
|
2012-06-03 18:49:45 +00:00
|
|
|
|
2012-06-07 20:34:41 +00:00
|
|
|
pr_debug("%s: bInterfaceNumber=%d\n", __func__,
|
|
|
|
intf->cur_altsetting->desc.bInterfaceNumber);
|
2012-06-06 03:05:06 +00:00
|
|
|
|
|
|
|
if (!id->driver_info) {
|
|
|
|
pr_err("%s: driver_info failed\n", KBUILD_MODNAME);
|
|
|
|
ret = -ENODEV;
|
2012-06-03 18:49:45 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2012-06-06 03:05:06 +00:00
|
|
|
d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL);
|
|
|
|
if (!d) {
|
|
|
|
pr_err("%s: kzalloc() failed\n", KBUILD_MODNAME);
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
2012-06-03 18:49:45 +00:00
|
|
|
|
2012-06-06 03:05:06 +00:00
|
|
|
d->name = driver_info->name;
|
|
|
|
d->rc_map = driver_info->rc_map;
|
|
|
|
d->udev = interface_to_usbdev(intf);
|
|
|
|
d->intf = intf;
|
2012-06-12 19:25:01 +00:00
|
|
|
d->props = driver_info->props;
|
2012-06-07 20:34:41 +00:00
|
|
|
|
|
|
|
if (d->intf->cur_altsetting->desc.bInterfaceNumber !=
|
2012-06-12 19:25:01 +00:00
|
|
|
d->props->bInterfaceNumber) {
|
2012-06-10 02:42:47 +00:00
|
|
|
ret = -ENODEV;
|
2012-06-10 02:24:29 +00:00
|
|
|
goto err_kfree;
|
2012-06-07 20:34:41 +00:00
|
|
|
}
|
|
|
|
|
2012-06-06 03:05:06 +00:00
|
|
|
mutex_init(&d->usb_mutex);
|
|
|
|
mutex_init(&d->i2c_mutex);
|
|
|
|
INIT_WORK(&d->probe_work, dvb_usbv2_init_work);
|
|
|
|
usb_set_intfdata(intf, d);
|
|
|
|
ret = schedule_work(&d->probe_work);
|
2012-06-03 18:49:45 +00:00
|
|
|
if (ret < 0) {
|
2012-06-06 03:05:06 +00:00
|
|
|
pr_err("%s: schedule_work() failed\n", KBUILD_MODNAME);
|
2012-06-03 18:49:45 +00:00
|
|
|
goto err_kfree;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
err_kfree:
|
2012-06-06 03:05:06 +00:00
|
|
|
kfree(d);
|
2012-06-03 18:49:45 +00:00
|
|
|
err:
|
|
|
|
pr_debug("%s: failed=%d\n", __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-06-07 19:23:40 +00:00
|
|
|
EXPORT_SYMBOL(dvb_usbv2_probe);
|
2012-06-03 18:49:45 +00:00
|
|
|
|
2012-06-07 19:23:40 +00:00
|
|
|
void dvb_usbv2_disconnect(struct usb_interface *intf)
|
2012-05-23 13:06:09 +00:00
|
|
|
{
|
|
|
|
struct dvb_usb_device *d = usb_get_intfdata(intf);
|
2012-06-10 02:42:47 +00:00
|
|
|
const char *name;
|
2012-06-03 18:49:45 +00:00
|
|
|
|
2012-06-10 00:11:28 +00:00
|
|
|
pr_debug("%s: pid=%d work_pid=%d\n", __func__, current->pid,
|
|
|
|
d->work_pid);
|
2012-06-06 03:05:06 +00:00
|
|
|
|
2012-06-10 00:11:28 +00:00
|
|
|
/* ensure initialization work is finished until release resources */
|
|
|
|
if (d->work_pid != current->pid)
|
|
|
|
cancel_work_sync(&d->probe_work);
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-06-12 19:25:01 +00:00
|
|
|
if (d->props->disconnect)
|
|
|
|
d->props->disconnect(d);
|
2012-06-10 03:46:22 +00:00
|
|
|
|
2012-06-10 02:42:47 +00:00
|
|
|
name = d->name;
|
|
|
|
dvb_usbv2_exit(d);
|
2012-06-06 03:05:06 +00:00
|
|
|
|
|
|
|
pr_info("%s: '%s' successfully deinitialized and disconnected\n",
|
|
|
|
KBUILD_MODNAME, name);
|
2012-05-23 13:06:09 +00:00
|
|
|
}
|
2012-06-07 19:23:40 +00:00
|
|
|
EXPORT_SYMBOL(dvb_usbv2_disconnect);
|
2012-05-23 13:06:09 +00:00
|
|
|
|
2012-06-11 20:47:04 +00:00
|
|
|
int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg)
|
|
|
|
{
|
|
|
|
struct dvb_usb_device *d = usb_get_intfdata(intf);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pr_debug("%s:\n", __func__);
|
|
|
|
|
|
|
|
/* stop remote controller poll */
|
|
|
|
if (d->rc.query && !d->rc.bulk_mode)
|
|
|
|
cancel_delayed_work_sync(&d->rc_query_work);
|
|
|
|
|
|
|
|
/* stop streaming */
|
|
|
|
for (i = d->num_adapters_initialized - 1; i >= 0; i--) {
|
|
|
|
if (d->adapter[i].active_fe != -1)
|
|
|
|
usb_urb_killv2(&d->adapter[i].stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dvb_usbv2_suspend);
|
|
|
|
|
|
|
|
int dvb_usbv2_resume(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct dvb_usb_device *d = usb_get_intfdata(intf);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pr_debug("%s:\n", __func__);
|
|
|
|
|
|
|
|
/* start streaming */
|
|
|
|
for (i = 0; i < d->num_adapters_initialized; i++) {
|
|
|
|
if (d->adapter[i].active_fe != -1)
|
|
|
|
usb_urb_submitv2(&d->adapter[i].stream, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* start remote controller poll */
|
|
|
|
if (d->rc.query && !d->rc.bulk_mode)
|
|
|
|
schedule_delayed_work(&d->rc_query_work,
|
|
|
|
msecs_to_jiffies(d->rc.interval));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dvb_usbv2_resume);
|
|
|
|
|
2012-05-23 13:06:09 +00:00
|
|
|
MODULE_VERSION("1.0");
|
|
|
|
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
|
|
|
|
MODULE_DESCRIPTION("A library module containing commonly used USB and DVB function USB DVB devices");
|
|
|
|
MODULE_LICENSE("GPL");
|