blob: 46239e47a2605a41fafb50d6fe66498f554a41f4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Generic gameport layer
3 *
4 * Copyright (c) 1999-2002 Vojtech Pavlik
5 * Copyright (c) 2005 Dmitry Torokhov
6 */
7
8/*
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 */
13
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -080014#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/stddef.h>
17#include <linux/module.h>
18#include <linux/ioport.h>
19#include <linux/init.h>
20#include <linux/gameport.h>
21#include <linux/wait.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/slab.h>
23#include <linux/delay.h>
Linus Torvalds3c803e82005-06-27 17:49:45 -070024#include <linux/kthread.h>
Tim Schmielau4e57b682005-10-30 15:03:48 -080025#include <linux/sched.h> /* HZ */
Arjan van de Ven286295e2006-02-19 00:22:03 -050026#include <linux/mutex.h>
Nigel Cunningham7dfb7102006-12-06 20:34:23 -080027#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29/*#include <asm/io.h>*/
30
31MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
32MODULE_DESCRIPTION("Generic gameport layer");
33MODULE_LICENSE("GPL");
34
Linus Torvalds1da177e2005-04-16 15:20:36 -070035/*
Arjan van de Ven286295e2006-02-19 00:22:03 -050036 * gameport_mutex protects entire gameport subsystem and is taken
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 * every time gameport port or driver registrered or unregistered.
38 */
Arjan van de Ven286295e2006-02-19 00:22:03 -050039static DEFINE_MUTEX(gameport_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
41static LIST_HEAD(gameport_list);
42
Russell King29a4a202006-01-05 14:38:22 +000043static struct bus_type gameport_bus;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45static void gameport_add_port(struct gameport *gameport);
Dmitry Torokhov4ced8e72009-04-13 15:27:49 -070046static void gameport_attach_driver(struct gameport_driver *drv);
Linus Torvalds1da177e2005-04-16 15:20:36 -070047static void gameport_reconnect_port(struct gameport *gameport);
48static void gameport_disconnect_port(struct gameport *gameport);
49
50#if defined(__i386__)
51
Ingo Molnar306e4402005-06-30 02:58:55 -070052#include <asm/i8253.h>
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0))
55#define GET_TIME(x) do { x = get_time_pit(); } while (0)
56
57static unsigned int get_time_pit(void)
58{
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 unsigned long flags;
60 unsigned int count;
61
Thomas Gleixnerced918e2010-02-17 16:47:10 +000062 raw_spin_lock_irqsave(&i8253_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 outb_p(0x00, 0x43);
64 count = inb_p(0x40);
65 count |= inb_p(0x40) << 8;
Thomas Gleixnerced918e2010-02-17 16:47:10 +000066 raw_spin_unlock_irqrestore(&i8253_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68 return count;
69}
70
71#endif
72
73
74
75/*
76 * gameport_measure_speed() measures the gameport i/o speed.
77 */
78
79static int gameport_measure_speed(struct gameport *gameport)
80{
81#if defined(__i386__)
82
83 unsigned int i, t, t1, t2, t3, tx;
84 unsigned long flags;
85
86 if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
87 return 0;
88
89 tx = 1 << 30;
90
91 for(i = 0; i < 50; i++) {
92 local_irq_save(flags);
93 GET_TIME(t1);
94 for (t = 0; t < 50; t++) gameport_read(gameport);
95 GET_TIME(t2);
96 GET_TIME(t3);
97 local_irq_restore(flags);
98 udelay(i * 10);
99 if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t;
100 }
101
102 gameport_close(gameport);
103 return 59659 / (tx < 1 ? 1 : tx);
104
105#elif defined (__x86_64__)
106
107 unsigned int i, t;
108 unsigned long tx, t1, t2, flags;
109
110 if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
111 return 0;
112
113 tx = 1 << 30;
114
115 for(i = 0; i < 50; i++) {
116 local_irq_save(flags);
117 rdtscl(t1);
118 for (t = 0; t < 50; t++) gameport_read(gameport);
119 rdtscl(t2);
120 local_irq_restore(flags);
121 udelay(i * 10);
122 if (t2 - t1 < tx) tx = t2 - t1;
123 }
124
125 gameport_close(gameport);
Mike Travis92cb7612007-10-19 20:35:04 +0200126 return (cpu_data(raw_smp_processor_id()).loops_per_jiffy *
127 (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
129#else
130
131 unsigned int j, t = 0;
132
133 if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
134 return 0;
135
136 j = jiffies; while (j == jiffies);
137 j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); }
138
139 gameport_close(gameport);
140 return t * HZ / 1000;
141
142#endif
143}
144
145void gameport_start_polling(struct gameport *gameport)
146{
147 spin_lock(&gameport->timer_lock);
148
149 if (!gameport->poll_cnt++) {
150 BUG_ON(!gameport->poll_handler);
151 BUG_ON(!gameport->poll_interval);
152 mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));
153 }
154
155 spin_unlock(&gameport->timer_lock);
156}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700157EXPORT_SYMBOL(gameport_start_polling);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159void gameport_stop_polling(struct gameport *gameport)
160{
161 spin_lock(&gameport->timer_lock);
162
163 if (!--gameport->poll_cnt)
164 del_timer(&gameport->poll_timer);
165
166 spin_unlock(&gameport->timer_lock);
167}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700168EXPORT_SYMBOL(gameport_stop_polling);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
170static void gameport_run_poll_handler(unsigned long d)
171{
172 struct gameport *gameport = (struct gameport *)d;
173
174 gameport->poll_handler(gameport);
175 if (gameport->poll_cnt)
176 mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));
177}
178
179/*
180 * Basic gameport -> driver core mappings
181 */
182
Dmitry Torokhov79580052007-04-10 00:40:48 -0400183static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
Dmitry Torokhov23de1512006-10-12 01:06:34 -0400185 int error;
186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 gameport->dev.driver = &drv->driver;
188 if (drv->connect(gameport, drv)) {
189 gameport->dev.driver = NULL;
Dmitry Torokhov79580052007-04-10 00:40:48 -0400190 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 }
Dmitry Torokhov23de1512006-10-12 01:06:34 -0400192
193 error = device_bind_driver(&gameport->dev);
194 if (error) {
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800195 dev_warn(&gameport->dev,
196 "device_bind_driver() failed for %s (%s) and %s, error: %d\n",
Dmitry Torokhov23de1512006-10-12 01:06:34 -0400197 gameport->phys, gameport->name,
198 drv->description, error);
199 drv->disconnect(gameport);
200 gameport->dev.driver = NULL;
Dmitry Torokhov79580052007-04-10 00:40:48 -0400201 return error;
Dmitry Torokhov23de1512006-10-12 01:06:34 -0400202 }
203
Dmitry Torokhov79580052007-04-10 00:40:48 -0400204 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205}
206
207static void gameport_find_driver(struct gameport *gameport)
208{
Randy Dunlap73b59a32006-07-19 01:14:55 -0400209 int error;
210
Randy Dunlap73b59a32006-07-19 01:14:55 -0400211 error = device_attach(&gameport->dev);
212 if (error < 0)
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800213 dev_warn(&gameport->dev,
214 "device_attach() failed for %s (%s), error: %d\n",
215 gameport->phys, gameport->name, error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216}
217
218
219/*
220 * Gameport event processing.
221 */
222
223enum gameport_event_type {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 GAMEPORT_REGISTER_PORT,
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400225 GAMEPORT_ATTACH_DRIVER,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226};
227
228struct gameport_event {
229 enum gameport_event_type type;
230 void *object;
231 struct module *owner;
232 struct list_head node;
233};
234
235static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */
236static LIST_HEAD(gameport_event_list);
237static DECLARE_WAIT_QUEUE_HEAD(gameport_wait);
Linus Torvalds3c803e82005-06-27 17:49:45 -0700238static struct task_struct *gameport_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400240static int gameport_queue_event(void *object, struct module *owner,
241 enum gameport_event_type event_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 unsigned long flags;
244 struct gameport_event *event;
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400245 int retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 spin_lock_irqsave(&gameport_event_lock, flags);
248
249 /*
Linus Torvalds3c803e82005-06-27 17:49:45 -0700250 * Scan event list for the other events for the same gameport port,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 * starting with the most recent one. If event is the same we
252 * do not need add new one. If event is of different type we
253 * need to add this event and should not look further because
254 * we need to preseve sequence of distinct events.
Linus Torvalds3c803e82005-06-27 17:49:45 -0700255 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 list_for_each_entry_reverse(event, &gameport_event_list, node) {
257 if (event->object == object) {
258 if (event->type == event_type)
259 goto out;
260 break;
261 }
262 }
263
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400264 event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
265 if (!event) {
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800266 pr_err("Not enough memory to queue event %d\n", event_type);
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400267 retval = -ENOMEM;
268 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 }
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400270
271 if (!try_module_get(owner)) {
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800272 pr_warning("Can't get module reference, dropping event %d\n",
273 event_type);
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400274 kfree(event);
275 retval = -EINVAL;
276 goto out;
277 }
278
279 event->type = event_type;
280 event->object = object;
281 event->owner = owner;
282
283 list_add_tail(&event->node, &gameport_event_list);
284 wake_up(&gameport_wait);
285
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286out:
287 spin_unlock_irqrestore(&gameport_event_lock, flags);
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400288 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289}
290
291static void gameport_free_event(struct gameport_event *event)
292{
293 module_put(event->owner);
294 kfree(event);
295}
296
297static void gameport_remove_duplicate_events(struct gameport_event *event)
298{
Dmitry Torokhovd621af42010-01-05 17:56:03 -0800299 struct gameport_event *e, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 unsigned long flags;
301
302 spin_lock_irqsave(&gameport_event_lock, flags);
303
Dmitry Torokhovd621af42010-01-05 17:56:03 -0800304 list_for_each_entry_safe(e, next, &gameport_event_list, node) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 if (event->object == e->object) {
306 /*
307 * If this event is of different type we should not
308 * look further - we only suppress duplicate events
309 * that were sent back-to-back.
310 */
311 if (event->type != e->type)
312 break;
313
Dmitry Torokhovd621af42010-01-05 17:56:03 -0800314 list_del_init(&e->node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 gameport_free_event(e);
316 }
317 }
318
319 spin_unlock_irqrestore(&gameport_event_lock, flags);
320}
321
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322static struct gameport_event *gameport_get_event(void)
323{
Dmitry Torokhovd621af42010-01-05 17:56:03 -0800324 struct gameport_event *event = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 unsigned long flags;
326
327 spin_lock_irqsave(&gameport_event_lock, flags);
328
Dmitry Torokhovd621af42010-01-05 17:56:03 -0800329 if (!list_empty(&gameport_event_list)) {
330 event = list_first_entry(&gameport_event_list,
331 struct gameport_event, node);
332 list_del_init(&event->node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
334
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 spin_unlock_irqrestore(&gameport_event_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 return event;
337}
338
Dmitry Torokhov9e50afd2005-11-20 00:56:43 -0500339static void gameport_handle_event(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
341 struct gameport_event *event;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Arjan van de Ven286295e2006-02-19 00:22:03 -0500343 mutex_lock(&gameport_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
Dmitry Torokhov9e50afd2005-11-20 00:56:43 -0500345 /*
346 * Note that we handle only one event here to give swsusp
347 * a chance to freeze kgameportd thread. Gameport events
348 * should be pretty rare so we are not concerned about
349 * taking performance hit.
350 */
351 if ((event = gameport_get_event())) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
353 switch (event->type) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800355 case GAMEPORT_REGISTER_PORT:
356 gameport_add_port(event->object);
357 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800359 case GAMEPORT_ATTACH_DRIVER:
360 gameport_attach_driver(event->object);
361 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
363
364 gameport_remove_duplicate_events(event);
365 gameport_free_event(event);
366 }
367
Arjan van de Ven286295e2006-02-19 00:22:03 -0500368 mutex_unlock(&gameport_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369}
370
371/*
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400372 * Remove all events that have been submitted for a given object,
373 * be it a gameport port or a driver.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 */
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400375static void gameport_remove_pending_events(void *object)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376{
Dmitry Torokhovd621af42010-01-05 17:56:03 -0800377 struct gameport_event *event, *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 unsigned long flags;
379
380 spin_lock_irqsave(&gameport_event_lock, flags);
381
Dmitry Torokhovd621af42010-01-05 17:56:03 -0800382 list_for_each_entry_safe(event, next, &gameport_event_list, node) {
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400383 if (event->object == object) {
Dmitry Torokhovd621af42010-01-05 17:56:03 -0800384 list_del_init(&event->node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 gameport_free_event(event);
386 }
387 }
388
389 spin_unlock_irqrestore(&gameport_event_lock, flags);
390}
391
392/*
393 * Destroy child gameport port (if any) that has not been fully registered yet.
394 *
395 * Note that we rely on the fact that port can have only one child and therefore
396 * only one child registration request can be pending. Additionally, children
397 * are registered by driver's connect() handler so there can't be a grandchild
398 * pending registration together with a child.
399 */
400static struct gameport *gameport_get_pending_child(struct gameport *parent)
401{
402 struct gameport_event *event;
403 struct gameport *gameport, *child = NULL;
404 unsigned long flags;
405
406 spin_lock_irqsave(&gameport_event_lock, flags);
407
408 list_for_each_entry(event, &gameport_event_list, node) {
409 if (event->type == GAMEPORT_REGISTER_PORT) {
410 gameport = event->object;
411 if (gameport->parent == parent) {
412 child = gameport;
413 break;
414 }
415 }
416 }
417
418 spin_unlock_irqrestore(&gameport_event_lock, flags);
419 return child;
420}
421
422static int gameport_thread(void *nothing)
423{
Rafael J. Wysocki83144182007-07-17 04:03:35 -0700424 set_freezable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 do {
Dmitry Torokhov9e50afd2005-11-20 00:56:43 -0500426 gameport_handle_event();
Rafael J. Wysockie42837b2007-10-18 03:04:45 -0700427 wait_event_freezable(gameport_wait,
Linus Torvalds3c803e82005-06-27 17:49:45 -0700428 kthread_should_stop() || !list_empty(&gameport_event_list));
Linus Torvalds3c803e82005-06-27 17:49:45 -0700429 } while (!kthread_should_stop());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
Linus Torvalds3c803e82005-06-27 17:49:45 -0700431 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432}
433
434
435/*
436 * Gameport port operations
437 */
438
Yani Ioannoue404e272005-05-17 06:42:58 -0400439static ssize_t gameport_show_description(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440{
441 struct gameport *gameport = to_gameport_port(dev);
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 return sprintf(buf, "%s\n", gameport->name);
444}
445
Yani Ioannoue404e272005-05-17 06:42:58 -0400446static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
448 struct gameport *gameport = to_gameport_port(dev);
449 struct device_driver *drv;
Dmitry Torokhov79580052007-04-10 00:40:48 -0400450 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
Dmitry Torokhov79580052007-04-10 00:40:48 -0400452 error = mutex_lock_interruptible(&gameport_mutex);
453 if (error)
454 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 if (!strncmp(buf, "none", count)) {
457 gameport_disconnect_port(gameport);
458 } else if (!strncmp(buf, "reconnect", count)) {
459 gameport_reconnect_port(gameport);
460 } else if (!strncmp(buf, "rescan", count)) {
461 gameport_disconnect_port(gameport);
462 gameport_find_driver(gameport);
463 } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
464 gameport_disconnect_port(gameport);
Dmitry Torokhov79580052007-04-10 00:40:48 -0400465 error = gameport_bind_driver(gameport, to_gameport_driver(drv));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 put_driver(drv);
467 } else {
Dmitry Torokhov79580052007-04-10 00:40:48 -0400468 error = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
470
Arjan van de Ven286295e2006-02-19 00:22:03 -0500471 mutex_unlock(&gameport_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
Dmitry Torokhov79580052007-04-10 00:40:48 -0400473 return error ? error : count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474}
475
476static struct device_attribute gameport_device_attrs[] = {
477 __ATTR(description, S_IRUGO, gameport_show_description, NULL),
478 __ATTR(drvctl, S_IWUSR, NULL, gameport_rebind_driver),
479 __ATTR_NULL
480};
481
482static void gameport_release_port(struct device *dev)
483{
484 struct gameport *gameport = to_gameport_port(dev);
485
486 kfree(gameport);
487 module_put(THIS_MODULE);
488}
489
490void gameport_set_phys(struct gameport *gameport, const char *fmt, ...)
491{
492 va_list args;
493
494 va_start(args, fmt);
495 vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args);
496 va_end(args);
497}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700498EXPORT_SYMBOL(gameport_set_phys);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
500/*
501 * Prepare gameport port for registration.
502 */
503static void gameport_init_port(struct gameport *gameport)
504{
505 static atomic_t gameport_no = ATOMIC_INIT(0);
506
507 __module_get(THIS_MODULE);
508
Arjan van de Ven286295e2006-02-19 00:22:03 -0500509 mutex_init(&gameport->drv_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 device_initialize(&gameport->dev);
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800511 dev_set_name(&gameport->dev, "gameport%lu",
512 (unsigned long)atomic_inc_return(&gameport_no) - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 gameport->dev.bus = &gameport_bus;
514 gameport->dev.release = gameport_release_port;
515 if (gameport->parent)
516 gameport->dev.parent = &gameport->parent->dev;
517
Randy Dunlap73b59a32006-07-19 01:14:55 -0400518 INIT_LIST_HEAD(&gameport->node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 spin_lock_init(&gameport->timer_lock);
520 init_timer(&gameport->poll_timer);
521 gameport->poll_timer.function = gameport_run_poll_handler;
522 gameport->poll_timer.data = (unsigned long)gameport;
523}
524
525/*
526 * Complete gameport port registration.
527 * Driver core will attempt to find appropriate driver for the port.
528 */
529static void gameport_add_port(struct gameport *gameport)
530{
Randy Dunlap73b59a32006-07-19 01:14:55 -0400531 int error;
532
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 if (gameport->parent)
534 gameport->parent->child = gameport;
535
536 gameport->speed = gameport_measure_speed(gameport);
537
538 list_add_tail(&gameport->node, &gameport_list);
539
540 if (gameport->io)
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800541 dev_info(&gameport->dev, "%s is %s, io %#x, speed %dkHz\n",
542 gameport->name, gameport->phys, gameport->io, gameport->speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 else
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800544 dev_info(&gameport->dev, "%s is %s, speed %dkHz\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 gameport->name, gameport->phys, gameport->speed);
546
Randy Dunlap73b59a32006-07-19 01:14:55 -0400547 error = device_add(&gameport->dev);
548 if (error)
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800549 dev_err(&gameport->dev,
550 "device_add() failed for %s (%s), error: %d\n",
Randy Dunlap73b59a32006-07-19 01:14:55 -0400551 gameport->phys, gameport->name, error);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552}
553
554/*
555 * gameport_destroy_port() completes deregistration process and removes
556 * port from the system
557 */
558static void gameport_destroy_port(struct gameport *gameport)
559{
560 struct gameport *child;
561
562 child = gameport_get_pending_child(gameport);
563 if (child) {
564 gameport_remove_pending_events(child);
565 put_device(&child->dev);
566 }
567
568 if (gameport->parent) {
569 gameport->parent->child = NULL;
570 gameport->parent = NULL;
571 }
572
Dmitry Torokhov361b7b52010-01-05 17:56:03 -0800573 if (device_is_registered(&gameport->dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 device_del(&gameport->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
Randy Dunlap73b59a32006-07-19 01:14:55 -0400576 list_del_init(&gameport->node);
577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 gameport_remove_pending_events(gameport);
579 put_device(&gameport->dev);
580}
581
582/*
583 * Reconnect gameport port and all its children (re-initialize attached devices)
584 */
585static void gameport_reconnect_port(struct gameport *gameport)
586{
587 do {
588 if (!gameport->drv || !gameport->drv->reconnect || gameport->drv->reconnect(gameport)) {
589 gameport_disconnect_port(gameport);
590 gameport_find_driver(gameport);
591 /* Ok, old children are now gone, we are done */
592 break;
593 }
594 gameport = gameport->child;
595 } while (gameport);
596}
597
598/*
599 * gameport_disconnect_port() unbinds a port from its driver. As a side effect
600 * all child ports are unbound and destroyed.
601 */
602static void gameport_disconnect_port(struct gameport *gameport)
603{
604 struct gameport *s, *parent;
605
606 if (gameport->child) {
607 /*
608 * Children ports should be disconnected and destroyed
609 * first, staring with the leaf one, since we don't want
610 * to do recursion
611 */
612 for (s = gameport; s->child; s = s->child)
613 /* empty */;
614
615 do {
616 parent = s->parent;
617
Dmitry Torokhov79580052007-04-10 00:40:48 -0400618 device_release_driver(&s->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 gameport_destroy_port(s);
620 } while ((s = parent) != gameport);
621 }
622
623 /*
624 * Ok, no children left, now disconnect this port
625 */
Dmitry Torokhov79580052007-04-10 00:40:48 -0400626 device_release_driver(&gameport->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627}
628
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629/*
630 * Submits register request to kgameportd for subsequent execution.
631 * Note that port registration is always asynchronous.
632 */
633void __gameport_register_port(struct gameport *gameport, struct module *owner)
634{
635 gameport_init_port(gameport);
636 gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT);
637}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700638EXPORT_SYMBOL(__gameport_register_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640/*
641 * Synchronously unregisters gameport port.
642 */
643void gameport_unregister_port(struct gameport *gameport)
644{
Arjan van de Ven286295e2006-02-19 00:22:03 -0500645 mutex_lock(&gameport_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 gameport_disconnect_port(gameport);
647 gameport_destroy_port(gameport);
Arjan van de Ven286295e2006-02-19 00:22:03 -0500648 mutex_unlock(&gameport_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700650EXPORT_SYMBOL(gameport_unregister_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
652
653/*
654 * Gameport driver operations
655 */
656
657static ssize_t gameport_driver_show_description(struct device_driver *drv, char *buf)
658{
659 struct gameport_driver *driver = to_gameport_driver(drv);
660 return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
661}
662
663static struct driver_attribute gameport_driver_attrs[] = {
664 __ATTR(description, S_IRUGO, gameport_driver_show_description, NULL),
665 __ATTR_NULL
666};
667
668static int gameport_driver_probe(struct device *dev)
669{
670 struct gameport *gameport = to_gameport_port(dev);
671 struct gameport_driver *drv = to_gameport_driver(dev->driver);
672
673 drv->connect(gameport, drv);
674 return gameport->drv ? 0 : -ENODEV;
675}
676
677static int gameport_driver_remove(struct device *dev)
678{
679 struct gameport *gameport = to_gameport_port(dev);
680 struct gameport_driver *drv = to_gameport_driver(dev->driver);
681
682 drv->disconnect(gameport);
683 return 0;
684}
685
Dmitry Torokhov4ced8e72009-04-13 15:27:49 -0700686static void gameport_attach_driver(struct gameport_driver *drv)
Randy Dunlap73b59a32006-07-19 01:14:55 -0400687{
688 int error;
689
Dmitry Torokhov4ced8e72009-04-13 15:27:49 -0700690 error = driver_attach(&drv->driver);
Randy Dunlap73b59a32006-07-19 01:14:55 -0400691 if (error)
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800692 pr_err("driver_attach() failed for %s, error: %d\n",
Randy Dunlap73b59a32006-07-19 01:14:55 -0400693 drv->driver.name, error);
694}
695
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400696int __gameport_register_driver(struct gameport_driver *drv, struct module *owner,
697 const char *mod_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698{
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400699 int error;
700
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 drv->driver.bus = &gameport_bus;
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400702 drv->driver.owner = owner;
703 drv->driver.mod_name = mod_name;
704
705 /*
706 * Temporarily disable automatic binding because probing
707 * takes long time and we are better off doing it in kgameportd
708 */
Dmitry Torokhov7e044e02009-05-09 16:08:05 -0700709 drv->ignore = true;
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400710
711 error = driver_register(&drv->driver);
712 if (error) {
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800713 pr_err("driver_register() failed for %s, error: %d\n",
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400714 drv->driver.name, error);
715 return error;
716 }
717
718 /*
719 * Reset ignore flag and let kgameportd bind the driver to free ports
720 */
Dmitry Torokhov7e044e02009-05-09 16:08:05 -0700721 drv->ignore = false;
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400722 error = gameport_queue_event(drv, NULL, GAMEPORT_ATTACH_DRIVER);
723 if (error) {
724 driver_unregister(&drv->driver);
725 return error;
726 }
727
728 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700730EXPORT_SYMBOL(__gameport_register_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731
732void gameport_unregister_driver(struct gameport_driver *drv)
733{
734 struct gameport *gameport;
735
Arjan van de Ven286295e2006-02-19 00:22:03 -0500736 mutex_lock(&gameport_mutex);
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400737
Dmitry Torokhov7e044e02009-05-09 16:08:05 -0700738 drv->ignore = true; /* so gameport_find_driver ignores it */
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400739 gameport_remove_pending_events(drv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
741start_over:
742 list_for_each_entry(gameport, &gameport_list, node) {
743 if (gameport->drv == drv) {
744 gameport_disconnect_port(gameport);
745 gameport_find_driver(gameport);
746 /* we could've deleted some ports, restart */
747 goto start_over;
748 }
749 }
750
751 driver_unregister(&drv->driver);
Dmitry Torokhov6902c0b2008-06-06 01:33:22 -0400752
Arjan van de Ven286295e2006-02-19 00:22:03 -0500753 mutex_unlock(&gameport_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700755EXPORT_SYMBOL(gameport_unregister_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
757static int gameport_bus_match(struct device *dev, struct device_driver *drv)
758{
759 struct gameport_driver *gameport_drv = to_gameport_driver(drv);
760
761 return !gameport_drv->ignore;
762}
763
Dmitry Torokhovb187dd72006-11-02 23:27:30 -0500764static struct bus_type gameport_bus = {
765 .name = "gameport",
766 .dev_attrs = gameport_device_attrs,
767 .drv_attrs = gameport_driver_attrs,
768 .match = gameport_bus_match,
769 .probe = gameport_driver_probe,
770 .remove = gameport_driver_remove,
771};
772
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv)
774{
Arjan van de Ven286295e2006-02-19 00:22:03 -0500775 mutex_lock(&gameport->drv_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 gameport->drv = drv;
Arjan van de Ven286295e2006-02-19 00:22:03 -0500777 mutex_unlock(&gameport->drv_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778}
779
780int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode)
781{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 if (gameport->open) {
783 if (gameport->open(gameport, mode)) {
784 return -1;
785 }
786 } else {
787 if (mode != GAMEPORT_MODE_RAW)
788 return -1;
789 }
790
791 gameport_set_drv(gameport, drv);
792 return 0;
793}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700794EXPORT_SYMBOL(gameport_open);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796void gameport_close(struct gameport *gameport)
797{
798 del_timer_sync(&gameport->poll_timer);
799 gameport->poll_handler = NULL;
800 gameport->poll_interval = 0;
801 gameport_set_drv(gameport, NULL);
802 if (gameport->close)
803 gameport->close(gameport);
804}
Dmitry Torokhov3e650672009-04-17 20:12:05 -0700805EXPORT_SYMBOL(gameport_close);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
807static int __init gameport_init(void)
808{
Randy Dunlap73b59a32006-07-19 01:14:55 -0400809 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Randy Dunlap73b59a32006-07-19 01:14:55 -0400811 error = bus_register(&gameport_bus);
812 if (error) {
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800813 pr_err("failed to register gameport bus, error: %d\n", error);
Randy Dunlap73b59a32006-07-19 01:14:55 -0400814 return error;
815 }
816
817 gameport_task = kthread_run(gameport_thread, NULL, "kgameportd");
818 if (IS_ERR(gameport_task)) {
819 bus_unregister(&gameport_bus);
820 error = PTR_ERR(gameport_task);
Dmitry Torokhovfc99ec62010-01-05 17:56:03 -0800821 pr_err("Failed to start kgameportd, error: %d\n", error);
Randy Dunlap73b59a32006-07-19 01:14:55 -0400822 return error;
823 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
825 return 0;
826}
827
828static void __exit gameport_exit(void)
829{
830 bus_unregister(&gameport_bus);
Linus Torvalds3c803e82005-06-27 17:49:45 -0700831 kthread_stop(gameport_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832}
833
Dmitry Torokhov51c38f92006-02-19 00:22:51 -0500834subsys_initcall(gameport_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835module_exit(gameport_exit);