diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2007-08-30 00:22:32 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2007-08-30 00:22:32 -0400 |
commit | b126207ccdfe492fbc339c18d4898b1b5353fc6b (patch) | |
tree | 2ebc6d125d455cec87552ccd8ce2a3dc9e6e8242 /drivers/input | |
parent | 464b241575f3700e14492e34f26bcd1794280f55 (diff) |
Input: joydev - implement proper locking
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/joydev.c | 743 |
1 files changed, 492 insertions, 251 deletions
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index a9a0180bfd46..a4272d63c4d6 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c | |||
@@ -43,6 +43,8 @@ struct joydev { | |||
43 | struct input_handle handle; | 43 | struct input_handle handle; |
44 | wait_queue_head_t wait; | 44 | wait_queue_head_t wait; |
45 | struct list_head client_list; | 45 | struct list_head client_list; |
46 | spinlock_t client_lock; /* protects client_list */ | ||
47 | struct mutex mutex; | ||
46 | struct device dev; | 48 | struct device dev; |
47 | 49 | ||
48 | struct js_corr corr[ABS_MAX + 1]; | 50 | struct js_corr corr[ABS_MAX + 1]; |
@@ -61,31 +63,61 @@ struct joydev_client { | |||
61 | int head; | 63 | int head; |
62 | int tail; | 64 | int tail; |
63 | int startup; | 65 | int startup; |
66 | spinlock_t buffer_lock; /* protects access to buffer, head and tail */ | ||
64 | struct fasync_struct *fasync; | 67 | struct fasync_struct *fasync; |
65 | struct joydev *joydev; | 68 | struct joydev *joydev; |
66 | struct list_head node; | 69 | struct list_head node; |
67 | }; | 70 | }; |
68 | 71 | ||
69 | static struct joydev *joydev_table[JOYDEV_MINORS]; | 72 | static struct joydev *joydev_table[JOYDEV_MINORS]; |
73 | static DEFINE_MUTEX(joydev_table_mutex); | ||
70 | 74 | ||
71 | static int joydev_correct(int value, struct js_corr *corr) | 75 | static int joydev_correct(int value, struct js_corr *corr) |
72 | { | 76 | { |
73 | switch (corr->type) { | 77 | switch (corr->type) { |
74 | case JS_CORR_NONE: | 78 | |
75 | break; | 79 | case JS_CORR_NONE: |
76 | case JS_CORR_BROKEN: | 80 | break; |
77 | value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : | 81 | |
78 | ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : | 82 | case JS_CORR_BROKEN: |
79 | ((corr->coef[2] * (value - corr->coef[0])) >> 14); | 83 | value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : |
80 | break; | 84 | ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : |
81 | default: | 85 | ((corr->coef[2] * (value - corr->coef[0])) >> 14); |
82 | return 0; | 86 | break; |
87 | |||
88 | default: | ||
89 | return 0; | ||
83 | } | 90 | } |
84 | 91 | ||
85 | return value < -32767 ? -32767 : (value > 32767 ? 32767 : value); | 92 | return value < -32767 ? -32767 : (value > 32767 ? 32767 : value); |
86 | } | 93 | } |
87 | 94 | ||
88 | static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) | 95 | static void joydev_pass_event(struct joydev_client *client, |
96 | struct js_event *event) | ||
97 | { | ||
98 | struct joydev *joydev = client->joydev; | ||
99 | |||
100 | /* | ||
101 | * IRQs already disabled, just acquire the lock | ||
102 | */ | ||
103 | spin_lock(&client->buffer_lock); | ||
104 | |||
105 | client->buffer[client->head] = *event; | ||
106 | |||
107 | if (client->startup == joydev->nabs + joydev->nkey) { | ||
108 | client->head++; | ||
109 | client->head &= JOYDEV_BUFFER_SIZE - 1; | ||
110 | if (client->tail == client->head) | ||
111 | client->startup = 0; | ||
112 | } | ||
113 | |||
114 | spin_unlock(&client->buffer_lock); | ||
115 | |||
116 | kill_fasync(&client->fasync, SIGIO, POLL_IN); | ||
117 | } | ||
118 | |||
119 | static void joydev_event(struct input_handle *handle, | ||
120 | unsigned int type, unsigned int code, int value) | ||
89 | { | 121 | { |
90 | struct joydev *joydev = handle->private; | 122 | struct joydev *joydev = handle->private; |
91 | struct joydev_client *client; | 123 | struct joydev_client *client; |
@@ -93,39 +125,32 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne | |||
93 | 125 | ||
94 | switch (type) { | 126 | switch (type) { |
95 | 127 | ||
96 | case EV_KEY: | 128 | case EV_KEY: |
97 | if (code < BTN_MISC || value == 2) | 129 | if (code < BTN_MISC || value == 2) |
98 | return; | 130 | return; |
99 | event.type = JS_EVENT_BUTTON; | 131 | event.type = JS_EVENT_BUTTON; |
100 | event.number = joydev->keymap[code - BTN_MISC]; | 132 | event.number = joydev->keymap[code - BTN_MISC]; |
101 | event.value = value; | 133 | event.value = value; |
102 | break; | 134 | break; |
103 | |||
104 | case EV_ABS: | ||
105 | event.type = JS_EVENT_AXIS; | ||
106 | event.number = joydev->absmap[code]; | ||
107 | event.value = joydev_correct(value, joydev->corr + event.number); | ||
108 | if (event.value == joydev->abs[event.number]) | ||
109 | return; | ||
110 | joydev->abs[event.number] = event.value; | ||
111 | break; | ||
112 | 135 | ||
113 | default: | 136 | case EV_ABS: |
137 | event.type = JS_EVENT_AXIS; | ||
138 | event.number = joydev->absmap[code]; | ||
139 | event.value = joydev_correct(value, | ||
140 | &joydev->corr[event.number]); | ||
141 | if (event.value == joydev->abs[event.number]) | ||
114 | return; | 142 | return; |
143 | joydev->abs[event.number] = event.value; | ||
144 | break; | ||
145 | |||
146 | default: | ||
147 | return; | ||
115 | } | 148 | } |
116 | 149 | ||
117 | event.time = jiffies_to_msecs(jiffies); | 150 | event.time = jiffies_to_msecs(jiffies); |
118 | 151 | ||
119 | list_for_each_entry(client, &joydev->client_list, node) { | 152 | list_for_each_entry_rcu(client, &joydev->client_list, node) |
120 | 153 | joydev_pass_event(client, &event); | |
121 | memcpy(client->buffer + client->head, &event, sizeof(struct js_event)); | ||
122 | |||
123 | if (client->startup == joydev->nabs + joydev->nkey) | ||
124 | if (client->tail == (client->head = (client->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) | ||
125 | client->startup = 0; | ||
126 | |||
127 | kill_fasync(&client->fasync, SIGIO, POLL_IN); | ||
128 | } | ||
129 | 154 | ||
130 | wake_up_interruptible(&joydev->wait); | 155 | wake_up_interruptible(&joydev->wait); |
131 | } | 156 | } |
@@ -144,23 +169,85 @@ static void joydev_free(struct device *dev) | |||
144 | { | 169 | { |
145 | struct joydev *joydev = container_of(dev, struct joydev, dev); | 170 | struct joydev *joydev = container_of(dev, struct joydev, dev); |
146 | 171 | ||
147 | joydev_table[joydev->minor] = NULL; | ||
148 | kfree(joydev); | 172 | kfree(joydev); |
149 | } | 173 | } |
150 | 174 | ||
175 | static void joydev_attach_client(struct joydev *joydev, | ||
176 | struct joydev_client *client) | ||
177 | { | ||
178 | spin_lock(&joydev->client_lock); | ||
179 | list_add_tail_rcu(&client->node, &joydev->client_list); | ||
180 | spin_unlock(&joydev->client_lock); | ||
181 | /* | ||
182 | * We don't use synchronize_rcu() here because read-side | ||
183 | * critical section is protected by a spinlock (dev->event_lock) | ||
184 | * instead of rcu_read_lock(). | ||
185 | */ | ||
186 | synchronize_sched(); | ||
187 | } | ||
188 | |||
189 | static void joydev_detach_client(struct joydev *joydev, | ||
190 | struct joydev_client *client) | ||
191 | { | ||
192 | spin_lock(&joydev->client_lock); | ||
193 | list_del_rcu(&client->node); | ||
194 | spin_unlock(&joydev->client_lock); | ||
195 | synchronize_sched(); | ||
196 | } | ||
197 | |||
198 | static int joydev_open_device(struct joydev *joydev) | ||
199 | { | ||
200 | int retval; | ||
201 | |||
202 | retval = mutex_lock_interruptible(&joydev->mutex); | ||
203 | if (retval) | ||
204 | return retval; | ||
205 | |||
206 | if (!joydev->exist) | ||
207 | retval = -ENODEV; | ||
208 | else if (!joydev->open++) | ||
209 | retval = input_open_device(&joydev->handle); | ||
210 | |||
211 | mutex_unlock(&joydev->mutex); | ||
212 | return retval; | ||
213 | } | ||
214 | |||
215 | static void joydev_close_device(struct joydev *joydev) | ||
216 | { | ||
217 | mutex_lock(&joydev->mutex); | ||
218 | |||
219 | if (joydev->exist && !--joydev->open) | ||
220 | input_close_device(&joydev->handle); | ||
221 | |||
222 | mutex_unlock(&joydev->mutex); | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * Wake up users waiting for IO so they can disconnect from | ||
227 | * dead device. | ||
228 | */ | ||
229 | static void joydev_hangup(struct joydev *joydev) | ||
230 | { | ||
231 | struct joydev_client *client; | ||
232 | |||
233 | spin_lock(&joydev->client_lock); | ||
234 | list_for_each_entry(client, &joydev->client_list, node) | ||
235 | kill_fasync(&client->fasync, SIGIO, POLL_HUP); | ||
236 | spin_unlock(&joydev->client_lock); | ||
237 | |||
238 | wake_up_interruptible(&joydev->wait); | ||
239 | } | ||
240 | |||
151 | static int joydev_release(struct inode *inode, struct file *file) | 241 | static int joydev_release(struct inode *inode, struct file *file) |
152 | { | 242 | { |
153 | struct joydev_client *client = file->private_data; | 243 | struct joydev_client *client = file->private_data; |
154 | struct joydev *joydev = client->joydev; | 244 | struct joydev *joydev = client->joydev; |
155 | 245 | ||
156 | joydev_fasync(-1, file, 0); | 246 | joydev_fasync(-1, file, 0); |
157 | 247 | joydev_detach_client(joydev, client); | |
158 | list_del(&client->node); | ||
159 | kfree(client); | 248 | kfree(client); |
160 | 249 | ||
161 | if (!--joydev->open && joydev->exist) | 250 | joydev_close_device(joydev); |
162 | input_close_device(&joydev->handle); | ||
163 | |||
164 | put_device(&joydev->dev); | 251 | put_device(&joydev->dev); |
165 | 252 | ||
166 | return 0; | 253 | return 0; |
@@ -176,11 +263,16 @@ static int joydev_open(struct inode *inode, struct file *file) | |||
176 | if (i >= JOYDEV_MINORS) | 263 | if (i >= JOYDEV_MINORS) |
177 | return -ENODEV; | 264 | return -ENODEV; |
178 | 265 | ||
266 | error = mutex_lock_interruptible(&joydev_table_mutex); | ||
267 | if (error) | ||
268 | return error; | ||
179 | joydev = joydev_table[i]; | 269 | joydev = joydev_table[i]; |
180 | if (!joydev || !joydev->exist) | 270 | if (joydev) |
181 | return -ENODEV; | 271 | get_device(&joydev->dev); |
272 | mutex_unlock(&joydev_table_mutex); | ||
182 | 273 | ||
183 | get_device(&joydev->dev); | 274 | if (!joydev) |
275 | return -ENODEV; | ||
184 | 276 | ||
185 | client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); | 277 | client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); |
186 | if (!client) { | 278 | if (!client) { |
@@ -188,37 +280,129 @@ static int joydev_open(struct inode *inode, struct file *file) | |||
188 | goto err_put_joydev; | 280 | goto err_put_joydev; |
189 | } | 281 | } |
190 | 282 | ||
283 | spin_lock_init(&client->buffer_lock); | ||
191 | client->joydev = joydev; | 284 | client->joydev = joydev; |
192 | list_add_tail(&client->node, &joydev->client_list); | 285 | joydev_attach_client(joydev, client); |
193 | 286 | ||
194 | if (!joydev->open++ && joydev->exist) { | 287 | error = joydev_open_device(joydev); |
195 | error = input_open_device(&joydev->handle); | 288 | if (error) |
196 | if (error) | 289 | goto err_free_client; |
197 | goto err_free_client; | ||
198 | } | ||
199 | 290 | ||
200 | file->private_data = client; | 291 | file->private_data = client; |
201 | return 0; | 292 | return 0; |
202 | 293 | ||
203 | err_free_client: | 294 | err_free_client: |
204 | list_del(&client->node); | 295 | joydev_detach_client(joydev, client); |
205 | kfree(client); | 296 | kfree(client); |
206 | err_put_joydev: | 297 | err_put_joydev: |
207 | put_device(&joydev->dev); | 298 | put_device(&joydev->dev); |
208 | return error; | 299 | return error; |
209 | } | 300 | } |
210 | 301 | ||
211 | static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) | 302 | static int joydev_generate_startup_event(struct joydev_client *client, |
303 | struct input_dev *input, | ||
304 | struct js_event *event) | ||
212 | { | 305 | { |
213 | return -EINVAL; | 306 | struct joydev *joydev = client->joydev; |
307 | int have_event; | ||
308 | |||
309 | spin_lock_irq(&client->buffer_lock); | ||
310 | |||
311 | have_event = client->startup < joydev->nabs + joydev->nkey; | ||
312 | |||
313 | if (have_event) { | ||
314 | |||
315 | event->time = jiffies_to_msecs(jiffies); | ||
316 | if (client->startup < joydev->nkey) { | ||
317 | event->type = JS_EVENT_BUTTON | JS_EVENT_INIT; | ||
318 | event->number = client->startup; | ||
319 | event->value = !!test_bit(joydev->keypam[event->number], | ||
320 | input->key); | ||
321 | } else { | ||
322 | event->type = JS_EVENT_AXIS | JS_EVENT_INIT; | ||
323 | event->number = client->startup - joydev->nkey; | ||
324 | event->value = joydev->abs[event->number]; | ||
325 | } | ||
326 | client->startup++; | ||
327 | } | ||
328 | |||
329 | spin_unlock_irq(&client->buffer_lock); | ||
330 | |||
331 | return have_event; | ||
332 | } | ||
333 | |||
334 | static int joydev_fetch_next_event(struct joydev_client *client, | ||
335 | struct js_event *event) | ||
336 | { | ||
337 | int have_event; | ||
338 | |||
339 | spin_lock_irq(&client->buffer_lock); | ||
340 | |||
341 | have_event = client->head != client->tail; | ||
342 | if (have_event) { | ||
343 | *event = client->buffer[client->tail++]; | ||
344 | client->tail &= JOYDEV_BUFFER_SIZE - 1; | ||
345 | } | ||
346 | |||
347 | spin_unlock_irq(&client->buffer_lock); | ||
348 | |||
349 | return have_event; | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Old joystick interface | ||
354 | */ | ||
355 | static ssize_t joydev_0x_read(struct joydev_client *client, | ||
356 | struct input_dev *input, | ||
357 | char __user *buf) | ||
358 | { | ||
359 | struct joydev *joydev = client->joydev; | ||
360 | struct JS_DATA_TYPE data; | ||
361 | int i; | ||
362 | |||
363 | spin_lock_irq(&input->event_lock); | ||
364 | |||
365 | /* | ||
366 | * Get device state | ||
367 | */ | ||
368 | for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++) | ||
369 | data.buttons |= | ||
370 | test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0; | ||
371 | data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; | ||
372 | data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; | ||
373 | |||
374 | /* | ||
375 | * Reset reader's event queue | ||
376 | */ | ||
377 | spin_lock(&client->buffer_lock); | ||
378 | client->startup = 0; | ||
379 | client->tail = client->head; | ||
380 | spin_unlock(&client->buffer_lock); | ||
381 | |||
382 | spin_unlock_irq(&input->event_lock); | ||
383 | |||
384 | if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) | ||
385 | return -EFAULT; | ||
386 | |||
387 | return sizeof(struct JS_DATA_TYPE); | ||
214 | } | 388 | } |
215 | 389 | ||
216 | static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 390 | static inline int joydev_data_pending(struct joydev_client *client) |
391 | { | ||
392 | struct joydev *joydev = client->joydev; | ||
393 | |||
394 | return client->startup < joydev->nabs + joydev->nkey || | ||
395 | client->head != client->tail; | ||
396 | } | ||
397 | |||
398 | static ssize_t joydev_read(struct file *file, char __user *buf, | ||
399 | size_t count, loff_t *ppos) | ||
217 | { | 400 | { |
218 | struct joydev_client *client = file->private_data; | 401 | struct joydev_client *client = file->private_data; |
219 | struct joydev *joydev = client->joydev; | 402 | struct joydev *joydev = client->joydev; |
220 | struct input_dev *input = joydev->handle.dev; | 403 | struct input_dev *input = joydev->handle.dev; |
221 | int retval = 0; | 404 | struct js_event event; |
405 | int retval; | ||
222 | 406 | ||
223 | if (!joydev->exist) | 407 | if (!joydev->exist) |
224 | return -ENODEV; | 408 | return -ENODEV; |
@@ -226,68 +410,35 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo | |||
226 | if (count < sizeof(struct js_event)) | 410 | if (count < sizeof(struct js_event)) |
227 | return -EINVAL; | 411 | return -EINVAL; |
228 | 412 | ||
229 | if (count == sizeof(struct JS_DATA_TYPE)) { | 413 | if (count == sizeof(struct JS_DATA_TYPE)) |
230 | 414 | return joydev_0x_read(client, input, buf); | |
231 | struct JS_DATA_TYPE data; | ||
232 | int i; | ||
233 | 415 | ||
234 | for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++) | 416 | if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK)) |
235 | data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0; | ||
236 | data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; | ||
237 | data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; | ||
238 | |||
239 | if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) | ||
240 | return -EFAULT; | ||
241 | |||
242 | client->startup = 0; | ||
243 | client->tail = client->head; | ||
244 | |||
245 | return sizeof(struct JS_DATA_TYPE); | ||
246 | } | ||
247 | |||
248 | if (client->startup == joydev->nabs + joydev->nkey && | ||
249 | client->head == client->tail && (file->f_flags & O_NONBLOCK)) | ||
250 | return -EAGAIN; | 417 | return -EAGAIN; |
251 | 418 | ||
252 | retval = wait_event_interruptible(joydev->wait, | 419 | retval = wait_event_interruptible(joydev->wait, |
253 | !joydev->exist || | 420 | !joydev->exist || joydev_data_pending(client)); |
254 | client->startup < joydev->nabs + joydev->nkey || | ||
255 | client->head != client->tail); | ||
256 | if (retval) | 421 | if (retval) |
257 | return retval; | 422 | return retval; |
258 | 423 | ||
259 | if (!joydev->exist) | 424 | if (!joydev->exist) |
260 | return -ENODEV; | 425 | return -ENODEV; |
261 | 426 | ||
262 | while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { | 427 | while (retval + sizeof(struct js_event) <= count && |
263 | 428 | joydev_generate_startup_event(client, input, &event)) { | |
264 | struct js_event event; | ||
265 | |||
266 | event.time = jiffies_to_msecs(jiffies); | ||
267 | |||
268 | if (client->startup < joydev->nkey) { | ||
269 | event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; | ||
270 | event.number = client->startup; | ||
271 | event.value = !!test_bit(joydev->keypam[event.number], input->key); | ||
272 | } else { | ||
273 | event.type = JS_EVENT_AXIS | JS_EVENT_INIT; | ||
274 | event.number = client->startup - joydev->nkey; | ||
275 | event.value = joydev->abs[event.number]; | ||
276 | } | ||
277 | 429 | ||
278 | if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) | 430 | if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) |
279 | return -EFAULT; | 431 | return -EFAULT; |
280 | 432 | ||
281 | client->startup++; | ||
282 | retval += sizeof(struct js_event); | 433 | retval += sizeof(struct js_event); |
283 | } | 434 | } |
284 | 435 | ||
285 | while (client->head != client->tail && retval + sizeof(struct js_event) <= count) { | 436 | while (retval + sizeof(struct js_event) <= count && |
437 | joydev_fetch_next_event(client, &event)) { | ||
286 | 438 | ||
287 | if (copy_to_user(buf + retval, client->buffer + client->tail, sizeof(struct js_event))) | 439 | if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) |
288 | return -EFAULT; | 440 | return -EFAULT; |
289 | 441 | ||
290 | client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); | ||
291 | retval += sizeof(struct js_event); | 442 | retval += sizeof(struct js_event); |
292 | } | 443 | } |
293 | 444 | ||
@@ -301,126 +452,144 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait) | |||
301 | struct joydev *joydev = client->joydev; | 452 | struct joydev *joydev = client->joydev; |
302 | 453 | ||
303 | poll_wait(file, &joydev->wait, wait); | 454 | poll_wait(file, &joydev->wait, wait); |
304 | return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ? | 455 | return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) | |
305 | (POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR)); | 456 | (joydev->exist ? 0 : (POLLHUP | POLLERR)); |
306 | } | 457 | } |
307 | 458 | ||
308 | static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) | 459 | static int joydev_ioctl_common(struct joydev *joydev, |
460 | unsigned int cmd, void __user *argp) | ||
309 | { | 461 | { |
310 | struct input_dev *dev = joydev->handle.dev; | 462 | struct input_dev *dev = joydev->handle.dev; |
311 | int i, j; | 463 | int i, j; |
312 | 464 | ||
313 | switch (cmd) { | 465 | switch (cmd) { |
314 | 466 | ||
315 | case JS_SET_CAL: | 467 | case JS_SET_CAL: |
316 | return copy_from_user(&joydev->glue.JS_CORR, argp, | 468 | return copy_from_user(&joydev->glue.JS_CORR, argp, |
317 | sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; | 469 | sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; |
318 | 470 | ||
319 | case JS_GET_CAL: | 471 | case JS_GET_CAL: |
320 | return copy_to_user(argp, &joydev->glue.JS_CORR, | 472 | return copy_to_user(argp, &joydev->glue.JS_CORR, |
321 | sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; | 473 | sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; |
322 | 474 | ||
323 | case JS_SET_TIMEOUT: | 475 | case JS_SET_TIMEOUT: |
324 | return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); | 476 | return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); |
325 | 477 | ||
326 | case JS_GET_TIMEOUT: | 478 | case JS_GET_TIMEOUT: |
327 | return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); | 479 | return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); |
328 | 480 | ||
329 | case JSIOCGVERSION: | 481 | case JSIOCGVERSION: |
330 | return put_user(JS_VERSION, (__u32 __user *) argp); | 482 | return put_user(JS_VERSION, (__u32 __user *) argp); |
331 | 483 | ||
332 | case JSIOCGAXES: | 484 | case JSIOCGAXES: |
333 | return put_user(joydev->nabs, (__u8 __user *) argp); | 485 | return put_user(joydev->nabs, (__u8 __user *) argp); |
334 | 486 | ||
335 | case JSIOCGBUTTONS: | 487 | case JSIOCGBUTTONS: |
336 | return put_user(joydev->nkey, (__u8 __user *) argp); | 488 | return put_user(joydev->nkey, (__u8 __user *) argp); |
337 | 489 | ||
338 | case JSIOCSCORR: | 490 | case JSIOCSCORR: |
339 | if (copy_from_user(joydev->corr, argp, | 491 | if (copy_from_user(joydev->corr, argp, |
340 | sizeof(joydev->corr[0]) * joydev->nabs)) | 492 | sizeof(joydev->corr[0]) * joydev->nabs)) |
341 | return -EFAULT; | 493 | return -EFAULT; |
342 | for (i = 0; i < joydev->nabs; i++) { | ||
343 | j = joydev->abspam[i]; | ||
344 | joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); | ||
345 | } | ||
346 | return 0; | ||
347 | 494 | ||
348 | case JSIOCGCORR: | 495 | for (i = 0; i < joydev->nabs; i++) { |
349 | return copy_to_user(argp, joydev->corr, | 496 | j = joydev->abspam[i]; |
350 | sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; | 497 | joydev->abs[i] = joydev_correct(dev->abs[j], |
498 | &joydev->corr[i]); | ||
499 | } | ||
500 | return 0; | ||
351 | 501 | ||
352 | case JSIOCSAXMAP: | 502 | case JSIOCGCORR: |
353 | if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1))) | 503 | return copy_to_user(argp, joydev->corr, |
354 | return -EFAULT; | 504 | sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; |
355 | for (i = 0; i < joydev->nabs; i++) { | 505 | |
356 | if (joydev->abspam[i] > ABS_MAX) | 506 | case JSIOCSAXMAP: |
357 | return -EINVAL; | 507 | if (copy_from_user(joydev->abspam, argp, |
358 | joydev->absmap[joydev->abspam[i]] = i; | 508 | sizeof(__u8) * (ABS_MAX + 1))) |
359 | } | 509 | return -EFAULT; |
360 | return 0; | 510 | |
361 | 511 | for (i = 0; i < joydev->nabs; i++) { | |
362 | case JSIOCGAXMAP: | 512 | if (joydev->abspam[i] > ABS_MAX) |
363 | return copy_to_user(argp, joydev->abspam, | 513 | return -EINVAL; |
364 | sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0; | 514 | joydev->absmap[joydev->abspam[i]] = i; |
365 | 515 | } | |
366 | case JSIOCSBTNMAP: | 516 | return 0; |
367 | if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1))) | 517 | |
518 | case JSIOCGAXMAP: | ||
519 | return copy_to_user(argp, joydev->abspam, | ||
520 | sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0; | ||
521 | |||
522 | case JSIOCSBTNMAP: | ||
523 | if (copy_from_user(joydev->keypam, argp, | ||
524 | sizeof(__u16) * (KEY_MAX - BTN_MISC + 1))) | ||
525 | return -EFAULT; | ||
526 | |||
527 | for (i = 0; i < joydev->nkey; i++) { | ||
528 | if (joydev->keypam[i] > KEY_MAX || | ||
529 | joydev->keypam[i] < BTN_MISC) | ||
530 | return -EINVAL; | ||
531 | joydev->keymap[joydev->keypam[i] - BTN_MISC] = i; | ||
532 | } | ||
533 | |||
534 | return 0; | ||
535 | |||
536 | case JSIOCGBTNMAP: | ||
537 | return copy_to_user(argp, joydev->keypam, | ||
538 | sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0; | ||
539 | |||
540 | default: | ||
541 | if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) { | ||
542 | int len; | ||
543 | if (!dev->name) | ||
544 | return 0; | ||
545 | len = strlen(dev->name) + 1; | ||
546 | if (len > _IOC_SIZE(cmd)) | ||
547 | len = _IOC_SIZE(cmd); | ||
548 | if (copy_to_user(argp, dev->name, len)) | ||
368 | return -EFAULT; | 549 | return -EFAULT; |
369 | for (i = 0; i < joydev->nkey; i++) { | 550 | return len; |
370 | if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) | 551 | } |
371 | return -EINVAL; | ||
372 | joydev->keymap[joydev->keypam[i] - BTN_MISC] = i; | ||
373 | } | ||
374 | return 0; | ||
375 | |||
376 | case JSIOCGBTNMAP: | ||
377 | return copy_to_user(argp, joydev->keypam, | ||
378 | sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0; | ||
379 | |||
380 | default: | ||
381 | if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { | ||
382 | int len; | ||
383 | if (!dev->name) | ||
384 | return 0; | ||
385 | len = strlen(dev->name) + 1; | ||
386 | if (len > _IOC_SIZE(cmd)) | ||
387 | len = _IOC_SIZE(cmd); | ||
388 | if (copy_to_user(argp, dev->name, len)) | ||
389 | return -EFAULT; | ||
390 | return len; | ||
391 | } | ||
392 | } | 552 | } |
393 | return -EINVAL; | 553 | return -EINVAL; |
394 | } | 554 | } |
395 | 555 | ||
396 | #ifdef CONFIG_COMPAT | 556 | #ifdef CONFIG_COMPAT |
397 | static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 557 | static long joydev_compat_ioctl(struct file *file, |
558 | unsigned int cmd, unsigned long arg) | ||
398 | { | 559 | { |
399 | struct joydev_client *client = file->private_data; | 560 | struct joydev_client *client = file->private_data; |
400 | struct joydev *joydev = client->joydev; | 561 | struct joydev *joydev = client->joydev; |
401 | void __user *argp = (void __user *)arg; | 562 | void __user *argp = (void __user *)arg; |
402 | s32 tmp32; | 563 | s32 tmp32; |
403 | struct JS_DATA_SAVE_TYPE_32 ds32; | 564 | struct JS_DATA_SAVE_TYPE_32 ds32; |
404 | int err; | 565 | int retval; |
405 | 566 | ||
406 | if (!joydev->exist) | 567 | retval = mutex_lock_interruptible(&joydev->mutex); |
407 | return -ENODEV; | 568 | if (retval) |
569 | return retval; | ||
570 | |||
571 | if (!joydev->exist) { | ||
572 | retval = -ENODEV; | ||
573 | goto out; | ||
574 | } | ||
575 | |||
576 | switch (cmd) { | ||
408 | 577 | ||
409 | switch(cmd) { | ||
410 | case JS_SET_TIMELIMIT: | 578 | case JS_SET_TIMELIMIT: |
411 | err = get_user(tmp32, (s32 __user *) arg); | 579 | retval = get_user(tmp32, (s32 __user *) arg); |
412 | if (err == 0) | 580 | if (retval == 0) |
413 | joydev->glue.JS_TIMELIMIT = tmp32; | 581 | joydev->glue.JS_TIMELIMIT = tmp32; |
414 | break; | 582 | break; |
583 | |||
415 | case JS_GET_TIMELIMIT: | 584 | case JS_GET_TIMELIMIT: |
416 | tmp32 = joydev->glue.JS_TIMELIMIT; | 585 | tmp32 = joydev->glue.JS_TIMELIMIT; |
417 | err = put_user(tmp32, (s32 __user *) arg); | 586 | retval = put_user(tmp32, (s32 __user *) arg); |
418 | break; | 587 | break; |
419 | 588 | ||
420 | case JS_SET_ALL: | 589 | case JS_SET_ALL: |
421 | err = copy_from_user(&ds32, argp, | 590 | retval = copy_from_user(&ds32, argp, |
422 | sizeof(ds32)) ? -EFAULT : 0; | 591 | sizeof(ds32)) ? -EFAULT : 0; |
423 | if (err == 0) { | 592 | if (retval == 0) { |
424 | joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT; | 593 | joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT; |
425 | joydev->glue.BUSY = ds32.BUSY; | 594 | joydev->glue.BUSY = ds32.BUSY; |
426 | joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME; | 595 | joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME; |
@@ -438,55 +607,119 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo | |||
438 | ds32.JS_SAVE = joydev->glue.JS_SAVE; | 607 | ds32.JS_SAVE = joydev->glue.JS_SAVE; |
439 | ds32.JS_CORR = joydev->glue.JS_CORR; | 608 | ds32.JS_CORR = joydev->glue.JS_CORR; |
440 | 609 | ||
441 | err = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0; | 610 | retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0; |
442 | break; | 611 | break; |
443 | 612 | ||
444 | default: | 613 | default: |
445 | err = joydev_ioctl_common(joydev, cmd, argp); | 614 | retval = joydev_ioctl_common(joydev, cmd, argp); |
615 | break; | ||
446 | } | 616 | } |
447 | return err; | 617 | |
618 | out: | ||
619 | mutex_unlock(&joydev->mutex); | ||
620 | return retval; | ||
448 | } | 621 | } |
449 | #endif /* CONFIG_COMPAT */ | 622 | #endif /* CONFIG_COMPAT */ |
450 | 623 | ||
451 | static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) | 624 | static long joydev_ioctl(struct file *file, |
625 | unsigned int cmd, unsigned long arg) | ||
452 | { | 626 | { |
453 | struct joydev_client *client = file->private_data; | 627 | struct joydev_client *client = file->private_data; |
454 | struct joydev *joydev = client->joydev; | 628 | struct joydev *joydev = client->joydev; |
455 | void __user *argp = (void __user *)arg; | 629 | void __user *argp = (void __user *)arg; |
630 | int retval; | ||
456 | 631 | ||
457 | if (!joydev->exist) | 632 | retval = mutex_lock_interruptible(&joydev->mutex); |
458 | return -ENODEV; | 633 | if (retval) |
634 | return retval; | ||
459 | 635 | ||
460 | switch(cmd) { | 636 | if (!joydev->exist) { |
461 | case JS_SET_TIMELIMIT: | 637 | retval = -ENODEV; |
462 | return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); | 638 | goto out; |
463 | case JS_GET_TIMELIMIT: | ||
464 | return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); | ||
465 | case JS_SET_ALL: | ||
466 | return copy_from_user(&joydev->glue, argp, | ||
467 | sizeof(joydev->glue)) ? -EFAULT : 0; | ||
468 | case JS_GET_ALL: | ||
469 | return copy_to_user(argp, &joydev->glue, | ||
470 | sizeof(joydev->glue)) ? -EFAULT : 0; | ||
471 | default: | ||
472 | return joydev_ioctl_common(joydev, cmd, argp); | ||
473 | } | 639 | } |
640 | |||
641 | switch (cmd) { | ||
642 | |||
643 | case JS_SET_TIMELIMIT: | ||
644 | retval = get_user(joydev->glue.JS_TIMELIMIT, | ||
645 | (long __user *) arg); | ||
646 | break; | ||
647 | |||
648 | case JS_GET_TIMELIMIT: | ||
649 | retval = put_user(joydev->glue.JS_TIMELIMIT, | ||
650 | (long __user *) arg); | ||
651 | break; | ||
652 | |||
653 | case JS_SET_ALL: | ||
654 | retval = copy_from_user(&joydev->glue, argp, | ||
655 | sizeof(joydev->glue)) ? -EFAULT: 0; | ||
656 | break; | ||
657 | |||
658 | case JS_GET_ALL: | ||
659 | retval = copy_to_user(argp, &joydev->glue, | ||
660 | sizeof(joydev->glue)) ? -EFAULT : 0; | ||
661 | break; | ||
662 | |||
663 | default: | ||
664 | retval = joydev_ioctl_common(joydev, cmd, argp); | ||
665 | break; | ||
666 | } | ||
667 | out: | ||
668 | mutex_unlock(&joydev->mutex); | ||
669 | return retval; | ||
474 | } | 670 | } |
475 | 671 | ||
476 | static const struct file_operations joydev_fops = { | 672 | static const struct file_operations joydev_fops = { |
477 | .owner = THIS_MODULE, | 673 | .owner = THIS_MODULE, |
478 | .read = joydev_read, | 674 | .read = joydev_read, |
479 | .write = joydev_write, | 675 | .poll = joydev_poll, |
480 | .poll = joydev_poll, | 676 | .open = joydev_open, |
481 | .open = joydev_open, | 677 | .release = joydev_release, |
482 | .release = joydev_release, | 678 | .unlocked_ioctl = joydev_ioctl, |
483 | .ioctl = joydev_ioctl, | ||
484 | #ifdef CONFIG_COMPAT | 679 | #ifdef CONFIG_COMPAT |
485 | .compat_ioctl = joydev_compat_ioctl, | 680 | .compat_ioctl = joydev_compat_ioctl, |
486 | #endif | 681 | #endif |
487 | .fasync = joydev_fasync, | 682 | .fasync = joydev_fasync, |
488 | }; | 683 | }; |
489 | 684 | ||
685 | static int joydev_install_chrdev(struct joydev *joydev) | ||
686 | { | ||
687 | joydev_table[joydev->minor] = joydev; | ||
688 | return 0; | ||
689 | } | ||
690 | |||
691 | static void joydev_remove_chrdev(struct joydev *joydev) | ||
692 | { | ||
693 | mutex_lock(&joydev_table_mutex); | ||
694 | joydev_table[joydev->minor] = NULL; | ||
695 | mutex_unlock(&joydev_table_mutex); | ||
696 | } | ||
697 | |||
698 | /* | ||
699 | * Mark device non-existant. This disables writes, ioctls and | ||
700 | * prevents new users from opening the device. Already posted | ||
701 | * blocking reads will stay, however new ones will fail. | ||
702 | */ | ||
703 | static void joydev_mark_dead(struct joydev *joydev) | ||
704 | { | ||
705 | mutex_lock(&joydev->mutex); | ||
706 | joydev->exist = 0; | ||
707 | mutex_unlock(&joydev->mutex); | ||
708 | } | ||
709 | |||
710 | static void joydev_cleanup(struct joydev *joydev) | ||
711 | { | ||
712 | struct input_handle *handle = &joydev->handle; | ||
713 | |||
714 | joydev_mark_dead(joydev); | ||
715 | joydev_hangup(joydev); | ||
716 | joydev_remove_chrdev(joydev); | ||
717 | |||
718 | /* joydev is marked dead so noone else accesses joydev->open */ | ||
719 | if (joydev->open) | ||
720 | input_close_device(handle); | ||
721 | } | ||
722 | |||
490 | static int joydev_connect(struct input_handler *handler, struct input_dev *dev, | 723 | static int joydev_connect(struct input_handler *handler, struct input_dev *dev, |
491 | const struct input_device_id *id) | 724 | const struct input_device_id *id) |
492 | { | 725 | { |
@@ -494,7 +727,10 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, | |||
494 | int i, j, t, minor; | 727 | int i, j, t, minor; |
495 | int error; | 728 | int error; |
496 | 729 | ||
497 | for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); | 730 | for (minor = 0; minor < JOYDEV_MINORS; minor++) |
731 | if (!joydev_table[minor]) | ||
732 | break; | ||
733 | |||
498 | if (minor == JOYDEV_MINORS) { | 734 | if (minor == JOYDEV_MINORS) { |
499 | printk(KERN_ERR "joydev: no more free joydev devices\n"); | 735 | printk(KERN_ERR "joydev: no more free joydev devices\n"); |
500 | return -ENFILE; | 736 | return -ENFILE; |
@@ -505,15 +741,19 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, | |||
505 | return -ENOMEM; | 741 | return -ENOMEM; |
506 | 742 | ||
507 | INIT_LIST_HEAD(&joydev->client_list); | 743 | INIT_LIST_HEAD(&joydev->client_list); |
744 | spin_lock_init(&joydev->client_lock); | ||
745 | mutex_init(&joydev->mutex); | ||
508 | init_waitqueue_head(&joydev->wait); | 746 | init_waitqueue_head(&joydev->wait); |
509 | 747 | ||
748 | snprintf(joydev->name, sizeof(joydev->name), "js%d", minor); | ||
749 | joydev->exist = 1; | ||
510 | joydev->minor = minor; | 750 | joydev->minor = minor; |
751 | |||
511 | joydev->exist = 1; | 752 | joydev->exist = 1; |
512 | joydev->handle.dev = dev; | 753 | joydev->handle.dev = dev; |
513 | joydev->handle.name = joydev->name; | 754 | joydev->handle.name = joydev->name; |
514 | joydev->handle.handler = handler; | 755 | joydev->handle.handler = handler; |
515 | joydev->handle.private = joydev; | 756 | joydev->handle.private = joydev; |
516 | snprintf(joydev->name, sizeof(joydev->name), "js%d", minor); | ||
517 | 757 | ||
518 | for (i = 0; i < ABS_MAX + 1; i++) | 758 | for (i = 0; i < ABS_MAX + 1; i++) |
519 | if (test_bit(i, dev->absbit)) { | 759 | if (test_bit(i, dev->absbit)) { |
@@ -545,67 +785,65 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, | |||
545 | } | 785 | } |
546 | joydev->corr[i].type = JS_CORR_BROKEN; | 786 | joydev->corr[i].type = JS_CORR_BROKEN; |
547 | joydev->corr[i].prec = dev->absfuzz[j]; | 787 | joydev->corr[i].prec = dev->absfuzz[j]; |
548 | joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; | 788 | joydev->corr[i].coef[0] = |
549 | joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; | 789 | (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; |
550 | if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]))) | 790 | joydev->corr[i].coef[1] = |
551 | continue; | 791 | (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; |
552 | joydev->corr[i].coef[2] = (1 << 29) / t; | 792 | |
553 | joydev->corr[i].coef[3] = (1 << 29) / t; | 793 | t = (dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]; |
554 | 794 | if (t) { | |
555 | joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); | 795 | joydev->corr[i].coef[2] = (1 << 29) / t; |
796 | joydev->corr[i].coef[3] = (1 << 29) / t; | ||
797 | |||
798 | joydev->abs[i] = joydev_correct(dev->abs[j], | ||
799 | joydev->corr + i); | ||
800 | } | ||
556 | } | 801 | } |
557 | 802 | ||
558 | snprintf(joydev->dev.bus_id, sizeof(joydev->dev.bus_id), | 803 | strlcpy(joydev->dev.bus_id, joydev->name, sizeof(joydev->dev.bus_id)); |
559 | "js%d", minor); | 804 | joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); |
560 | joydev->dev.class = &input_class; | 805 | joydev->dev.class = &input_class; |
561 | joydev->dev.parent = &dev->dev; | 806 | joydev->dev.parent = &dev->dev; |
562 | joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); | ||
563 | joydev->dev.release = joydev_free; | 807 | joydev->dev.release = joydev_free; |
564 | device_initialize(&joydev->dev); | 808 | device_initialize(&joydev->dev); |
565 | 809 | ||
566 | joydev_table[minor] = joydev; | 810 | error = input_register_handle(&joydev->handle); |
567 | |||
568 | error = device_add(&joydev->dev); | ||
569 | if (error) | 811 | if (error) |
570 | goto err_free_joydev; | 812 | goto err_free_joydev; |
571 | 813 | ||
572 | error = input_register_handle(&joydev->handle); | 814 | error = joydev_install_chrdev(joydev); |
573 | if (error) | 815 | if (error) |
574 | goto err_delete_joydev; | 816 | goto err_unregister_handle; |
817 | |||
818 | error = device_add(&joydev->dev); | ||
819 | if (error) | ||
820 | goto err_cleanup_joydev; | ||
575 | 821 | ||
576 | return 0; | 822 | return 0; |
577 | 823 | ||
578 | err_delete_joydev: | 824 | err_cleanup_joydev: |
579 | device_del(&joydev->dev); | 825 | joydev_cleanup(joydev); |
826 | err_unregister_handle: | ||
827 | input_unregister_handle(&joydev->handle); | ||
580 | err_free_joydev: | 828 | err_free_joydev: |
581 | put_device(&joydev->dev); | 829 | put_device(&joydev->dev); |
582 | return error; | 830 | return error; |
583 | } | 831 | } |
584 | 832 | ||
585 | |||
586 | static void joydev_disconnect(struct input_handle *handle) | 833 | static void joydev_disconnect(struct input_handle *handle) |
587 | { | 834 | { |
588 | struct joydev *joydev = handle->private; | 835 | struct joydev *joydev = handle->private; |
589 | struct joydev_client *client; | ||
590 | 836 | ||
591 | input_unregister_handle(handle); | ||
592 | device_del(&joydev->dev); | 837 | device_del(&joydev->dev); |
593 | 838 | joydev_cleanup(joydev); | |
594 | joydev->exist = 0; | 839 | input_unregister_handle(handle); |
595 | |||
596 | if (joydev->open) { | ||
597 | input_close_device(handle); | ||
598 | list_for_each_entry(client, &joydev->client_list, node) | ||
599 | kill_fasync(&client->fasync, SIGIO, POLL_HUP); | ||
600 | wake_up_interruptible(&joydev->wait); | ||
601 | } | ||
602 | |||
603 | put_device(&joydev->dev); | 840 | put_device(&joydev->dev); |
604 | } | 841 | } |
605 | 842 | ||
606 | static const struct input_device_id joydev_blacklist[] = { | 843 | static const struct input_device_id joydev_blacklist[] = { |
607 | { | 844 | { |
608 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, | 845 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | |
846 | INPUT_DEVICE_ID_MATCH_KEYBIT, | ||
609 | .evbit = { BIT(EV_KEY) }, | 847 | .evbit = { BIT(EV_KEY) }, |
610 | .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, | 848 | .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, |
611 | }, /* Avoid itouchpads, touchscreens and tablets */ | 849 | }, /* Avoid itouchpads, touchscreens and tablets */ |
@@ -614,17 +852,20 @@ static const struct input_device_id joydev_blacklist[] = { | |||
614 | 852 | ||
615 | static const struct input_device_id joydev_ids[] = { | 853 | static const struct input_device_id joydev_ids[] = { |
616 | { | 854 | { |
617 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, | 855 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | |
856 | INPUT_DEVICE_ID_MATCH_ABSBIT, | ||
618 | .evbit = { BIT(EV_ABS) }, | 857 | .evbit = { BIT(EV_ABS) }, |
619 | .absbit = { BIT(ABS_X) }, | 858 | .absbit = { BIT(ABS_X) }, |
620 | }, | 859 | }, |
621 | { | 860 | { |
622 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, | 861 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | |
862 | INPUT_DEVICE_ID_MATCH_ABSBIT, | ||
623 | .evbit = { BIT(EV_ABS) }, | 863 | .evbit = { BIT(EV_ABS) }, |
624 | .absbit = { BIT(ABS_WHEEL) }, | 864 | .absbit = { BIT(ABS_WHEEL) }, |
625 | }, | 865 | }, |
626 | { | 866 | { |
627 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, | 867 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | |
868 | INPUT_DEVICE_ID_MATCH_ABSBIT, | ||
628 | .evbit = { BIT(EV_ABS) }, | 869 | .evbit = { BIT(EV_ABS) }, |
629 | .absbit = { BIT(ABS_THROTTLE) }, | 870 | .absbit = { BIT(ABS_THROTTLE) }, |
630 | }, | 871 | }, |
@@ -634,14 +875,14 @@ static const struct input_device_id joydev_ids[] = { | |||
634 | MODULE_DEVICE_TABLE(input, joydev_ids); | 875 | MODULE_DEVICE_TABLE(input, joydev_ids); |
635 | 876 | ||
636 | static struct input_handler joydev_handler = { | 877 | static struct input_handler joydev_handler = { |
637 | .event = joydev_event, | 878 | .event = joydev_event, |
638 | .connect = joydev_connect, | 879 | .connect = joydev_connect, |
639 | .disconnect = joydev_disconnect, | 880 | .disconnect = joydev_disconnect, |
640 | .fops = &joydev_fops, | 881 | .fops = &joydev_fops, |
641 | .minor = JOYDEV_MINOR_BASE, | 882 | .minor = JOYDEV_MINOR_BASE, |
642 | .name = "joydev", | 883 | .name = "joydev", |
643 | .id_table = joydev_ids, | 884 | .id_table = joydev_ids, |
644 | .blacklist = joydev_blacklist, | 885 | .blacklist = joydev_blacklist, |
645 | }; | 886 | }; |
646 | 887 | ||
647 | static int __init joydev_init(void) | 888 | static int __init joydev_init(void) |