Commit 7f8d4cad authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: extend the number of event (and other) devices

Extend the amount of character devices, such as eventX, mouseX and jsX,
from a hard limit of 32 per input handler to about 1024 shared across
all handlers.

To be compatible with legacy installations input handlers will start
creating char devices with minors in their legacy range, however once
legacy range is exhausted they will start allocating minors from the
dynamic range 256-1024.
Reviewed-by: default avatarDavid Herrmann <dh.herrmann@googlemail.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 0124be49
...@@ -23,11 +23,11 @@ ...@@ -23,11 +23,11 @@
#include <linux/input/mt.h> #include <linux/input/mt.h>
#include <linux/major.h> #include <linux/major.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/cdev.h>
#include "input-compat.h" #include "input-compat.h"
struct evdev { struct evdev {
int open; int open;
int minor;
struct input_handle handle; struct input_handle handle;
wait_queue_head_t wait; wait_queue_head_t wait;
struct evdev_client __rcu *grab; struct evdev_client __rcu *grab;
...@@ -35,6 +35,7 @@ struct evdev { ...@@ -35,6 +35,7 @@ struct evdev {
spinlock_t client_lock; /* protects client_list */ spinlock_t client_lock; /* protects client_list */
struct mutex mutex; struct mutex mutex;
struct device dev; struct device dev;
struct cdev cdev;
bool exist; bool exist;
}; };
...@@ -51,9 +52,6 @@ struct evdev_client { ...@@ -51,9 +52,6 @@ struct evdev_client {
struct input_event buffer[]; struct input_event buffer[];
}; };
static struct evdev *evdev_table[EVDEV_MINORS];
static DEFINE_MUTEX(evdev_table_mutex);
static void __pass_event(struct evdev_client *client, static void __pass_event(struct evdev_client *client,
const struct input_event *event) const struct input_event *event)
{ {
...@@ -310,35 +308,16 @@ static unsigned int evdev_compute_buffer_size(struct input_dev *dev) ...@@ -310,35 +308,16 @@ static unsigned int evdev_compute_buffer_size(struct input_dev *dev)
static int evdev_open(struct inode *inode, struct file *file) static int evdev_open(struct inode *inode, struct file *file)
{ {
struct evdev *evdev; struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
struct evdev_client *client; struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE;
unsigned int bufsize;
int error; int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
evdev = evdev_table[i];
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if (!evdev)
return -ENODEV;
bufsize = evdev_compute_buffer_size(evdev->handle.dev);
client = kzalloc(sizeof(struct evdev_client) + client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event), bufsize * sizeof(struct input_event),
GFP_KERNEL); GFP_KERNEL);
if (!client) { if (!client)
error = -ENOMEM; return -ENOMEM;
goto err_put_evdev;
}
client->bufsize = bufsize; client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock); spin_lock_init(&client->buffer_lock);
...@@ -352,13 +331,12 @@ static int evdev_open(struct inode *inode, struct file *file) ...@@ -352,13 +331,12 @@ static int evdev_open(struct inode *inode, struct file *file)
file->private_data = client; file->private_data = client;
nonseekable_open(inode, file); nonseekable_open(inode, file);
get_device(&evdev->dev);
return 0; return 0;
err_free_client: err_free_client:
evdev_detach_client(evdev, client); evdev_detach_client(evdev, client);
kfree(client); kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error; return error;
} }
...@@ -942,26 +920,6 @@ static const struct file_operations evdev_fops = { ...@@ -942,26 +920,6 @@ static const struct file_operations evdev_fops = {
.llseek = no_llseek, .llseek = no_llseek,
}; };
static int evdev_install_chrdev(struct evdev *evdev)
{
/*
* No need to do any locking here as calls to connect and
* disconnect are serialized by the input core
*/
evdev_table[evdev->minor] = evdev;
return 0;
}
static void evdev_remove_chrdev(struct evdev *evdev)
{
/*
* Lock evdev table to prevent race with evdev_open()
*/
mutex_lock(&evdev_table_mutex);
evdev_table[evdev->minor] = NULL;
mutex_unlock(&evdev_table_mutex);
}
/* /*
* Mark device non-existent. This disables writes, ioctls and * Mark device non-existent. This disables writes, ioctls and
* prevents new users from opening the device. Already posted * prevents new users from opening the device. Already posted
...@@ -980,7 +938,8 @@ static void evdev_cleanup(struct evdev *evdev) ...@@ -980,7 +938,8 @@ static void evdev_cleanup(struct evdev *evdev)
evdev_mark_dead(evdev); evdev_mark_dead(evdev);
evdev_hangup(evdev); evdev_hangup(evdev);
evdev_remove_chrdev(evdev);
cdev_del(&evdev->cdev);
/* evdev is marked dead so no one else accesses evdev->open */ /* evdev is marked dead so no one else accesses evdev->open */
if (evdev->open) { if (evdev->open) {
...@@ -991,43 +950,47 @@ static void evdev_cleanup(struct evdev *evdev) ...@@ -991,43 +950,47 @@ static void evdev_cleanup(struct evdev *evdev)
/* /*
* Create new evdev device. Note that input core serializes calls * Create new evdev device. Note that input core serializes calls
* to connect and disconnect so we don't need to lock evdev_table here. * to connect and disconnect.
*/ */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id) const struct input_device_id *id)
{ {
struct evdev *evdev; struct evdev *evdev;
int minor; int minor;
int dev_no;
int error; int error;
for (minor = 0; minor < EVDEV_MINORS; minor++) minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (!evdev_table[minor]) if (minor < 0) {
break; error = minor;
pr_err("failed to reserve new minor: %d\n", error);
if (minor == EVDEV_MINORS) { return error;
pr_err("no more free evdev devices\n");
return -ENFILE;
} }
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) if (!evdev) {
return -ENOMEM; error = -ENOMEM;
goto err_free_minor;
}
INIT_LIST_HEAD(&evdev->client_list); INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock); spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex); mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait); init_waitqueue_head(&evdev->wait);
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = true; evdev->exist = true;
evdev->minor = minor;
dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
dev_no -= EVDEV_MINOR_BASE;
dev_set_name(&evdev->dev, "event%d", dev_no);
evdev->handle.dev = input_get_device(dev); evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev); evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler; evdev->handle.handler = handler;
evdev->handle.private = evdev; evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class; evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev; evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free; evdev->dev.release = evdev_free;
...@@ -1037,7 +1000,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -1037,7 +1000,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
if (error) if (error)
goto err_free_evdev; goto err_free_evdev;
error = evdev_install_chrdev(evdev); cdev_init(&evdev->cdev, &evdev_fops);
error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
if (error) if (error)
goto err_unregister_handle; goto err_unregister_handle;
...@@ -1053,6 +1017,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -1053,6 +1017,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
input_unregister_handle(&evdev->handle); input_unregister_handle(&evdev->handle);
err_free_evdev: err_free_evdev:
put_device(&evdev->dev); put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error; return error;
} }
...@@ -1062,6 +1028,7 @@ static void evdev_disconnect(struct input_handle *handle) ...@@ -1062,6 +1028,7 @@ static void evdev_disconnect(struct input_handle *handle)
device_del(&evdev->dev); device_del(&evdev->dev);
evdev_cleanup(evdev); evdev_cleanup(evdev);
input_free_minor(MINOR(evdev->dev.devt));
input_unregister_handle(handle); input_unregister_handle(handle);
put_device(&evdev->dev); put_device(&evdev->dev);
} }
...@@ -1078,7 +1045,7 @@ static struct input_handler evdev_handler = { ...@@ -1078,7 +1045,7 @@ static struct input_handler evdev_handler = {
.events = evdev_events, .events = evdev_events,
.connect = evdev_connect, .connect = evdev_connect,
.disconnect = evdev_disconnect, .disconnect = evdev_disconnect,
.fops = &evdev_fops, .legacy_minors = true,
.minor = EVDEV_MINOR_BASE, .minor = EVDEV_MINOR_BASE,
.name = "evdev", .name = "evdev",
.id_table = evdev_ids, .id_table = evdev_ids,
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/idr.h>
#include <linux/input/mt.h> #include <linux/input/mt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -32,7 +33,9 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); ...@@ -32,7 +33,9 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core"); MODULE_DESCRIPTION("Input core");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#define INPUT_DEVICES 256 #define INPUT_MAX_CHAR_DEVICES 1024
#define INPUT_FIRST_DYNAMIC_DEV 256
static DEFINE_IDA(input_ida);
static LIST_HEAD(input_dev_list); static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list); static LIST_HEAD(input_handler_list);
...@@ -45,8 +48,6 @@ static LIST_HEAD(input_handler_list); ...@@ -45,8 +48,6 @@ static LIST_HEAD(input_handler_list);
*/ */
static DEFINE_MUTEX(input_mutex); static DEFINE_MUTEX(input_mutex);
static struct input_handler *input_table[8];
static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 }; static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
static inline int is_event_supported(unsigned int code, static inline int is_event_supported(unsigned int code,
...@@ -1218,7 +1219,7 @@ static int input_handlers_seq_show(struct seq_file *seq, void *v) ...@@ -1218,7 +1219,7 @@ static int input_handlers_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name); seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name);
if (handler->filter) if (handler->filter)
seq_puts(seq, " (filter)"); seq_puts(seq, " (filter)");
if (handler->fops) if (handler->legacy_minors)
seq_printf(seq, " Minor=%d", handler->minor); seq_printf(seq, " Minor=%d", handler->minor);
seq_putc(seq, '\n'); seq_putc(seq, '\n');
...@@ -2016,22 +2017,14 @@ EXPORT_SYMBOL(input_unregister_device); ...@@ -2016,22 +2017,14 @@ EXPORT_SYMBOL(input_unregister_device);
int input_register_handler(struct input_handler *handler) int input_register_handler(struct input_handler *handler)
{ {
struct input_dev *dev; struct input_dev *dev;
int retval; int error;
retval = mutex_lock_interruptible(&input_mutex); error = mutex_lock_interruptible(&input_mutex);
if (retval) if (error)
return retval; return error;
INIT_LIST_HEAD(&handler->h_list); INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
list_add_tail(&handler->node, &input_handler_list); list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node) list_for_each_entry(dev, &input_dev_list, node)
...@@ -2039,9 +2032,8 @@ int input_register_handler(struct input_handler *handler) ...@@ -2039,9 +2032,8 @@ int input_register_handler(struct input_handler *handler)
input_wakeup_procfs_readers(); input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex); mutex_unlock(&input_mutex);
return retval; return 0;
} }
EXPORT_SYMBOL(input_register_handler); EXPORT_SYMBOL(input_register_handler);
...@@ -2064,9 +2056,6 @@ void input_unregister_handler(struct input_handler *handler) ...@@ -2064,9 +2056,6 @@ void input_unregister_handler(struct input_handler *handler)
list_del_init(&handler->node); list_del_init(&handler->node);
if (handler->fops != NULL)
input_table[handler->minor >> 5] = NULL;
input_wakeup_procfs_readers(); input_wakeup_procfs_readers();
mutex_unlock(&input_mutex); mutex_unlock(&input_mutex);
...@@ -2183,51 +2172,52 @@ void input_unregister_handle(struct input_handle *handle) ...@@ -2183,51 +2172,52 @@ void input_unregister_handle(struct input_handle *handle)
} }
EXPORT_SYMBOL(input_unregister_handle); EXPORT_SYMBOL(input_unregister_handle);
static int input_open_file(struct inode *inode, struct file *file) /**
* input_get_new_minor - allocates a new input minor number
* @legacy_base: beginning or the legacy range to be searched
* @legacy_num: size of legacy range
* @allow_dynamic: whether we can also take ID from the dynamic range
*
* This function allocates a new device minor for from input major namespace.
* Caller can request legacy minor by specifying @legacy_base and @legacy_num
* parameters and whether ID can be allocated from dynamic range if there are
* no free IDs in legacy range.
*/
int input_get_new_minor(int legacy_base, unsigned int legacy_num,
bool allow_dynamic)
{ {
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
err = mutex_lock_interruptible(&input_mutex);
if (err)
return err;
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5];
if (handler)
new_fops = fops_get(handler->fops);
mutex_unlock(&input_mutex);
/* /*
* That's _really_ odd. Usually NULL ->open means "nothing special", * This function should be called from input handler's ->connect()
* not "no device". Oh, well... * methods, which are serialized with input_mutex, so no additional
* locking is needed here.
*/ */
if (!new_fops || !new_fops->open) { if (legacy_base >= 0) {
fops_put(new_fops); int minor = ida_simple_get(&input_ida,
err = -ENODEV; legacy_base,
goto out; legacy_base + legacy_num,
GFP_KERNEL);
if (minor >= 0 || !allow_dynamic)
return minor;
} }
old_fops = file->f_op; return ida_simple_get(&input_ida,
file->f_op = new_fops; INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES,
GFP_KERNEL);
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
return err;
} }
EXPORT_SYMBOL(input_get_new_minor);
static const struct file_operations input_fops = { /**
.owner = THIS_MODULE, * input_free_minor - release previously allocated minor
.open = input_open_file, * @minor: minor to be released
.llseek = noop_llseek, *
}; * This function releases previously allocated input minor so that it can be
* reused later.
*/
void input_free_minor(unsigned int minor)
{
ida_simple_remove(&input_ida, minor);
}
EXPORT_SYMBOL(input_free_minor);
static int __init input_init(void) static int __init input_init(void)
{ {
...@@ -2243,7 +2233,8 @@ static int __init input_init(void) ...@@ -2243,7 +2233,8 @@ static int __init input_init(void)
if (err) if (err)
goto fail1; goto fail1;
err = register_chrdev(INPUT_MAJOR, "input", &input_fops); err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
if (err) { if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR); pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2; goto fail2;
...@@ -2259,7 +2250,8 @@ static int __init input_init(void) ...@@ -2259,7 +2250,8 @@ static int __init input_init(void)
static void __exit input_exit(void) static void __exit input_exit(void)
{ {
input_proc_exit(); input_proc_exit();
unregister_chrdev(INPUT_MAJOR, "input"); unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES);
class_unregister(&input_class); class_unregister(&input_class);
} }
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/cdev.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Joystick device interfaces"); MODULE_DESCRIPTION("Joystick device interfaces");
...@@ -39,13 +40,13 @@ MODULE_LICENSE("GPL"); ...@@ -39,13 +40,13 @@ MODULE_LICENSE("GPL");
struct joydev { struct joydev {
int open; int open;
int minor;
struct input_handle handle; struct input_handle handle;
wait_queue_head_t wait; wait_queue_head_t wait;
struct list_head client_list; struct list_head client_list;
spinlock_t client_lock; /* protects client_list */ spinlock_t client_lock; /* protects client_list */
struct mutex mutex; struct mutex mutex;
struct device dev; struct device dev;
struct cdev cdev;
bool exist; bool exist;
struct js_corr corr[ABS_CNT]; struct js_corr corr[ABS_CNT];
...@@ -70,9 +71,6 @@ struct joydev_client { ...@@ -70,9 +71,6 @@ struct joydev_client {
struct list_head node; struct list_head node;
}; };
static struct joydev *joydev_table[JOYDEV_MINORS];
static DEFINE_MUTEX(joydev_table_mutex);
static int joydev_correct(int value, struct js_corr *corr) static int joydev_correct(int value, struct js_corr *corr)
{ {
switch (corr->type) { switch (corr->type) {
...@@ -252,30 +250,14 @@ static int joydev_release(struct inode *inode, struct file *file) ...@@ -252,30 +250,14 @@ static int joydev_release(struct inode *inode, struct file *file)
static int joydev_open(struct inode *inode, struct file *file) static int joydev_open(struct inode *inode, struct file *file)
{ {
struct joydev *joydev =
container_of(inode->i_cdev, struct joydev, cdev);
struct joydev_client *client; struct joydev_client *client;
struct joydev *joydev;
int i = iminor(inode) - JOYDEV_MINOR_BASE;
int error; int error;
if (i >= JOYDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&joydev_table_mutex);
if (error)
return error;
joydev = joydev_table[i];
if (joydev)
get_device(&joydev->dev);
mutex_unlock(&joydev_table_mutex);
if (!joydev)
return -ENODEV;
client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);
if (!client) { if (!client)
error = -ENOMEM; return -ENOMEM;
goto err_put_joydev;
}
spin_lock_init(&client->buffer_lock); spin_lock_init(&client->buffer_lock);
client->joydev = joydev; client->joydev = joydev;
...@@ -288,13 +270,12 @@ static int joydev_open(struct inode *inode, struct file *file) ...@@ -288,13 +270,12 @@ static int joydev_open(struct inode *inode, struct file *file)
file->private_data = client; file->private_data = client;
nonseekable_open(inode, file); nonseekable_open(inode, file);
get_device(&joydev->dev);
return 0; return 0;
err_free_client: err_free_client:
joydev_detach_client(joydev, client); joydev_detach_client(joydev, client);
kfree(client); kfree(client);
err_put_joydev:
put_device(&joydev->dev);
return error; return error;
} }
...@@ -742,19 +723,6 @@ static const struct file_operations joydev_fops = { ...@@ -742,19 +723,6 @@ static const struct file_operations joydev_fops = {
.llseek = no_llseek, .llseek = no_llseek,
}; };
static int joydev_install_chrdev(struct joydev *joydev)
{
joydev_table[joydev->minor] = joydev;
return 0;
}
static void joydev_remove_chrdev(struct joydev *joydev)
{
mutex_lock(&joydev_table_mutex);
joydev_table[joydev->minor] = NULL;
mutex_unlock(&joydev_table_mutex);
}
/* /*
* Mark device non-existent. This disables writes, ioctls and * Mark device non-existent. This disables writes, ioctls and
* prevents new users from opening the device. Already posted * prevents new users from opening the device. Already posted
...@@ -773,7 +741,8 @@ static void joydev_cleanup(struct joydev *joydev) ...@@ -773,7 +741,8 @@ static void joydev_cleanup(struct joydev *joydev)
joydev_mark_dead(joydev); joydev_mark_dead(joydev);
joydev_hangup(joydev); joydev_hangup(joydev);
joydev_remove_chrdev(joydev);
cdev_del(&joydev->cdev);
/* joydev is marked dead so no one else accesses joydev->open */ /* joydev is marked dead so no one else accesses joydev->open */
if (joydev->open) if (joydev->open)
...@@ -798,30 +767,33 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -798,30 +767,33 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id) const struct input_device_id *id)
{ {
struct joydev *joydev; struct joydev *joydev;
int i, j, t, minor; int i, j, t, minor, dev_no;
int error; int error;
for (minor = 0; minor < JOYDEV_MINORS; minor++) minor = input_get_new_minor(JOYDEV_MINOR_BASE, JOYDEV_MINORS, true);
if (!joydev_table[minor]) if (minor < 0) {
break; error = minor;
pr_err("failed to reserve new minor: %d\n", error);
if (minor == JOYDEV_MINORS) { return error;
pr_err("no more free joydev devices\n");
return -ENFILE;
} }
joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL); joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL);
if (!joydev) if (!joydev) {
return -ENOMEM; error = -ENOMEM;
goto err_free_minor;
}
INIT_LIST_HEAD(&joydev->client_list); INIT_LIST_HEAD(&joydev->client_list);
spin_lock_init(&joydev->client_lock); spin_lock_init(&joydev->client_lock);
mutex_init(&joydev->mutex); mutex_init(&joydev->mutex);
init_waitqueue_head(&joydev->wait); init_waitqueue_head(&joydev->wait);
dev_set_name(&joydev->dev, "js%d", minor);
joydev->exist = true; joydev->exist = true;
joydev->minor = minor;
dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < JOYDEV_MINOR_BASE + JOYDEV_MINORS)
dev_no -= JOYDEV_MINOR_BASE;
dev_set_name(&joydev->dev, "js%d", dev_no);
joydev->handle.dev = input_get_device(dev); joydev->handle.dev = input_get_device(dev);
joydev->handle.name = dev_name(&joydev->dev); joydev->handle.name = dev_name(&joydev->dev);
...@@ -875,7 +847,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -875,7 +847,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
} }
} }
joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); joydev->dev.devt = MKDEV(INPUT_MAJOR, minor);
joydev->dev.class = &input_class; joydev->dev.class = &input_class;
joydev->dev.parent = &dev->dev; joydev->dev.parent = &dev->dev;
joydev->dev.release = joydev_free; joydev->dev.release = joydev_free;
...@@ -885,7 +857,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -885,7 +857,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
if (error) if (error)
goto err_free_joydev; goto err_free_joydev;
error = joydev_install_chrdev(joydev); cdev_init(&joydev->cdev, &joydev_fops);
error = cdev_add(&joydev->cdev, joydev->dev.devt, 1);
if (error) if (error)
goto err_unregister_handle; goto err_unregister_handle;
...@@ -901,6 +874,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -901,6 +874,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
input_unregister_handle(&joydev->handle); input_unregister_handle(&joydev->handle);
err_free_joydev: err_free_joydev:
put_device(&joydev->dev); put_device(&joydev->dev);
err_free_minor:
input_free_minor(minor);
return error; return error;
} }
...@@ -910,6 +885,7 @@ static void joydev_disconnect(struct input_handle *handle) ...@@ -910,6 +885,7 @@ static void joydev_disconnect(struct input_handle *handle)
device_del(&joydev->dev); device_del(&joydev->dev);
joydev_cleanup(joydev); joydev_cleanup(joydev);
input_free_minor(MINOR(joydev->dev.devt));
input_unregister_handle(handle); input_unregister_handle(handle);
put_device(&joydev->dev); put_device(&joydev->dev);
} }
...@@ -961,7 +937,7 @@ static struct input_handler joydev_handler = { ...@@ -961,7 +937,7 @@ static struct input_handler joydev_handler = {
.match = joydev_match, .match = joydev_match,
.connect = joydev_connect, .connect = joydev_connect,
.disconnect = joydev_disconnect, .disconnect = joydev_disconnect,
.fops = &joydev_fops, .legacy_minors = true,
.minor = JOYDEV_MINOR_BASE, .minor = JOYDEV_MINOR_BASE,
.name = "joydev", .name = "joydev",
.id_table = joydev_ids, .id_table = joydev_ids,
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/major.h> #include <linux/major.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/cdev.h>
#include <linux/kernel.h> #include <linux/kernel.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
...@@ -58,14 +59,15 @@ struct mousedev_hw_data { ...@@ -58,14 +59,15 @@ struct mousedev_hw_data {
struct mousedev { struct mousedev {
int open; int open;
int minor;
struct input_handle handle; struct input_handle handle;
wait_queue_head_t wait; wait_queue_head_t wait;
struct list_head client_list; struct list_head client_list;
spinlock_t client_lock; /* protects client_list */ spinlock_t client_lock; /* protects client_list */
struct mutex mutex; struct mutex mutex;
struct device dev; struct device dev;
struct cdev cdev;
bool exist; bool exist;
bool is_mixdev;
struct list_head mixdev_node; struct list_head mixdev_node;
bool opened_by_mixdev; bool opened_by_mixdev;
...@@ -111,10 +113,6 @@ struct mousedev_client { ...@@ -111,10 +113,6 @@ struct mousedev_client {
static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
static struct input_handler mousedev_handler;
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
static DEFINE_MUTEX(mousedev_table_mutex);
static struct mousedev *mousedev_mix; static struct mousedev *mousedev_mix;
static LIST_HEAD(mousedev_mix_list); static LIST_HEAD(mousedev_mix_list);
...@@ -430,7 +428,7 @@ static int mousedev_open_device(struct mousedev *mousedev) ...@@ -430,7 +428,7 @@ static int mousedev_open_device(struct mousedev *mousedev)
if (retval) if (retval)
return retval; return retval;
if (mousedev->minor == MOUSEDEV_MIX) if (mousedev->is_mixdev)
mixdev_open_devices(); mixdev_open_devices();
else if (!mousedev->exist) else if (!mousedev->exist)
retval = -ENODEV; retval = -ENODEV;
...@@ -448,7 +446,7 @@ static void mousedev_close_device(struct mousedev *mousedev) ...@@ -448,7 +446,7 @@ static void mousedev_close_device(struct mousedev *mousedev)
{ {
mutex_lock(&mousedev->mutex); mutex_lock(&mousedev->mutex);
if (mousedev->minor == MOUSEDEV_MIX) if (mousedev->is_mixdev)
mixdev_close_devices(); mixdev_close_devices();
else if (mousedev->exist && !--mousedev->open) else if (mousedev->exist && !--mousedev->open)
input_close_device(&mousedev->handle); input_close_device(&mousedev->handle);
...@@ -535,35 +533,17 @@ static int mousedev_open(struct inode *inode, struct file *file) ...@@ -535,35 +533,17 @@ static int mousedev_open(struct inode *inode, struct file *file)
struct mousedev_client *client; struct mousedev_client *client;
struct mousedev *mousedev; struct mousedev *mousedev;
int error; int error;
int i;
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
if (imajor(inode) == MISC_MAJOR) if (imajor(inode) == MISC_MAJOR)
i = MOUSEDEV_MIX; mousedev = mousedev_mix;
else else
#endif #endif
i = iminor(inode) - MOUSEDEV_MINOR_BASE; mousedev = container_of(inode->i_cdev, struct mousedev, cdev);
if (i >= MOUSEDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&mousedev_table_mutex);
if (error)
return error;
mousedev = mousedev_table[i];
if (mousedev)
get_device(&mousedev->dev);
mutex_unlock(&mousedev_table_mutex);
if (!mousedev)
return -ENODEV;
client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
if (!client) { if (!client)
error = -ENOMEM; return -ENOMEM;
goto err_put_mousedev;
}
spin_lock_init(&client->packet_lock); spin_lock_init(&client->packet_lock);
client->pos_x = xres / 2; client->pos_x = xres / 2;
...@@ -577,13 +557,13 @@ static int mousedev_open(struct inode *inode, struct file *file) ...@@ -577,13 +557,13 @@ static int mousedev_open(struct inode *inode, struct file *file)
file->private_data = client; file->private_data = client;
nonseekable_open(inode, file); nonseekable_open(inode, file);
get_device(&mousedev->dev);
return 0; return 0;
err_free_client: err_free_client:
mousedev_detach_client(mousedev, client); mousedev_detach_client(mousedev, client);
kfree(client); kfree(client);
err_put_mousedev:
put_device(&mousedev->dev);
return error; return error;
} }
...@@ -793,19 +773,6 @@ static const struct file_operations mousedev_fops = { ...@@ -793,19 +773,6 @@ static const struct file_operations mousedev_fops = {
.llseek = noop_llseek, .llseek = noop_llseek,
}; };
static int mousedev_install_chrdev(struct mousedev *mousedev)
{
mousedev_table[mousedev->minor] = mousedev;
return 0;
}
static void mousedev_remove_chrdev(struct mousedev *mousedev)
{
mutex_lock(&mousedev_table_mutex);
mousedev_table[mousedev->minor] = NULL;
mutex_unlock(&mousedev_table_mutex);
}
/* /*
* Mark device non-existent. This disables writes, ioctls and * Mark device non-existent. This disables writes, ioctls and
* prevents new users from opening the device. Already posted * prevents new users from opening the device. Already posted
...@@ -840,24 +807,50 @@ static void mousedev_cleanup(struct mousedev *mousedev) ...@@ -840,24 +807,50 @@ static void mousedev_cleanup(struct mousedev *mousedev)
mousedev_mark_dead(mousedev); mousedev_mark_dead(mousedev);
mousedev_hangup(mousedev); mousedev_hangup(mousedev);
mousedev_remove_chrdev(mousedev);
cdev_del(&mousedev->cdev);
/* mousedev is marked dead so no one else accesses mousedev->open */ /* mousedev is marked dead so no one else accesses mousedev->open */
if (mousedev->open) if (mousedev->open)
input_close_device(handle); input_close_device(handle);
} }
static int mousedev_reserve_minor(bool mixdev)
{
int minor;
if (mixdev) {
minor = input_get_new_minor(MOUSEDEV_MIX, 1, false);
if (minor < 0)
pr_err("failed to reserve mixdev minor: %d\n", minor);
} else {
minor = input_get_new_minor(MOUSEDEV_MINOR_BASE,
MOUSEDEV_MINORS, true);
if (minor < 0)
pr_err("failed to reserve new minor: %d\n", minor);
}
return minor;
}
static struct mousedev *mousedev_create(struct input_dev *dev, static struct mousedev *mousedev_create(struct input_dev *dev,
struct input_handler *handler, struct input_handler *handler,
int minor) bool mixdev)
{ {
struct mousedev *mousedev; struct mousedev *mousedev;
int minor;
int error; int error;
minor = mousedev_reserve_minor(mixdev);
if (minor < 0) {
error = minor;
goto err_out;
}
mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);
if (!mousedev) { if (!mousedev) {
error = -ENOMEM; error = -ENOMEM;
goto err_out; goto err_free_minor;
} }
INIT_LIST_HEAD(&mousedev->client_list); INIT_LIST_HEAD(&mousedev->client_list);
...@@ -865,16 +858,21 @@ static struct mousedev *mousedev_create(struct input_dev *dev, ...@@ -865,16 +858,21 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
spin_lock_init(&mousedev->client_lock); spin_lock_init(&mousedev->client_lock);
mutex_init(&mousedev->mutex); mutex_init(&mousedev->mutex);
lockdep_set_subclass(&mousedev->mutex, lockdep_set_subclass(&mousedev->mutex,
minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0); mixdev ? SINGLE_DEPTH_NESTING : 0);
init_waitqueue_head(&mousedev->wait); init_waitqueue_head(&mousedev->wait);
if (minor == MOUSEDEV_MIX) if (mixdev) {
dev_set_name(&mousedev->dev, "mice"); dev_set_name(&mousedev->dev, "mice");
else } else {
dev_set_name(&mousedev->dev, "mouse%d", minor); int dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS)
dev_no -= MOUSEDEV_MINOR_BASE;
dev_set_name(&mousedev->dev, "mouse%d", dev_no);
}
mousedev->minor = minor;
mousedev->exist = true; mousedev->exist = true;
mousedev->is_mixdev = mixdev;
mousedev->handle.dev = input_get_device(dev); mousedev->handle.dev = input_get_device(dev);
mousedev->handle.name = dev_name(&mousedev->dev); mousedev->handle.name = dev_name(&mousedev->dev);
mousedev->handle.handler = handler; mousedev->handle.handler = handler;
...@@ -883,17 +881,18 @@ static struct mousedev *mousedev_create(struct input_dev *dev, ...@@ -883,17 +881,18 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
mousedev->dev.class = &input_class; mousedev->dev.class = &input_class;
if (dev) if (dev)
mousedev->dev.parent = &dev->dev; mousedev->dev.parent = &dev->dev;
mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor); mousedev->dev.devt = MKDEV(INPUT_MAJOR, minor);
mousedev->dev.release = mousedev_free; mousedev->dev.release = mousedev_free;
device_initialize(&mousedev->dev); device_initialize(&mousedev->dev);
if (minor != MOUSEDEV_MIX) { if (!mixdev) {
error = input_register_handle(&mousedev->handle); error = input_register_handle(&mousedev->handle);
if (error) if (error)
goto err_free_mousedev; goto err_free_mousedev;
} }
error = mousedev_install_chrdev(mousedev); cdev_init(&mousedev->cdev, &mousedev_fops);
error = cdev_add(&mousedev->cdev, mousedev->dev.devt, 1);
if (error) if (error)
goto err_unregister_handle; goto err_unregister_handle;
...@@ -906,10 +905,12 @@ static struct mousedev *mousedev_create(struct input_dev *dev, ...@@ -906,10 +905,12 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
err_cleanup_mousedev: err_cleanup_mousedev:
mousedev_cleanup(mousedev); mousedev_cleanup(mousedev);
err_unregister_handle: err_unregister_handle:
if (minor != MOUSEDEV_MIX) if (!mixdev)
input_unregister_handle(&mousedev->handle); input_unregister_handle(&mousedev->handle);
err_free_mousedev: err_free_mousedev:
put_device(&mousedev->dev); put_device(&mousedev->dev);
err_free_minor:
input_free_minor(minor);
err_out: err_out:
return ERR_PTR(error); return ERR_PTR(error);
} }
...@@ -918,7 +919,8 @@ static void mousedev_destroy(struct mousedev *mousedev) ...@@ -918,7 +919,8 @@ static void mousedev_destroy(struct mousedev *mousedev)
{ {
device_del(&mousedev->dev); device_del(&mousedev->dev);
mousedev_cleanup(mousedev); mousedev_cleanup(mousedev);
if (mousedev->minor != MOUSEDEV_MIX) input_free_minor(MINOR(mousedev->dev.devt));
if (!mousedev->is_mixdev)
input_unregister_handle(&mousedev->handle); input_unregister_handle(&mousedev->handle);
put_device(&mousedev->dev); put_device(&mousedev->dev);
} }
...@@ -967,19 +969,9 @@ static int mousedev_connect(struct input_handler *handler, ...@@ -967,19 +969,9 @@ static int mousedev_connect(struct input_handler *handler,
const struct input_device_id *id) const struct input_device_id *id)
{ {
struct mousedev *mousedev; struct mousedev *mousedev;
int minor;
int error; int error;
for (minor = 0; minor < MOUSEDEV_MINORS; minor++) mousedev = mousedev_create(dev, handler, false);
if (!mousedev_table[minor])
break;
if (minor == MOUSEDEV_MINORS) {
pr_err("no more free mousedev devices\n");
return -ENFILE;
}
mousedev = mousedev_create(dev, handler, minor);
if (IS_ERR(mousedev)) if (IS_ERR(mousedev))
return PTR_ERR(mousedev); return PTR_ERR(mousedev);
...@@ -1055,7 +1047,7 @@ static struct input_handler mousedev_handler = { ...@@ -1055,7 +1047,7 @@ static struct input_handler mousedev_handler = {
.event = mousedev_event, .event = mousedev_event,
.connect = mousedev_connect, .connect = mousedev_connect,
.disconnect = mousedev_disconnect, .disconnect = mousedev_disconnect,
.fops = &mousedev_fops, .legacy_minors = true,
.minor = MOUSEDEV_MINOR_BASE, .minor = MOUSEDEV_MINOR_BASE,
.name = "mousedev", .name = "mousedev",
.id_table = mousedev_ids, .id_table = mousedev_ids,
...@@ -1098,7 +1090,7 @@ static int __init mousedev_init(void) ...@@ -1098,7 +1090,7 @@ static int __init mousedev_init(void)
{ {
int error; int error;
mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX); mousedev_mix = mousedev_create(NULL, &mousedev_handler, true);
if (IS_ERR(mousedev_mix)) if (IS_ERR(mousedev_mix))
return PTR_ERR(mousedev_mix); return PTR_ERR(mousedev_mix);
......
...@@ -1396,8 +1396,8 @@ struct input_handle; ...@@ -1396,8 +1396,8 @@ struct input_handle;
* @start: starts handler for given handle. This function is called by * @start: starts handler for given handle. This function is called by
* input core right after connect() method and also when a process * input core right after connect() method and also when a process
* that "grabbed" a device releases it * that "grabbed" a device releases it
* @fops: file operations this driver implements * @legacy_minors: set to %true by drivers using legacy minor ranges
* @minor: beginning of range of 32 minors for devices this driver * @minor: beginning of range of 32 legacy minors for devices this driver
* can provide * can provide
* @name: name of the handler, to be shown in /proc/bus/input/handlers * @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can * @id_table: pointer to a table of input_device_ids this driver can
...@@ -1431,7 +1431,7 @@ struct input_handler { ...@@ -1431,7 +1431,7 @@ struct input_handler {
void (*disconnect)(struct input_handle *handle); void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle); void (*start)(struct input_handle *handle);
const struct file_operations *fops; bool legacy_minors;
int minor; int minor;
const char *name; const char *name;
...@@ -1499,6 +1499,10 @@ void input_reset_device(struct input_dev *); ...@@ -1499,6 +1499,10 @@ void input_reset_device(struct input_dev *);
int __must_check input_register_handler(struct input_handler *); int __must_check input_register_handler(struct input_handler *);
void input_unregister_handler(struct input_handler *); void input_unregister_handler(struct input_handler *);
int __must_check input_get_new_minor(int legacy_base, unsigned int legacy_num,
bool allow_dynamic);
void input_free_minor(unsigned int minor);
int input_handler_for_each_handle(struct input_handler *, void *data, int input_handler_for_each_handle(struct input_handler *, void *data,
int (*fn)(struct input_handle *, void *)); int (*fn)(struct input_handle *, void *));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment