#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <linux/dvb/frontend.h>
+#include <linux/mutex.h>
#include "dmxdev.h"
#include "dvb_demux.h"
#include "dvb_net.h"
-
#ifdef CONFIG_DVB_CINERGYT2_TUNING
#define STREAM_URB_COUNT (CONFIG_DVB_CINERGYT2_STREAM_URB_COUNT)
#define STREAM_BUF_SIZE (CONFIG_DVB_CINERGYT2_STREAM_BUF_SIZE)
#define STREAM_URB_COUNT (32)
#define STREAM_BUF_SIZE (512) /* bytes */
#define ENABLE_RC (1)
- #define RC_QUERY_INTERVAL (100) /* milliseconds */
+ #define RC_QUERY_INTERVAL (50) /* milliseconds */
#define QUERY_INTERVAL (333) /* milliseconds */
#endif
#define dprintk(level, args...) \
do { \
if ((debug & level)) { \
- printk("%s: %s(): ", __stringify(KBUILD_MODNAME), \
+ printk("%s: %s(): ", KBUILD_MODNAME, \
__FUNCTION__); \
printk(args); } \
} while (0)
struct cinergyt2 {
struct dvb_demux demux;
struct usb_device *udev;
- struct semaphore sem;
+ struct mutex sem;
struct dvb_adapter adapter;
struct dvb_device *fedev;
struct dmxdev dmxdev;
wait_queue_head_t poll_wq;
int pending_fe_events;
+ int disconnect_pending;
+ atomic_t inuse;
void *streambuf;
dma_addr_t streambuf_dmahandle;
struct urb *stream_urb [STREAM_URB_COUNT];
#ifdef ENABLE_RC
- struct input_dev rc_input_dev;
+ struct input_dev *rc_input_dev;
+ char phys[64];
struct work_struct rc_query_work;
int rc_input_event;
+ u32 rc_last_code;
+ unsigned long last_event_jiffies;
#endif
};
uint32_t value;
} __attribute__((packed));
-static const uint32_t rc_keys [] = {
+static const uint32_t rc_keys[] = {
CINERGYT2_RC_EVENT_TYPE_NEC, 0xfe01eb04, KEY_POWER,
CINERGYT2_RC_EVENT_TYPE_NEC, 0xfd02eb04, KEY_1,
CINERGYT2_RC_EVENT_TYPE_NEC, 0xfc03eb04, KEY_2,
if (cinergyt2->stream_urb[i])
usb_free_urb(cinergyt2->stream_urb[i]);
- pci_free_consistent(NULL, STREAM_URB_COUNT*STREAM_BUF_SIZE,
+ usb_buffer_free(cinergyt2->udev, STREAM_URB_COUNT*STREAM_BUF_SIZE,
cinergyt2->streambuf, cinergyt2->streambuf_dmahandle);
}
{
int i;
- cinergyt2->streambuf = pci_alloc_consistent(NULL,
- STREAM_URB_COUNT*STREAM_BUF_SIZE,
- &cinergyt2->streambuf_dmahandle);
+ cinergyt2->streambuf = usb_buffer_alloc(cinergyt2->udev, STREAM_URB_COUNT*STREAM_BUF_SIZE,
+ SLAB_KERNEL, &cinergyt2->streambuf_dmahandle);
if (!cinergyt2->streambuf) {
dprintk(1, "failed to alloc consistent stream memory area, bailing out!\n");
return -ENOMEM;
struct dvb_demux *demux = dvbdmxfeed->demux;
struct cinergyt2 *cinergyt2 = demux->priv;
- if (down_interruptible(&cinergyt2->sem))
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS;
if (cinergyt2->streaming == 0)
cinergyt2_start_stream_xfer(cinergyt2);
cinergyt2->streaming++;
- up(&cinergyt2->sem);
+ mutex_unlock(&cinergyt2->sem);
return 0;
}
struct dvb_demux *demux = dvbdmxfeed->demux;
struct cinergyt2 *cinergyt2 = demux->priv;
- if (down_interruptible(&cinergyt2->sem))
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS;
if (--cinergyt2->streaming == 0)
cinergyt2_stop_stream_xfer(cinergyt2);
- up(&cinergyt2->sem);
+ mutex_unlock(&cinergyt2->sem);
return 0;
}
{
struct dvb_device *dvbdev = file->private_data;
struct cinergyt2 *cinergyt2 = dvbdev->priv;
- int err;
+ int err = -ERESTARTSYS;
+
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
- if ((err = dvb_generic_open(inode, file)))
+ if ((err = dvb_generic_open(inode, file))) {
+ mutex_unlock(&cinergyt2->sem);
return err;
+ }
- if (down_interruptible(&cinergyt2->sem))
- return -ERESTARTSYS;
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
cinergyt2_sleep(cinergyt2, 0);
schedule_delayed_work(&cinergyt2->query_work, HZ/2);
}
- up(&cinergyt2->sem);
+ atomic_inc(&cinergyt2->inuse);
+
+ mutex_unlock(&cinergyt2->sem);
return 0;
}
+static void cinergyt2_unregister(struct cinergyt2 *cinergyt2)
+{
+ dvb_net_release(&cinergyt2->dvbnet);
+ dvb_dmxdev_release(&cinergyt2->dmxdev);
+ dvb_dmx_release(&cinergyt2->demux);
+ dvb_unregister_device(cinergyt2->fedev);
+ dvb_unregister_adapter(&cinergyt2->adapter);
+
+ cinergyt2_free_stream_urbs(cinergyt2);
+ kfree(cinergyt2);
+}
+
static int cinergyt2_release (struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
struct cinergyt2 *cinergyt2 = dvbdev->priv;
- if (down_interruptible(&cinergyt2->sem))
+ if (mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS;
- if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ if (!cinergyt2->disconnect_pending && (file->f_flags & O_ACCMODE) != O_RDONLY) {
cancel_delayed_work(&cinergyt2->query_work);
flush_scheduled_work();
cinergyt2_sleep(cinergyt2, 1);
}
- up(&cinergyt2->sem);
+ mutex_unlock(&cinergyt2->sem);
+
+ if (atomic_dec_and_test(&cinergyt2->inuse) && cinergyt2->disconnect_pending) {
+ warn("delayed unregister in release");
+ cinergyt2_unregister(cinergyt2);
+ }
return dvb_generic_release(inode, file);
}
{
struct dvb_device *dvbdev = file->private_data;
struct cinergyt2 *cinergyt2 = dvbdev->priv;
+
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
+ return -ERESTARTSYS;
+
poll_wait(file, &cinergyt2->poll_wq, wait);
+
+ mutex_unlock(&cinergyt2->sem);
+
return (POLLIN | POLLRDNORM | POLLPRI);
}
(__u16 __user *) arg);
case FE_READ_UNCORRECTED_BLOCKS:
- /* UNC are already converted to host byte order... */
- return put_user(stat->uncorrected_block_count,
- (__u32 __user *) arg);
+ {
+ uint32_t unc_count;
+
+ unc_count = stat->uncorrected_block_count;
+ stat->uncorrected_block_count = 0;
+ /* UNC are already converted to host byte order... */
+ return put_user(unc_count,(__u32 __user *) arg);
+ }
case FE_SET_FRONTEND:
{
struct dvbt_set_parameters_msg *param = &cinergyt2->param;
if (copy_from_user(&p, (void __user*) arg, sizeof(p)))
return -EFAULT;
- if (down_interruptible(&cinergyt2->sem))
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS;
param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
(char *) param, sizeof(*param),
NULL, 0);
- up(&cinergyt2->sem);
+ mutex_unlock(&cinergyt2->sem);
return (err < 0) ? err : 0;
}
};
#ifdef ENABLE_RC
+
static void cinergyt2_query_rc (void *data)
{
- struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data;
- char buf [1] = { CINERGYT2_EP1_GET_RC_EVENTS };
+ struct cinergyt2 *cinergyt2 = data;
+ char buf[1] = { CINERGYT2_EP1_GET_RC_EVENTS };
struct cinergyt2_rc_event rc_events[12];
- int n, len;
+ int n, len, i;
- if (down_interruptible(&cinergyt2->sem))
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return;
len = cinergyt2_command(cinergyt2, buf, sizeof(buf),
- (char *) rc_events, sizeof(rc_events));
+ (char *) rc_events, sizeof(rc_events));
+ if (len < 0)
+ goto out;
+ if (len == 0) {
+ if (time_after(jiffies, cinergyt2->last_event_jiffies +
+ msecs_to_jiffies(150))) {
+ /* stop key repeat */
+ if (cinergyt2->rc_input_event != KEY_MAX) {
+ dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event);
+ input_report_key(cinergyt2->rc_input_dev,
+ cinergyt2->rc_input_event, 0);
+ cinergyt2->rc_input_event = KEY_MAX;
+ }
+ cinergyt2->rc_last_code = ~0;
+ }
+ goto out;
+ }
+ cinergyt2->last_event_jiffies = jiffies;
- for (n=0; len>0 && n<(len/sizeof(rc_events[0])); n++) {
- int i;
+ for (n = 0; n < (len / sizeof(rc_events[0])); n++) {
+ dprintk(1, "rc_events[%d].value = %x, type=%x\n",
+ n, le32_to_cpu(rc_events[n].value), rc_events[n].type);
if (rc_events[n].type == CINERGYT2_RC_EVENT_TYPE_NEC &&
- rc_events[n].value == ~0)
- {
- /**
- * keyrepeat bit. If we would handle this properly
- * we would need to emit down events as long the
- * keyrepeat goes, a up event if no further
- * repeat bits occur. Would need a timer to implement
- * and no other driver does this, so we simply
- * emit the last key up/down sequence again.
- */
+ rc_events[n].value == ~0) {
+ /* keyrepeat bit -> just repeat last rc_input_event */
} else {
cinergyt2->rc_input_event = KEY_MAX;
- for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3) {
- if (rc_keys[i+0] == rc_events[n].type &&
- rc_keys[i+1] == rc_events[n].value)
- {
- cinergyt2->rc_input_event = rc_keys[i+2];
+ for (i = 0; i < ARRAY_SIZE(rc_keys); i += 3) {
+ if (rc_keys[i + 0] == rc_events[n].type &&
+ rc_keys[i + 1] == le32_to_cpu(rc_events[n].value)) {
+ cinergyt2->rc_input_event = rc_keys[i + 2];
break;
}
}
}
if (cinergyt2->rc_input_event != KEY_MAX) {
- input_report_key(&cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 1);
- input_report_key(&cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 0);
- input_sync(&cinergyt2->rc_input_dev);
+ if (rc_events[n].value == cinergyt2->rc_last_code &&
+ cinergyt2->rc_last_code != ~0) {
+ /* emit a key-up so the double event is recognized */
+ dprintk(1, "rc_input_event=%d UP\n", cinergyt2->rc_input_event);
+ input_report_key(cinergyt2->rc_input_dev,
+ cinergyt2->rc_input_event, 0);
+ }
+ dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event);
+ input_report_key(cinergyt2->rc_input_dev,
+ cinergyt2->rc_input_event, 1);
+ cinergyt2->rc_last_code = rc_events[n].value;
}
}
+out:
schedule_delayed_work(&cinergyt2->rc_query_work,
msecs_to_jiffies(RC_QUERY_INTERVAL));
- up(&cinergyt2->sem);
+ mutex_unlock(&cinergyt2->sem);
}
-#endif
+
+static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
+{
+ struct input_dev *input_dev;
+ int i;
+
+ cinergyt2->rc_input_dev = input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ usb_make_path(cinergyt2->udev, cinergyt2->phys, sizeof(cinergyt2->phys));
+ strlcat(cinergyt2->phys, "/input0", sizeof(cinergyt2->phys));
+ cinergyt2->rc_input_event = KEY_MAX;
+ cinergyt2->rc_last_code = ~0;
+ INIT_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc, cinergyt2);
+
+ input_dev->name = DRIVER_NAME " remote control";
+ input_dev->phys = cinergyt2->phys;
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ for (i = 0; i < ARRAY_SIZE(rc_keys); i += 3)
+ set_bit(rc_keys[i + 2], input_dev->keybit);
+ input_dev->keycodesize = 0;
+ input_dev->keycodemax = 0;
+
+ input_register_device(cinergyt2->rc_input_dev);
+ schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
+
+ return 0;
+}
+
+static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2)
+{
+ cancel_delayed_work(&cinergyt2->rc_query_work);
+ input_unregister_device(cinergyt2->rc_input_dev);
+}
+
+static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2)
+{
+ cancel_delayed_work(&cinergyt2->rc_query_work);
+}
+
+static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2)
+{
+ schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
+}
+
+#else
+
+static inline int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) { return 0; }
+static inline void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2) { }
+static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2) { }
+static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2) { }
+
+#endif /* ENABLE_RC */
static void cinergyt2_query (void *data)
{
uint8_t lock_bits;
uint32_t unc;
- if (down_interruptible(&cinergyt2->sem))
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return;
unc = s->uncorrected_block_count;
schedule_delayed_work(&cinergyt2->query_work,
msecs_to_jiffies(QUERY_INTERVAL));
- up(&cinergyt2->sem);
+ mutex_unlock(&cinergyt2->sem);
}
static int cinergyt2_probe (struct usb_interface *intf,
const struct usb_device_id *id)
{
struct cinergyt2 *cinergyt2;
- int i, err;
+ int err;
if (!(cinergyt2 = kmalloc (sizeof(struct cinergyt2), GFP_KERNEL))) {
dprintk(1, "out of memory?!?\n");
memset (cinergyt2, 0, sizeof (struct cinergyt2));
usb_set_intfdata (intf, (void *) cinergyt2);
- init_MUTEX(&cinergyt2->sem);
+ mutex_init(&cinergyt2->sem);
init_waitqueue_head (&cinergyt2->poll_wq);
INIT_WORK(&cinergyt2->query_work, cinergyt2_query, cinergyt2);
return -ENOMEM;
}
- dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE);
+ if ((err = dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE)) < 0) {
+ kfree(cinergyt2);
+ return err;
+ }
cinergyt2->demux.priv = cinergyt2;
cinergyt2->demux.filternum = 256;
&cinergyt2_fe_template, cinergyt2,
DVB_DEVICE_FRONTEND);
-#ifdef ENABLE_RC
- init_input_dev(&cinergyt2->rc_input_dev);
-
- cinergyt2->rc_input_dev.evbit[0] = BIT(EV_KEY);
- cinergyt2->rc_input_dev.keycodesize = sizeof(unsigned char);
- cinergyt2->rc_input_dev.keycodemax = KEY_MAX;
- cinergyt2->rc_input_dev.name = DRIVER_NAME " remote control";
-
- for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3)
- set_bit(rc_keys[i+2], cinergyt2->rc_input_dev.keybit);
-
- input_register_device(&cinergyt2->rc_input_dev);
-
- cinergyt2->rc_input_event = KEY_MAX;
+ err = cinergyt2_register_rc(cinergyt2);
+ if (err)
+ goto bailout;
- INIT_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc, cinergyt2);
- schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
-#endif
return 0;
bailout:
+ dvb_net_release(&cinergyt2->dvbnet);
dvb_dmxdev_release(&cinergyt2->dmxdev);
dvb_dmx_release(&cinergyt2->demux);
- dvb_unregister_adapter (&cinergyt2->adapter);
- cinergyt2_free_stream_urbs (cinergyt2);
+ dvb_unregister_adapter(&cinergyt2->adapter);
+ cinergyt2_free_stream_urbs(cinergyt2);
kfree(cinergyt2);
return -ENOMEM;
}
{
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
- if (down_interruptible(&cinergyt2->sem))
- return;
-
-#ifdef ENABLE_RC
- cancel_delayed_work(&cinergyt2->rc_query_work);
flush_scheduled_work();
- input_unregister_device(&cinergyt2->rc_input_dev);
-#endif
+
+ cinergyt2_unregister_rc(cinergyt2);
+
+ cancel_delayed_work(&cinergyt2->query_work);
+ wake_up_interruptible(&cinergyt2->poll_wq);
cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx);
- dvb_net_release(&cinergyt2->dvbnet);
- dvb_dmxdev_release(&cinergyt2->dmxdev);
- dvb_dmx_release(&cinergyt2->demux);
- dvb_unregister_device(cinergyt2->fedev);
- dvb_unregister_adapter(&cinergyt2->adapter);
+ cinergyt2->disconnect_pending = 1;
- cinergyt2_free_stream_urbs(cinergyt2);
- up(&cinergyt2->sem);
- kfree(cinergyt2);
+ if (!atomic_read(&cinergyt2->inuse))
+ cinergyt2_unregister(cinergyt2);
}
static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
{
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
- if (down_interruptible(&cinergyt2->sem))
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS;
- if (state > 0) { /* state 0 seems to mean DEVICE_PM_ON */
+ if (state.event > PM_EVENT_ON) {
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
-#ifdef ENABLE_RC
- cancel_delayed_work(&cinergyt2->rc_query_work);
-#endif
+
+ cinergyt2_suspend_rc(cinergyt2);
cancel_delayed_work(&cinergyt2->query_work);
if (cinergyt2->streaming)
cinergyt2_stop_stream_xfer(cinergyt2);
cinergyt2_sleep(cinergyt2, 1);
}
- up(&cinergyt2->sem);
+ mutex_unlock(&cinergyt2->sem);
return 0;
}
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
struct dvbt_set_parameters_msg *param = &cinergyt2->param;
- if (down_interruptible(&cinergyt2->sem))
+ if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS;
if (!cinergyt2->sleeping) {
schedule_delayed_work(&cinergyt2->query_work, HZ/2);
}
-#ifdef ENABLE_RC
- schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
-#endif
- up(&cinergyt2->sem);
+ cinergyt2_resume_rc(cinergyt2);
+
+ mutex_unlock(&cinergyt2->sem);
return 0;
}
MODULE_DEVICE_TABLE(usb, cinergyt2_table);
static struct usb_driver cinergyt2_driver = {
- .owner = THIS_MODULE,
.name = "cinergyT2",
.probe = cinergyt2_probe,
.disconnect = cinergyt2_disconnect,
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Holger Waechtler, Daniel Mack");
-