This is another step towards allowing multiple keytables per rc_dev.

struct rc_dev is changed to hold an array of keytables (used later for
indexed access to keytables) as well as a list of the same keytables
(used for iteration in fast paths).

Signed-off-by: David Härdeman <da...@hardeman.nu>
---
 drivers/media/rc/rc-core-priv.h |    4 +-
 drivers/media/rc/rc-keytable.c  |   95 ++++++++++++++++++++++++---------------
 drivers/media/rc/rc-main.c      |   74 ++++++++++++++++++++++++++++--
 include/media/rc-core.h         |    6 ++
 4 files changed, 134 insertions(+), 45 deletions(-)

diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 7a7770e..02b538c 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -162,8 +162,8 @@ void rc_keytable_keyup(struct rc_keytable *kt);
 void rc_keytable_repeat(struct rc_keytable *kt);
 void rc_keytable_keydown(struct rc_keytable *kt, enum rc_type protocol,
                         u32 scancode, u8 toggle, bool autokeyup);
-int rc_keytable_add(struct rc_dev *dev, struct rc_map *rc_map);
-void rc_keytable_del(struct rc_dev *dev);
+struct rc_keytable *rc_keytable_create(struct rc_dev *dev, struct rc_map 
*rc_map);
+void rc_keytable_destroy(struct rc_keytable *kt);
 
 /* Only to be used by rc-core and ir-lirc-codec */
 void rc_init_ir_rx(struct rc_dev *dev, struct rc_ir_rx *rx);
diff --git a/drivers/media/rc/rc-keytable.c b/drivers/media/rc/rc-keytable.c
index 0f1b817..412d342 100644
--- a/drivers/media/rc/rc-keytable.c
+++ b/drivers/media/rc/rc-keytable.c
@@ -322,7 +322,7 @@ static unsigned int ir_establish_scancode(struct 
rc_keytable *kt,
  */
 static inline enum rc_type guess_protocol(struct rc_dev *rdev)
 {
-       struct rc_map *rc_map = &rdev->kt->rc_map;
+       struct rc_map *rc_map = &rdev->keytables[0]->rc_map;
 
        if (hweight64(rdev->enabled_protocols) == 1)
                return rc_bitmap_to_type(rdev->enabled_protocols);
@@ -604,40 +604,63 @@ out:
        return retval;
 }
 
-/**
- * rc_g_keycode_from_table() - gets the keycode that corresponds to a scancode
- * @dev:       the struct rc_dev descriptor of the device
- * @protocol:  the protocol to look for
- * @scancode:  the scancode to look for
- * @return:    the corresponding keycode, or KEY_RESERVED
- *
- * This routine is used by drivers which need to convert a scancode to a
- * keycode. Normally it should not be used since drivers should have no
- * interest in keycodes.
- */
-u32 rc_g_keycode_from_table(struct rc_dev *dev,
-                           enum rc_type protocol, u64 scancode)
+static u32 rc_get_keycode(struct rc_keytable *kt,
+                         enum rc_type protocol, u64 scancode)
 {
-       struct rc_map *rc_map = &dev->kt->rc_map;
-       unsigned int keycode;
+       struct rc_map *rc_map;
+       unsigned int keycode = KEY_RESERVED;
        unsigned int index;
        unsigned long flags;
 
+       rc_map = &kt->rc_map;
+       if (!rc_map)
+               return KEY_RESERVED;
+
        spin_lock_irqsave(&rc_map->lock, flags);
 
        index = ir_lookup_by_scancode(rc_map, protocol, scancode);
-       keycode = index < rc_map->len ?
-                       rc_map->scan[index].keycode : KEY_RESERVED;
+       if (index < rc_map->len)
+               keycode = rc_map->scan[index].keycode;
 
        spin_unlock_irqrestore(&rc_map->lock, flags);
 
        if (keycode != KEY_RESERVED)
                IR_dprintk(1, "%s: protocol 0x%04x scancode 0x%08llx keycode 
0x%02x\n",
-                          dev->input_name, protocol,
+                          kt->dev->input_name, protocol,
                           (unsigned long long)scancode, keycode);
 
        return keycode;
 }
+
+
+/**
+ * rc_g_keycode_from_table() - gets the keycode that corresponds to a scancode
+ * @dev:       the struct rc_dev descriptor of the device
+ * @protocol:  the protocol to look for
+ * @scancode:  the scancode to look for
+ * @return:    the corresponding keycode, or KEY_RESERVED
+ *
+ * This routine is used by drivers which need to convert a scancode to a
+ * keycode. It should not be used since drivers should have no
+ * interest in keycodes. (deprecated)
+ */
+u32 rc_g_keycode_from_table(struct rc_dev *dev,
+                           enum rc_type protocol, u64 scancode)
+{
+       struct rc_keytable *kt;
+       unsigned int keycode = KEY_RESERVED;
+
+       /* FIXME: This entire function is a hack. Remove it */
+       rcu_read_lock();
+       kt = rcu_dereference(dev->keytables[0]);
+       if (!kt)
+               goto out;
+       keycode = rc_get_keycode(kt, protocol, scancode);
+
+out:
+       rcu_read_unlock();
+       return keycode;
+}
 EXPORT_SYMBOL_GPL(rc_g_keycode_from_table);
 
 /**
@@ -751,7 +774,7 @@ void rc_keytable_keydown(struct rc_keytable *kt, enum 
rc_type protocol,
 
        spin_lock_irqsave(&kt->keylock, flags);
 
-       keycode = rc_g_keycode_from_table(kt->dev, protocol, scancode);
+       keycode = rc_get_keycode(kt, protocol, scancode);
        new_event = (!kt->keypressed ||
                     kt->last_protocol != protocol ||
                     kt->last_scancode != scancode ||
@@ -800,16 +823,16 @@ static void rc_input_close(struct input_dev *idev)
 }
 
 /**
- * rc_keytable_add() - adds a new keytable
+ * rc_keytable_create() - create a new keytable
  * @dev:       the struct rc_dev device this keytable should belong to
  * @rc_map:    the keymap to use for the new keytable
  * @return:    zero on success or a negative error code
  *
- * This function add a new keytable (essentially the combination of a keytable
- * and an input device along with some state (whether a key is currently
- * pressed or not, etc).
+ * This function creates a new keytable (essentially the combination of a
+ * keytable and an input device along with some state (whether a key is
+ * currently pressed or not, etc).
  */
-int rc_keytable_add(struct rc_dev *dev, struct rc_map *rc_map)
+struct rc_keytable *rc_keytable_create(struct rc_dev *dev, struct rc_map 
*rc_map)
 {
        struct rc_keytable *kt;
        struct input_dev *idev = NULL;
@@ -870,31 +893,29 @@ int rc_keytable_add(struct rc_dev *dev, struct rc_map 
*rc_map)
         */
        idev->rep[REP_PERIOD] = 125;
 
-       dev->kt = kt;
-       return 0;
+       return kt;
 
 out:
        ir_free_table(&kt->rc_map);
        input_free_device(idev);
        kfree(kt);
-       return err;
+       return ERR_PTR(err);
 }
 
 /**
- * rc_keytable_del() - unregisters and deletes a keytable
- * @dev:       the struct rc_dev device with the keytable
+ * rc_keytable_del() - unregisters and frees a keytable
+ * @kt:                the struct rc_keytable to destroy
  *
  * This function unregisters and deletes an existing keytable.
  */
-void rc_keytable_del(struct rc_dev *dev)
+void rc_keytable_destroy(struct rc_keytable *kt)
 {
-       if (!dev->kt)
+       if (!kt)
                return;
 
-       del_timer_sync(&dev->kt->timer_keyup);
-       ir_free_table(&dev->kt->rc_map);
-       input_unregister_device(dev->kt->idev);
-       kfree(dev->kt);
-       dev->kt = NULL;
+       del_timer_sync(&kt->timer_keyup);
+       ir_free_table(&kt->rc_map);
+       input_unregister_device(kt->idev);
+       kfree(kt);
 }
 
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 23a6701..bc2d479 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -150,8 +150,14 @@ EXPORT_SYMBOL_GPL(rc_close);
 void rc_do_keydown(struct rc_dev *dev, enum rc_type protocol,
                   u32 scancode, u8 toggle, bool autoup)
 {
+       struct rc_keytable *kt;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(kt, &dev->keytable_list, node)
+               rc_keytable_keydown(kt, protocol, scancode, toggle, autoup);
+       rcu_read_unlock();
+
        led_trigger_event(led_feedback, LED_FULL);
-       rc_keytable_keydown(dev->kt, protocol, scancode, toggle, autoup);
        rc_event(dev, RC_KEY, RC_KEY_PROTOCOL, protocol);
        rc_event(dev, RC_KEY, RC_KEY_SCANCODE, scancode);
        rc_event(dev, RC_KEY, RC_KEY_TOGGLE, toggle);
@@ -166,7 +172,12 @@ EXPORT_SYMBOL_GPL(rc_do_keydown);
  */
 void rc_keyup(struct rc_dev *dev)
 {
-       rc_keytable_keyup(dev->kt);
+       struct rc_keytable *kt;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(kt, &dev->keytable_list, node)
+               rc_keytable_keyup(kt);
+       rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(rc_keyup);
 
@@ -179,11 +190,60 @@ EXPORT_SYMBOL_GPL(rc_keyup);
  */
 void rc_repeat(struct rc_dev *dev)
 {
-       rc_keytable_repeat(dev->kt);
+       struct rc_keytable *kt;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(kt, &dev->keytable_list, node)
+               rc_keytable_repeat(kt);
+       rcu_read_unlock();
+
        rc_event(dev, RC_KEY, RC_KEY_REPEAT, 1);
 }
 EXPORT_SYMBOL_GPL(rc_repeat);
 
+static int rc_add_keytable(struct rc_dev *dev, struct rc_map *rc_map)
+{
+       struct rc_keytable *kt;
+       unsigned i;
+
+       if (!rc_map)
+               rc_map = rc_map_get(RC_MAP_EMPTY);
+
+       if (!rc_map)
+               return -EFAULT;
+
+       for (i = 0; i < ARRAY_SIZE(dev->keytables); i++)
+               if (!dev->keytables[i])
+                       break;
+
+       if (i >= ARRAY_SIZE(dev->keytables))
+               return -ENFILE;
+
+       kt = rc_keytable_create(dev, rc_map);
+       if (IS_ERR(kt))
+               return PTR_ERR(kt);
+
+       rcu_assign_pointer(dev->keytables[i], kt);
+       list_add_rcu(&kt->node, &dev->keytable_list);
+       synchronize_rcu();
+       return 0;
+}
+
+static void rc_remove_keytable(struct rc_dev *dev, unsigned i)
+{
+       struct rc_keytable *kt;
+
+       if (i >= ARRAY_SIZE(dev->keytables))
+               return;
+
+       kt = dev->keytables[i];
+       rcu_assign_pointer(dev->keytables[i], NULL);
+       if (kt)
+               list_del_rcu(&kt->node);
+       synchronize_rcu();
+       rc_keytable_destroy(kt);
+}
+
 /* class for /sys/class/rc */
 static char *rc_devnode(struct device *dev, umode_t *mode)
 {
@@ -1108,6 +1168,7 @@ struct rc_dev *rc_allocate_device(void)
 
        INIT_LIST_HEAD(&dev->client_list);
        spin_lock_init(&dev->client_lock);
+       INIT_LIST_HEAD(&dev->keytable_list);
        mutex_init(&dev->txmutex);
        init_waitqueue_head(&dev->txwait);
        init_waitqueue_head(&dev->rxwait);
@@ -1215,7 +1276,7 @@ int rc_register_device(struct rc_dev *dev)
        if (rc)
                goto out_cdev;
 
-       rc = rc_keytable_add(dev, rc_map);
+       rc = rc_add_keytable(dev, rc_map);
        if (rc)
                goto out_dev;
 
@@ -1243,6 +1304,7 @@ EXPORT_SYMBOL_GPL(rc_register_device);
 void rc_unregister_device(struct rc_dev *dev)
 {
        struct rc_client *client;
+       unsigned i;
 
        if (!dev)
                return;
@@ -1263,7 +1325,8 @@ void rc_unregister_device(struct rc_dev *dev)
        if (dev->driver_type == RC_DRIVER_IR_RAW)
                ir_raw_event_unregister(dev);
 
-       rc_keytable_del(dev);
+       for (i = 0; i < ARRAY_SIZE(dev->keytables); i++)
+               rc_remove_keytable(dev, i);
 
        /* dev is marked as dead so no one changes dev->users */
        if (dev->users && dev->close)
@@ -1329,3 +1392,4 @@ module_param_named(debug, rc_core_debug, int, 0644);
 
 MODULE_AUTHOR("Mauro Carvalho Chehab");
 MODULE_LICENSE("GPL");
+
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index e64d47c..f48d5cd 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -303,6 +303,7 @@ enum rc_filter_type {
  * @get_ir_tx: allow driver to provide tx settings
  * @set_ir_tx: allow driver to change tx settings
  */
+#define RC_MAX_KEYTABLES               1
 struct rc_dev {
        struct device                   dev;
        struct cdev                     cdev;
@@ -312,7 +313,8 @@ struct rc_dev {
        struct input_id                 input_id;
        char                            *driver_name;
        const char                      *map_name;
-       struct rc_keytable              *kt;
+       struct rc_keytable              *keytables[RC_MAX_KEYTABLES];
+       struct list_head                keytable_list;
        struct mutex                    lock;
        bool                            dead;
        struct list_head                client_list;
@@ -362,6 +364,7 @@ struct rc_dev {
 
 /**
  * struct rc_keytable - represents one keytable for a rc_dev device
+ * @node:              used to iterate over all keytables for a rc_dev device
  * @dev:               the rc_dev device this keytable belongs to
  * @idev:              the input_dev device which belongs to this keytable
  * @rc_map:            holds the scancode <-> keycode mappings
@@ -375,6 +378,7 @@ struct rc_dev {
  * @last_toggle:       toggle of the last keypress
  */
 struct rc_keytable {
+       struct list_head                node;
        struct rc_dev                   *dev;
        struct input_dev                *idev;
        struct rc_map                   rc_map;

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to