X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmedia%2Fdvb%2FcinergyT2%2FcinergyT2.c;h=d64b96cb0c469ea9e325eca7e557e0f084ce7c39;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=644d9241c10d40d0ff4ee1bd045d5e4f3484f5e9;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c index 644d9241c..d64b96cb0 100644 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -22,28 +22,20 @@ * */ -#include #include #include -#include #include #include #include #include #include +#include +#include #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) @@ -56,7 +48,7 @@ #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 @@ -66,10 +58,10 @@ static int debug; module_param_named(debug, debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); -#define dprintk(level, args...) \ +#define dprintk(level, args...) \ do { \ if ((debug & level)) { \ - printk("%s: %s(): ", __stringify(KBUILD_MODNAME), \ + printk("%s: %s(): ", KBUILD_MODNAME, \ __FUNCTION__); \ printk(args); } \ } while (0) @@ -77,8 +69,8 @@ do { \ enum cinergyt2_ep1_cmd { CINERGYT2_EP1_PID_TABLE_RESET = 0x01, CINERGYT2_EP1_PID_SETUP = 0x02, - CINERGYT2_EP1_CONTROL_STREAM_TRANSFER = 0x03, - CINERGYT2_EP1_SET_TUNER_PARAMETERS = 0x04, + CINERGYT2_EP1_CONTROL_STREAM_TRANSFER = 0x03, + CINERGYT2_EP1_SET_TUNER_PARAMETERS = 0x04, CINERGYT2_EP1_GET_TUNER_STATUS = 0x05, CINERGYT2_EP1_START_SCAN = 0x06, CINERGYT2_EP1_CONTINUE_SCAN = 0x07, @@ -125,8 +117,8 @@ static struct dvb_frontend_info cinergyt2_fe_info = { struct cinergyt2 { struct dvb_demux demux; struct usb_device *udev; - struct semaphore sem; - struct dvb_adapter *adapter; + struct mutex sem; + struct dvb_adapter adapter; struct dvb_device *fedev; struct dmxdev dmxdev; struct dvb_net dvbnet; @@ -136,19 +128,24 @@ struct cinergyt2 { struct dvbt_set_parameters_msg param; struct dvbt_get_status_msg status; - struct work_struct query_work; + struct delayed_work query_work; 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]; + struct urb *stream_urb [STREAM_URB_COUNT]; #ifdef ENABLE_RC - struct input_dev rc_input_dev; - struct work_struct rc_query_work; + struct input_dev *rc_input_dev; + char phys[64]; + struct delayed_work rc_query_work; int rc_input_event; + u32 rc_last_code; + unsigned long last_event_jiffies; #endif }; @@ -163,48 +160,48 @@ struct cinergyt2_rc_event { uint32_t value; } __attribute__((packed)); -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, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xfb04eb04, KEY_3, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xfa05eb04, KEY_4, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf906eb04, KEY_5, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf807eb04, KEY_6, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf708eb04, KEY_7, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf609eb04, KEY_8, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf50aeb04, KEY_9, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf30ceb04, KEY_0, +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, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xfb04eb04, KEY_3, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xfa05eb04, KEY_4, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xf906eb04, KEY_5, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xf807eb04, KEY_6, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xf708eb04, KEY_7, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xf609eb04, KEY_8, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xf50aeb04, KEY_9, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xf30ceb04, KEY_0, CINERGYT2_RC_EVENT_TYPE_NEC, 0xf40beb04, KEY_VIDEO, CINERGYT2_RC_EVENT_TYPE_NEC, 0xf20deb04, KEY_REFRESH, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf10eeb04, KEY_SELECT, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf00feb04, KEY_EPG, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xef10eb04, KEY_UP, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xeb14eb04, KEY_DOWN, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xee11eb04, KEY_LEFT, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xec13eb04, KEY_RIGHT, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xed12eb04, KEY_OK, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xea15eb04, KEY_TEXT, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe916eb04, KEY_INFO, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe817eb04, KEY_RED, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe718eb04, KEY_GREEN, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe619eb04, KEY_YELLOW, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe51aeb04, KEY_BLUE, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe31ceb04, KEY_VOLUMEUP, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe11eeb04, KEY_VOLUMEDOWN, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe21deb04, KEY_MUTE, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe41beb04, KEY_CHANNELUP, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe01feb04, KEY_CHANNELDOWN, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xbf40eb04, KEY_PAUSE, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xb34ceb04, KEY_PLAY, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xa758eb04, KEY_RECORD, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xab54eb04, KEY_PREVIOUS, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xb748eb04, KEY_STOP, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xa35ceb04, KEY_NEXT + CINERGYT2_RC_EVENT_TYPE_NEC, 0xf10eeb04, KEY_SELECT, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xf00feb04, KEY_EPG, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xef10eb04, KEY_UP, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xeb14eb04, KEY_DOWN, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xee11eb04, KEY_LEFT, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xec13eb04, KEY_RIGHT, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xed12eb04, KEY_OK, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xea15eb04, KEY_TEXT, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe916eb04, KEY_INFO, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe817eb04, KEY_RED, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe718eb04, KEY_GREEN, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe619eb04, KEY_YELLOW, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe51aeb04, KEY_BLUE, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe31ceb04, KEY_VOLUMEUP, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe11eeb04, KEY_VOLUMEDOWN, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe21deb04, KEY_MUTE, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe41beb04, KEY_CHANNELUP, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xe01feb04, KEY_CHANNELDOWN, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xbf40eb04, KEY_PAUSE, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xb34ceb04, KEY_PLAY, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xa758eb04, KEY_RECORD, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xab54eb04, KEY_PREVIOUS, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xb748eb04, KEY_STOP, + CINERGYT2_RC_EVENT_TYPE_NEC, 0xa35ceb04, KEY_NEXT }; static int cinergyt2_command (struct cinergyt2 *cinergyt2, - char *send_buf, int send_buf_len, + char *send_buf, int send_buf_len, char *recv_buf, int recv_buf_len) { int actual_len; @@ -212,7 +209,7 @@ static int cinergyt2_command (struct cinergyt2 *cinergyt2, int ret; ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1), - send_buf, send_buf_len, &actual_len, HZ); + send_buf, send_buf_len, &actual_len, 1000); if (ret) dprintk(1, "usb_bulk_msg (send) failed, err %i\n", ret); @@ -221,7 +218,7 @@ static int cinergyt2_command (struct cinergyt2 *cinergyt2, recv_buf = &dummy; ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1), - recv_buf, recv_buf_len, &actual_len, HZ); + recv_buf, recv_buf_len, &actual_len, 1000); if (ret) dprintk(1, "usb_bulk_msg (read) failed, err %i\n", ret); @@ -232,17 +229,17 @@ static int cinergyt2_command (struct cinergyt2 *cinergyt2, static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable) { char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 }; - cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); + cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); } static void cinergyt2_sleep (struct cinergyt2 *cinergyt2, int sleep) { char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 }; - cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); + cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); cinergyt2->sleeping = sleep; } -static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs); +static void cinergyt2_stream_irq (struct urb *urb); static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb *urb) { @@ -262,7 +259,7 @@ static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb return err; } -static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs) +static void cinergyt2_stream_irq (struct urb *urb) { struct cinergyt2 *cinergyt2 = urb->context; @@ -279,10 +276,9 @@ static void cinergyt2_free_stream_urbs (struct cinergyt2 *cinergyt2) int i; for (i=0; istream_urb[i]) - usb_free_urb(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); } @@ -290,9 +286,8 @@ static int cinergyt2_alloc_stream_urbs (struct cinergyt2 *cinergyt2) { 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, + GFP_KERNEL, &cinergyt2->streambuf_dmahandle); if (!cinergyt2->streambuf) { dprintk(1, "failed to alloc consistent stream memory area, bailing out!\n"); return -ENOMEM; @@ -301,7 +296,7 @@ static int cinergyt2_alloc_stream_urbs (struct cinergyt2 *cinergyt2) memset(cinergyt2->streambuf, 0, STREAM_URB_COUNT*STREAM_BUF_SIZE); for (i=0; istream_urb[i]) - usb_kill_urb(cinergyt2->stream_urb[i]); + usb_kill_urb(cinergyt2->stream_urb[i]); } static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2) @@ -350,29 +344,29 @@ static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed) 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_start_stream_xfer(cinergyt2); cinergyt2->streaming++; - up(&cinergyt2->sem); + mutex_unlock(&cinergyt2->sem); return 0; } static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *demux = dvbdmxfeed->demux; - struct cinergyt2 *cinergyt2 = demux->priv; + 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; } @@ -486,54 +480,87 @@ static int cinergyt2_open (struct inode *inode, struct file *file) { 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); -} + } + + atomic_inc(&cinergyt2->inuse); - up(&cinergyt2->sem); + 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); - return dvb_generic_release(inode, file); + if (atomic_dec_and_test(&cinergyt2->inuse) && cinergyt2->disconnect_pending) { + warn("delayed unregister in release"); + cinergyt2_unregister(cinergyt2); } + return dvb_generic_release(inode, file); +} + static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct *wait) - { +{ struct dvb_device *dvbdev = file->private_data; struct cinergyt2 *cinergyt2 = dvbdev->priv; + unsigned int mask = 0; + + if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; + poll_wait(file, &cinergyt2->poll_wq, wait); - return (POLLIN | POLLRDNORM | POLLPRI); - } + + if (cinergyt2->pending_fe_events != 0) + mask |= (POLLIN | POLLRDNORM | POLLPRI); + + mutex_unlock(&cinergyt2->sem); + + return mask; +} static int cinergyt2_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long arg) - { +{ struct dvb_device *dvbdev = file->private_data; struct cinergyt2 *cinergyt2 = dvbdev->priv; struct dvbt_get_status_msg *stat = &cinergyt2->status; @@ -571,10 +598,15 @@ static int cinergyt2_ioctl (struct inode *inode, struct file *file, (__u16 __user *) arg); case FE_READ_UNCORRECTED_BLOCKS: + { + 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(stat->uncorrected_block_count, - (__u32 __user *) arg); - + return put_user(unc_count,(__u32 __user *) arg); + } case FE_SET_FRONTEND: { struct dvbt_set_parameters_msg *param = &cinergyt2->param; @@ -587,7 +619,7 @@ static int cinergyt2_ioctl (struct inode *inode, struct file *file, 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; @@ -603,7 +635,7 @@ static int cinergyt2_ioctl (struct inode *inode, struct file *file, (char *) param, sizeof(*param), NULL, 0); - up(&cinergyt2->sem); + mutex_unlock(&cinergyt2->sem); return (err < 0) ? err : 0; } @@ -611,7 +643,7 @@ static int cinergyt2_ioctl (struct inode *inode, struct file *file, case FE_GET_FRONTEND: /** * trivial to implement (see struct dvbt_get_status_msg). - * equivalent to FE_READ ioctls, but needs + * equivalent to FE_READ ioctls, but needs * TPS -> linux-dvb parameter set conversion. Feel free * to implement this and send us a patch if you need this * functionality. @@ -661,7 +693,7 @@ static int cinergyt2_mmap(struct file *file, struct vm_area_struct *vma) goto bailout; } - vma->vm_flags |= (VM_IO | VM_DONTCOPY); + vma->vm_flags |= (VM_IO | VM_DONTCOPY); vma->vm_file = file; ret = remap_pfn_range(vma, vma->vm_start, @@ -690,68 +722,151 @@ static struct dvb_device cinergyt2_fe_template = { }; #ifdef ENABLE_RC -static void cinergyt2_query_rc (void *data) + +static void cinergyt2_query_rc (struct work_struct *work) { - struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data; - char buf [1] = { CINERGYT2_EP1_GET_RC_EVENTS }; + struct cinergyt2 *cinergyt2 = + container_of(work, struct cinergyt2, rc_query_work.work); + 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)); + len = cinergyt2_command(cinergyt2, buf, sizeof(buf), + (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); + input_sync(cinergyt2->rc_input_dev); + 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; irc_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); + input_sync(cinergyt2->rc_input_dev); + 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 void cinergyt2_query (void *data) +static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) { - struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data; + struct input_dev *input_dev; + int i; + int err; + + 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_DELAYED_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc); + + 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; + + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + cinergyt2->rc_input_dev = 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 (struct work_struct *work) +{ + struct cinergyt2 *cinergyt2 = + container_of(work, struct cinergyt2, query_work.work); char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS }; struct dvbt_get_status_msg *s = &cinergyt2->status; 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; @@ -770,14 +885,14 @@ static void cinergyt2_query (void *data) 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"); @@ -787,20 +902,23 @@ static int cinergyt2_probe (struct usb_interface *intf, 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); + INIT_DELAYED_WORK(&cinergyt2->query_work, cinergyt2_query); cinergyt2->udev = interface_to_usbdev(intf); cinergyt2->param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; - + if (cinergyt2_alloc_stream_urbs (cinergyt2) < 0) { dprintk(1, "unable to allocate stream urbs\n"); kfree(cinergyt2); return -ENOMEM; } - dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE); + if ((err = dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE, &cinergyt2->udev->dev)) < 0) { + kfree(cinergyt2); + return err; + } cinergyt2->demux.priv = cinergyt2; cinergyt2->demux.filternum = 256; @@ -808,8 +926,8 @@ static int cinergyt2_probe (struct usb_interface *intf, cinergyt2->demux.start_feed = cinergyt2_start_feed; cinergyt2->demux.stop_feed = cinergyt2_stop_feed; cinergyt2->demux.dmx.capabilities = DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; if ((err = dvb_dmx_init(&cinergyt2->demux)) < 0) { dprintk(1, "dvb_dmx_init() failed (err = %d)\n", err); @@ -820,43 +938,30 @@ static int cinergyt2_probe (struct usb_interface *intf, cinergyt2->dmxdev.demux = &cinergyt2->demux.dmx; cinergyt2->dmxdev.capabilities = 0; - if ((err = dvb_dmxdev_init(&cinergyt2->dmxdev, cinergyt2->adapter)) < 0) { + if ((err = dvb_dmxdev_init(&cinergyt2->dmxdev, &cinergyt2->adapter)) < 0) { dprintk(1, "dvb_dmxdev_init() failed (err = %d)\n", err); goto bailout; } - if (dvb_net_init(cinergyt2->adapter, &cinergyt2->dvbnet, &cinergyt2->demux.dmx)) + if (dvb_net_init(&cinergyt2->adapter, &cinergyt2->dvbnet, &cinergyt2->demux.dmx)) dprintk(1, "dvb_net_init() failed!\n"); - dvb_register_device(cinergyt2->adapter, &cinergyt2->fedev, + dvb_register_device(&cinergyt2->adapter, &cinergyt2->fedev, &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; irc_input_dev.keybit); - - input_register_device(&cinergyt2->rc_input_dev); + err = cinergyt2_register_rc(cinergyt2); + if (err) + goto bailout; - cinergyt2->rc_input_event = KEY_MAX; - - 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; } @@ -865,48 +970,39 @@ static void cinergyt2_disconnect (struct usb_interface *intf) { 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->demux.dmx.close(&cinergyt2->demux.dmx); - dvb_net_release(&cinergyt2->dvbnet); - dvb_dmxdev_release(&cinergyt2->dmxdev); - dvb_dmx_release(&cinergyt2->demux); + cinergyt2_unregister_rc(cinergyt2); - dvb_unregister_device(cinergyt2->fedev); - dvb_unregister_adapter(cinergyt2->adapter); + cancel_delayed_work(&cinergyt2->query_work); + wake_up_interruptible(&cinergyt2->poll_wq); - cinergyt2_free_stream_urbs(cinergyt2); - up(&cinergyt2->sem); - kfree(cinergyt2); + cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx); + cinergyt2->disconnect_pending = 1; + + if (!atomic_read(&cinergyt2->inuse)) + cinergyt2_unregister(cinergyt2); } -static int cinergyt2_suspend (struct usb_interface *intf, u32 state) +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 (1) { 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_stop_stream_xfer(cinergyt2); flush_scheduled_work(); cinergyt2_sleep(cinergyt2, 1); } - up(&cinergyt2->sem); + mutex_unlock(&cinergyt2->sem); return 0; } @@ -915,21 +1011,20 @@ static int cinergyt2_resume (struct usb_interface *intf) 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) { cinergyt2_sleep(cinergyt2, 0); cinergyt2_command(cinergyt2, (char *) param, sizeof(*param), NULL, 0); if (cinergyt2->streaming) - cinergyt2_start_stream_xfer(cinergyt2); + cinergyt2_start_stream_xfer(cinergyt2); 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; } @@ -941,13 +1036,12 @@ static const struct usb_device_id cinergyt2_table [] __devinitdata = { MODULE_DEVICE_TABLE(usb, cinergyt2_table); static struct usb_driver cinergyt2_driver = { - .owner = THIS_MODULE, .name = "cinergyT2", - .probe = cinergyt2_probe, - .disconnect = cinergyt2_disconnect, + .probe = cinergyt2_probe, + .disconnect = cinergyt2_disconnect, .suspend = cinergyt2_suspend, .resume = cinergyt2_resume, - .id_table = cinergyt2_table + .id_table = cinergyt2_table }; static int __init cinergyt2_init (void) @@ -970,4 +1064,3 @@ module_exit (cinergyt2_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Holger Waechtler, Daniel Mack"); -