Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/dtor/input

Pull input layer updates from Dmitry Torokhov:
 "2nd round of updates for the input subsystem.  With it input core no
  longer limits number of character devices per event handler (such as
  evdev) to 32, but switches to dynamic minors once legacy range is
  exhausted.  This should get multi-seat installations that currently
  run our of event devices very quickly.

  You will also get an update for Wacom driver and a couple of driver
  fixes."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: extend the number of event (and other) devices
  Input: mousedev - mark mousedev interfaces as non-seekable
  Input: mousedev - rename mixdev_open to opened_by_mixdev
  Input: mousedev - reformat structure initializers
  Input: mousedev - factor out psaux code to reduce #ifdefery
  Input: samsung-keypad - add clk_prepare and clk_unprepare
  Input: atmel_mxt_ts - simplify mxt_dump_message
  Input: wacom - clean up wacom_query_tablet_data
  Input: wacom - introduce wacom_fix_phy_from_hid
  Input: wacom - allow any multi-input Intuos device to set prox
  Input: wacom - report correct touch contact size for I5/Bamboo
  • Loading branch information
Linus Torvalds committed Oct 13, 2012
2 parents bd81cce + 0cc8d6a commit 8bbbfa7
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 345 deletions.
99 changes: 33 additions & 66 deletions drivers/input/evdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,19 @@
#include <linux/input/mt.h>
#include <linux/major.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include "input-compat.h"

struct evdev {
int open;
int minor;
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
struct cdev cdev;
bool exist;
};

Expand All @@ -51,9 +52,6 @@ struct evdev_client {
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,
const struct input_event *event)
{
Expand Down Expand Up @@ -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)
{
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;
int i = iminor(inode) - EVDEV_MINOR_BASE;
unsigned int bufsize;
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) +
bufsize * sizeof(struct input_event),
GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
if (!client)
return -ENOMEM;

client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
Expand All @@ -352,13 +331,12 @@ static int evdev_open(struct inode *inode, struct file *file)
file->private_data = client;
nonseekable_open(inode, file);

get_device(&evdev->dev);
return 0;

err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}

Expand Down Expand Up @@ -942,26 +920,6 @@ static const struct file_operations evdev_fops = {
.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
* prevents new users from opening the device. Already posted
Expand All @@ -980,7 +938,8 @@ static void evdev_cleanup(struct evdev *evdev)

evdev_mark_dead(evdev);
evdev_hangup(evdev);
evdev_remove_chrdev(evdev);

cdev_del(&evdev->cdev);

/* evdev is marked dead so no one else accesses evdev->open */
if (evdev->open) {
Expand All @@ -991,43 +950,47 @@ static void evdev_cleanup(struct evdev *evdev)

/*
* 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,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;

for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;

if (minor == EVDEV_MINORS) {
pr_err("no more free evdev devices\n");
return -ENFILE;
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}

INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);

dev_set_name(&evdev->dev, "event%d", minor);
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.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
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.parent = &dev->dev;
evdev->dev.release = evdev_free;
Expand All @@ -1037,7 +1000,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
if (error)
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)
goto err_unregister_handle;

Expand All @@ -1053,6 +1017,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error;
}

Expand All @@ -1062,6 +1028,7 @@ static void evdev_disconnect(struct input_handle *handle)

device_del(&evdev->dev);
evdev_cleanup(evdev);
input_free_minor(MINOR(evdev->dev.devt));
input_unregister_handle(handle);
put_device(&evdev->dev);
}
Expand All @@ -1078,7 +1045,7 @@ static struct input_handler evdev_handler = {
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
Expand Down
Loading

0 comments on commit 8bbbfa7

Please sign in to comment.