Every input-handler-backend like evdev and joydev were allocated 32 minor
numbers for historical reasons. This is a very low limit for modern linux
desktops and prevents new technologies like multi-seat from becoming more
useful.

This introduces four new global helpers that allow input-handler-backends
to allocate minors dynamically. New backends can even drop any
static-minor support and allocate all minors dynamically through this API.

All minors that are available beyond the minors-range used for static
allocations can be allocated by this API. The maximum number of devices is
still limited by INPUT_DEVICES+register_chrdev() but can now be extended
to increase the dynamic-minors range.

This patch is fully backwards-compatible and all handlers can be converted
to use this API without breaking backwards-compatiblity. However, new
devices using dynamically allocated minor numbers might not be visible to
old user-space programs that do not use libudev or similar.

Signed-off-by: David Herrmann <dh.herrm...@googlemail.com>
---
 drivers/input/input.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/input.h |   5 ++
 2 files changed, 166 insertions(+), 1 deletion(-)

diff --git a/drivers/input/input.c b/drivers/input/input.c
index 8921c61..2741ce1 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -45,7 +45,14 @@ static LIST_HEAD(input_handler_list);
  */
 static DEFINE_MUTEX(input_mutex);
 
+/*
+ * Please note that everything beyond the size of this array is used for
+ * dynamic minor allocations. Please also avoid adding new users to this array.
+ * Instead of relying on static minor-allocations, you should use dynamic 
minors
+ * exlusively. See input_minor_alloc().
+ */
 static struct input_handler *input_table[8];
+#define INPUT_TABLE_SIZE ARRAY_SIZE(input_table)
 
 static inline int is_event_supported(unsigned int code,
                                     unsigned long *bm, unsigned int max)
@@ -2090,18 +2097,171 @@ void input_unregister_handle(struct input_handle 
*handle)
 }
 EXPORT_SYMBOL(input_unregister_handle);
 
+/*
+ * Dynamic Minors
+ * Historically, each handler-backend gets 32 minors allocated. This was enough
+ * in old times, but today we often want more input devices. Therefore, if you
+ * run out of minors, you can request new dynamic minors from the input core.
+ * These are above an upper limit so they do not collide with static minors.
+ * Furthermore, you can save an arbitrary data pointer with them so you don't
+ * have to keep a list of dynamic minors yourself.
+ *
+ * There can be up to INPUT_TABLE_SIZE static minor users with 32 minors for
+ * each. Therefore, we start allocating dynamic minors beyond
+ * INPUT_TABLE_SIZE << 5. But we still must make sure we are below 
INPUT_DEVICES
+ * which is the upper limit and maximum minor size that we allocate on startup.
+ */
+
+#define INPUT_MINOR_DYNAMIC_START (INPUT_TABLE_SIZE << 5)
+
+struct input_minor {
+       struct input_handler *handler;
+       void *data;
+};
+
+static DEFINE_MUTEX(dynamic_minors_lock);
+static size_t dynamic_minors_size;
+static struct input_minor *dynamic_minors;
+
+int input_minor_alloc(struct input_handler *handler, void *data)
+{
+       struct input_minor *narray;
+       unsigned int i, nsize;
+       int minor = -1, ret;
+
+       if (!handler)
+               return -EINVAL;
+
+       mutex_lock(&dynamic_minors_lock);
+
+       for (i = 0; i < dynamic_minors_size; ++i) {
+               if (!dynamic_minors[i].handler) {
+                       minor = i;
+                       break;
+               }
+       }
+
+       if (minor < 0) {
+               nsize = dynamic_minors_size * 2;
+               if (!nsize)
+                       nsize = 32;
+               narray = krealloc(dynamic_minors,
+                                 nsize * sizeof(*dynamic_minors),
+                                 GFP_KERNEL);
+               if (!narray) {
+                       ret = -ENOMEM;
+                       goto out_unlock;
+               }
+
+               memset(&narray[dynamic_minors_size], 0,
+                      sizeof(*dynamic_minors) * (nsize - dynamic_minors_size));
+
+               minor = dynamic_minors_size;
+               dynamic_minors = narray;
+               dynamic_minors_size = nsize;
+       }
+
+       ret = minor + INPUT_MINOR_DYNAMIC_START;
+       if (ret >= INPUT_DEVICES) {
+               ret = -ENFILE;
+               goto out_unlock;
+       }
+
+       dynamic_minors[minor].handler = handler;
+       dynamic_minors[minor].data = data;
+
+out_unlock:
+       mutex_unlock(&dynamic_minors_lock);
+       return ret;
+}
+EXPORT_SYMBOL(input_minor_alloc);
+
+void input_minor_free(int minor)
+{
+       if (minor < INPUT_MINOR_DYNAMIC_START)
+               return;
+
+       mutex_lock(&dynamic_minors_lock);
+
+       minor -= INPUT_MINOR_DYNAMIC_START;
+       if (minor >= dynamic_minors_size)
+               goto out_unlock;
+
+       dynamic_minors[minor].handler = NULL;
+       dynamic_minors[minor].data = NULL;
+
+out_unlock:
+       mutex_unlock(&dynamic_minors_lock);
+}
+EXPORT_SYMBOL(input_minor_free);
+
+void *input_minor_get_data(int minor)
+{
+       void *res;
+
+       if (minor < INPUT_MINOR_DYNAMIC_START)
+               return NULL;
+
+       mutex_lock(&dynamic_minors_lock);
+
+       minor -= INPUT_MINOR_DYNAMIC_START;
+       if (minor >= dynamic_minors_size) {
+               res = NULL;
+               goto out_unlock;
+       }
+
+       res = dynamic_minors[minor].data;
+
+out_unlock:
+       mutex_unlock(&dynamic_minors_lock);
+       return res;
+}
+EXPORT_SYMBOL(input_minor_get_data);
+
+struct input_handler *input_minor_get_handler(int minor)
+{
+       void *res;
+
+       if (minor < INPUT_MINOR_DYNAMIC_START)
+               return NULL;
+
+       mutex_lock(&dynamic_minors_lock);
+
+       minor -= INPUT_MINOR_DYNAMIC_START;
+       if (minor >= dynamic_minors_size) {
+               res = NULL;
+               goto out_unlock;
+       }
+
+       res = dynamic_minors[minor].handler;
+
+out_unlock:
+       mutex_unlock(&dynamic_minors_lock);
+       return res;
+}
+EXPORT_SYMBOL(input_minor_get_handler);
+
 static int input_open_file(struct inode *inode, struct file *file)
 {
        struct input_handler *handler;
        const struct file_operations *old_fops, *new_fops = NULL;
        int err;
+       unsigned int minor, minor_group;
 
        err = mutex_lock_interruptible(&input_mutex);
        if (err)
                return err;
 
        /* No load-on-demand here? */
-       handler = input_table[iminor(inode) >> 5];
+
+       minor = iminor(inode);
+       minor_group = minor >> 5;
+
+       if (minor_group < INPUT_TABLE_SIZE)
+               handler = input_table[minor_group];
+       else
+               handler = input_minor_get_handler(minor);
+
        if (handler)
                new_fops = fops_get(handler->fops);
 
diff --git a/include/linux/input.h b/include/linux/input.h
index 2740d08..3fa3d7b 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -1486,6 +1486,11 @@ void input_reset_device(struct input_dev *);
 int __must_check input_register_handler(struct input_handler *);
 void input_unregister_handler(struct input_handler *);
 
+int input_minor_alloc(struct input_handler *, void *);
+void input_minor_free(int);
+void *input_minor_get_data(int);
+struct input_handler *input_minor_get_handler(int);
+
 int input_handler_for_each_handle(struct input_handler *, void *data,
                                  int (*fn)(struct input_handle *, void *));
 
-- 
1.7.12

--
To unsubscribe from this list: send the line "unsubscribe linux-input" 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