diff options
Diffstat (limited to 'drivers/input/evdev.c')
-rw-r--r-- | drivers/input/evdev.c | 35 |
1 files changed, 28 insertions, 7 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 2ee6c7a68bd..cd323254ca6 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c | |||
@@ -10,7 +10,8 @@ | |||
10 | 10 | ||
11 | #define EVDEV_MINOR_BASE 64 | 11 | #define EVDEV_MINOR_BASE 64 |
12 | #define EVDEV_MINORS 32 | 12 | #define EVDEV_MINORS 32 |
13 | #define EVDEV_BUFFER_SIZE 64 | 13 | #define EVDEV_MIN_BUFFER_SIZE 64U |
14 | #define EVDEV_BUF_PACKETS 8 | ||
14 | 15 | ||
15 | #include <linux/poll.h> | 16 | #include <linux/poll.h> |
16 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
@@ -36,13 +37,14 @@ struct evdev { | |||
36 | }; | 37 | }; |
37 | 38 | ||
38 | struct evdev_client { | 39 | struct evdev_client { |
39 | struct input_event buffer[EVDEV_BUFFER_SIZE]; | ||
40 | int head; | 40 | int head; |
41 | int tail; | 41 | int tail; |
42 | spinlock_t buffer_lock; /* protects access to buffer, head and tail */ | 42 | spinlock_t buffer_lock; /* protects access to buffer, head and tail */ |
43 | struct fasync_struct *fasync; | 43 | struct fasync_struct *fasync; |
44 | struct evdev *evdev; | 44 | struct evdev *evdev; |
45 | struct list_head node; | 45 | struct list_head node; |
46 | int bufsize; | ||
47 | struct input_event buffer[]; | ||
46 | }; | 48 | }; |
47 | 49 | ||
48 | static struct evdev *evdev_table[EVDEV_MINORS]; | 50 | static struct evdev *evdev_table[EVDEV_MINORS]; |
@@ -52,11 +54,15 @@ static void evdev_pass_event(struct evdev_client *client, | |||
52 | struct input_event *event) | 54 | struct input_event *event) |
53 | { | 55 | { |
54 | /* | 56 | /* |
55 | * Interrupts are disabled, just acquire the lock | 57 | * Interrupts are disabled, just acquire the lock. |
58 | * Make sure we don't leave with the client buffer | ||
59 | * "empty" by having client->head == client->tail. | ||
56 | */ | 60 | */ |
57 | spin_lock(&client->buffer_lock); | 61 | spin_lock(&client->buffer_lock); |
58 | client->buffer[client->head++] = *event; | 62 | do { |
59 | client->head &= EVDEV_BUFFER_SIZE - 1; | 63 | client->buffer[client->head++] = *event; |
64 | client->head &= client->bufsize - 1; | ||
65 | } while (client->head == client->tail); | ||
60 | spin_unlock(&client->buffer_lock); | 66 | spin_unlock(&client->buffer_lock); |
61 | 67 | ||
62 | if (event->type == EV_SYN) | 68 | if (event->type == EV_SYN) |
@@ -242,11 +248,21 @@ static int evdev_release(struct inode *inode, struct file *file) | |||
242 | return 0; | 248 | return 0; |
243 | } | 249 | } |
244 | 250 | ||
251 | static unsigned int evdev_compute_buffer_size(struct input_dev *dev) | ||
252 | { | ||
253 | unsigned int n_events = | ||
254 | max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS, | ||
255 | EVDEV_MIN_BUFFER_SIZE); | ||
256 | |||
257 | return roundup_pow_of_two(n_events); | ||
258 | } | ||
259 | |||
245 | static int evdev_open(struct inode *inode, struct file *file) | 260 | static int evdev_open(struct inode *inode, struct file *file) |
246 | { | 261 | { |
247 | struct evdev *evdev; | 262 | struct evdev *evdev; |
248 | struct evdev_client *client; | 263 | struct evdev_client *client; |
249 | int i = iminor(inode) - EVDEV_MINOR_BASE; | 264 | int i = iminor(inode) - EVDEV_MINOR_BASE; |
265 | unsigned int bufsize; | ||
250 | int error; | 266 | int error; |
251 | 267 | ||
252 | if (i >= EVDEV_MINORS) | 268 | if (i >= EVDEV_MINORS) |
@@ -263,12 +279,17 @@ static int evdev_open(struct inode *inode, struct file *file) | |||
263 | if (!evdev) | 279 | if (!evdev) |
264 | return -ENODEV; | 280 | return -ENODEV; |
265 | 281 | ||
266 | client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); | 282 | bufsize = evdev_compute_buffer_size(evdev->handle.dev); |
283 | |||
284 | client = kzalloc(sizeof(struct evdev_client) + | ||
285 | bufsize * sizeof(struct input_event), | ||
286 | GFP_KERNEL); | ||
267 | if (!client) { | 287 | if (!client) { |
268 | error = -ENOMEM; | 288 | error = -ENOMEM; |
269 | goto err_put_evdev; | 289 | goto err_put_evdev; |
270 | } | 290 | } |
271 | 291 | ||
292 | client->bufsize = bufsize; | ||
272 | spin_lock_init(&client->buffer_lock); | 293 | spin_lock_init(&client->buffer_lock); |
273 | client->evdev = evdev; | 294 | client->evdev = evdev; |
274 | evdev_attach_client(evdev, client); | 295 | evdev_attach_client(evdev, client); |
@@ -334,7 +355,7 @@ static int evdev_fetch_next_event(struct evdev_client *client, | |||
334 | have_event = client->head != client->tail; | 355 | have_event = client->head != client->tail; |
335 | if (have_event) { | 356 | if (have_event) { |
336 | *event = client->buffer[client->tail++]; | 357 | *event = client->buffer[client->tail++]; |
337 | client->tail &= EVDEV_BUFFER_SIZE - 1; | 358 | client->tail &= client->bufsize - 1; |
338 | } | 359 | } |
339 | 360 | ||
340 | spin_unlock_irq(&client->buffer_lock); | 361 | spin_unlock_irq(&client->buffer_lock); |