Instead of one virtual input device which exists for the whole lifetime of the driver and receives events from all connected FireDTVs, register one input device for each firedtv device. These input devices will show up as children of the respective firedtv devices in the sysfs hierarchy. However, the implementation falls short because of a bug in userspace: Udev's path_id script gets stuck with 100% CPU utilization, maybe because of an assumption about the maximum ieee1394 device hierarchy depth. To avoid this bug, we use the fw-host device instead of the proper unit_directory device as parent of the input device. There is hope that the port to the new firewire stack won't be inhibited by this userspace bug because there are no fw-host devices there. Signed-off-by: Stefan Richter <stefanr at s5r6.in-berlin.de> --- drivers/media/dvb/firesat/avc_api.c | 4 +- drivers/media/dvb/firesat/firesat-rc.c | 42 +++++++++++++++-------- drivers/media/dvb/firesat/firesat-rc.h | 9 +++- drivers/media/dvb/firesat/firesat.h | 5 +- drivers/media/dvb/firesat/firesat_1394.c | 26 ++++++-------- 5 files changed, 52 insertions(+), 34 deletions(-) Index: linux/drivers/media/dvb/firesat/avc_api.c =================================================================== --- linux.orig/drivers/media/dvb/firesat/avc_api.c +++ linux/drivers/media/dvb/firesat/avc_api.c @@ -225,8 +225,8 @@ int AVCRecv(struct firesat *firesat, u8 RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { if (RspFrm->resp == CHANGED) { - firesat_handle_rc(RspFrm->operand[4] << 8 | - RspFrm->operand[5]); + firesat_handle_rc(firesat, + RspFrm->operand[4] << 8 | RspFrm->operand[5]); schedule_work(&firesat->remote_ctrl_work); } else if (RspFrm->resp != INTERIM) { dev_info(&firesat->ud->device, Index: linux/drivers/media/dvb/firesat/firesat-rc.c =================================================================== --- linux.orig/drivers/media/dvb/firesat/firesat-rc.c +++ linux/drivers/media/dvb/firesat/firesat-rc.c @@ -12,9 +12,11 @@ #include <linux/bitops.h> #include <linux/input.h> #include <linux/kernel.h> +#include <linux/string.h> #include <linux/types.h> #include "firesat-rc.h" +#include "firesat.h" /* fixed table with older keycodes, geared towards MythTV */ const static u16 oldtable[] = { @@ -61,7 +63,7 @@ const static u16 oldtable[] = { }; /* user-modifiable table for a remote as sold in 2008 */ -static u16 keytable[] = { +const static u16 keytable[] = { /* code from device: 0x0300...0x031f */ @@ -123,19 +125,24 @@ static u16 keytable[] = { [0x34] = KEY_EXIT, }; -static struct input_dev *idev; - -int firesat_register_rc(void) +int firesat_register_rc(struct firesat *firesat, struct device *dev) { + struct input_dev *idev; int i, err; idev = input_allocate_device(); if (!idev) return -ENOMEM; + firesat->remote_ctrl_dev = idev; idev->name = "FireDTV remote control"; + idev->dev.parent = dev; idev->evbit[0] = BIT_MASK(EV_KEY); - idev->keycode = keytable; + idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL); + if (!idev->keycode) { + err = -ENOMEM; + goto fail; + } idev->keycodesize = sizeof(keytable[0]); idev->keycodemax = ARRAY_SIZE(keytable); @@ -144,22 +151,31 @@ int firesat_register_rc(void) err = input_register_device(idev); if (err) - input_free_device(idev); + goto fail_free_keymap; + return 0; + +fail_free_keymap: + kfree(idev->keycode); +fail: + input_free_device(idev); return err; } -void firesat_unregister_rc(void) +void firesat_unregister_rc(struct firesat *firesat) { - input_unregister_device(idev); + kfree(firesat->remote_ctrl_dev->keycode); + input_unregister_device(firesat->remote_ctrl_dev); } -void firesat_handle_rc(unsigned int code) +void firesat_handle_rc(struct firesat *firesat, unsigned int code) { + u16 *keycode = firesat->remote_ctrl_dev->keycode; + if (code >= 0x0300 && code <= 0x031f) - code = keytable[code - 0x0300]; + code = keycode[code - 0x0300]; else if (code >= 0x0340 && code <= 0x0354) - code = keytable[code - 0x0320]; + code = keycode[code - 0x0320]; else if (code >= 0x4501 && code <= 0x451f) code = oldtable[code - 0x4501]; else if (code >= 0x4540 && code <= 0x4542) @@ -170,6 +186,6 @@ void firesat_handle_rc(unsigned int code return; } - input_report_key(idev, code, 1); - input_report_key(idev, code, 0); + input_report_key(firesat->remote_ctrl_dev, code, 1); + input_report_key(firesat->remote_ctrl_dev, code, 0); } Index: linux/drivers/media/dvb/firesat/firesat-rc.h =================================================================== --- linux.orig/drivers/media/dvb/firesat/firesat-rc.h +++ linux/drivers/media/dvb/firesat/firesat-rc.h @@ -1,8 +1,11 @@ #ifndef _FIREDTV_RC_H #define _FIREDTV_RC_H -int firesat_register_rc(void); -void firesat_unregister_rc(void); -void firesat_handle_rc(unsigned int code); +struct firesat; +struct device; + +int firesat_register_rc(struct firesat *firesat, struct device *dev); +void firesat_unregister_rc(struct firesat *firesat); +void firesat_handle_rc(struct firesat *firesat, unsigned int code); #endif /* _FIREDTV_RC_H */ Index: linux/drivers/media/dvb/firesat/firesat.h =================================================================== --- linux.orig/drivers/media/dvb/firesat/firesat.h +++ linux/drivers/media/dvb/firesat/firesat.h @@ -127,7 +127,7 @@ enum model_type { FireSAT_DVB_S2 = 4, }; -struct hpsb_host; +struct input_dev; struct hpsb_iso; struct unit_directory; @@ -147,12 +147,13 @@ struct firesat { wait_queue_head_t avc_wait; bool avc_reply_received; struct work_struct remote_ctrl_work; + struct input_dev *remote_ctrl_dev; struct firesat_channel { bool active; int pid; } channel[16]; - struct mutex demux_mutex; + struct mutex demux_mutex; struct unit_directory *ud; Index: linux/drivers/media/dvb/firesat/firesat_1394.c =================================================================== --- linux.orig/drivers/media/dvb/firesat/firesat_1394.c +++ linux/drivers/media/dvb/firesat/firesat_1394.c @@ -176,6 +176,14 @@ static int firesat_probe(struct device * break; firesat->type = i; + /* + * Work around a bug in udev's path_id script: Use the fw-host's dev + * instead of the unit directory's dev as parent of the input device. + */ + err = firesat_register_rc(firesat, dev->parent->parent); + if (err) + goto fail_free; + INIT_LIST_HEAD(&firesat->list); spin_lock_irqsave(&firesat_list_lock, flags); list_add_tail(&firesat->list, &firesat_list); @@ -196,6 +204,8 @@ fail: spin_lock_irqsave(&firesat_list_lock, flags); list_del(&firesat->list); spin_unlock_irqrestore(&firesat_list_lock, flags); + firesat_unregister_rc(firesat); +fail_free: kfree(firesat); return err; } @@ -220,6 +230,7 @@ static int firesat_remove(struct device spin_unlock_irqrestore(&firesat_list_lock, flags); cancel_work_sync(&firesat->remote_ctrl_work); + firesat_unregister_rc(firesat); kfree(firesat); return 0; @@ -263,26 +274,13 @@ static int __init firesat_init(void) ret = hpsb_register_protocol(&firesat_driver); if (ret) { printk(KERN_ERR "firedtv: failed to register protocol\n"); - goto fail; - } - - ret = firesat_register_rc(); - if (ret) { - printk(KERN_ERR "firedtv: failed to register input device\n"); - goto fail_rc; + hpsb_unregister_highlevel(&firesat_highlevel); } - - return 0; -fail_rc: - hpsb_unregister_protocol(&firesat_driver); -fail: - hpsb_unregister_highlevel(&firesat_highlevel); return ret; } static void __exit firesat_exit(void) { - firesat_unregister_rc(); hpsb_unregister_protocol(&firesat_driver); hpsb_unregister_highlevel(&firesat_highlevel); } -- Stefan Richter -=====-==--- =--= ===-= http://arcgraph.de/sr/