diff options
Diffstat (limited to 'drivers/input/gameport/gameport.c')
| -rw-r--r-- | drivers/input/gameport/gameport.c | 186 |
1 files changed, 87 insertions, 99 deletions
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 46239e47a26..24c41ba7d4e 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -18,13 +18,11 @@ #include <linux/ioport.h> #include <linux/init.h> #include <linux/gameport.h> -#include <linux/wait.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/kthread.h> +#include <linux/workqueue.h> #include <linux/sched.h> /* HZ */ #include <linux/mutex.h> -#include <linux/freezer.h> /*#include <asm/io.h>*/ @@ -49,7 +47,7 @@ static void gameport_disconnect_port(struct gameport *gameport); #if defined(__i386__) -#include <asm/i8253.h> +#include <linux/i8253.h> #define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0)) #define GET_TIME(x) do { x = get_time_pit(); } while (0) @@ -123,7 +121,7 @@ static int gameport_measure_speed(struct gameport *gameport) } gameport_close(gameport); - return (cpu_data(raw_smp_processor_id()).loops_per_jiffy * + return (this_cpu_read(cpu_info.loops_per_jiffy) * (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx); #else @@ -234,58 +232,22 @@ struct gameport_event { static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */ static LIST_HEAD(gameport_event_list); -static DECLARE_WAIT_QUEUE_HEAD(gameport_wait); -static struct task_struct *gameport_task; -static int gameport_queue_event(void *object, struct module *owner, - enum gameport_event_type event_type) +static struct gameport_event *gameport_get_event(void) { + struct gameport_event *event = NULL; unsigned long flags; - struct gameport_event *event; - int retval = 0; spin_lock_irqsave(&gameport_event_lock, flags); - /* - * Scan event list for the other events for the same gameport port, - * starting with the most recent one. If event is the same we - * do not need add new one. If event is of different type we - * need to add this event and should not look further because - * we need to preseve sequence of distinct events. - */ - list_for_each_entry_reverse(event, &gameport_event_list, node) { - if (event->object == object) { - if (event->type == event_type) - goto out; - break; - } - } - - event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC); - if (!event) { - pr_err("Not enough memory to queue event %d\n", event_type); - retval = -ENOMEM; - goto out; - } - - if (!try_module_get(owner)) { - pr_warning("Can't get module reference, dropping event %d\n", - event_type); - kfree(event); - retval = -EINVAL; - goto out; + if (!list_empty(&gameport_event_list)) { + event = list_first_entry(&gameport_event_list, + struct gameport_event, node); + list_del_init(&event->node); } - event->type = event_type; - event->object = object; - event->owner = owner; - - list_add_tail(&event->node, &gameport_event_list); - wake_up(&gameport_wait); - -out: spin_unlock_irqrestore(&gameport_event_lock, flags); - return retval; + return event; } static void gameport_free_event(struct gameport_event *event) @@ -319,24 +281,8 @@ static void gameport_remove_duplicate_events(struct gameport_event *event) spin_unlock_irqrestore(&gameport_event_lock, flags); } -static struct gameport_event *gameport_get_event(void) -{ - struct gameport_event *event = NULL; - unsigned long flags; - - spin_lock_irqsave(&gameport_event_lock, flags); - - if (!list_empty(&gameport_event_list)) { - event = list_first_entry(&gameport_event_list, - struct gameport_event, node); - list_del_init(&event->node); - } - - spin_unlock_irqrestore(&gameport_event_lock, flags); - return event; -} -static void gameport_handle_event(void) +static void gameport_handle_events(struct work_struct *work) { struct gameport_event *event; @@ -368,6 +314,59 @@ static void gameport_handle_event(void) mutex_unlock(&gameport_mutex); } +static DECLARE_WORK(gameport_event_work, gameport_handle_events); + +static int gameport_queue_event(void *object, struct module *owner, + enum gameport_event_type event_type) +{ + unsigned long flags; + struct gameport_event *event; + int retval = 0; + + spin_lock_irqsave(&gameport_event_lock, flags); + + /* + * Scan event list for the other events for the same gameport port, + * starting with the most recent one. If event is the same we + * do not need add new one. If event is of different type we + * need to add this event and should not look further because + * we need to preserve sequence of distinct events. + */ + list_for_each_entry_reverse(event, &gameport_event_list, node) { + if (event->object == object) { + if (event->type == event_type) + goto out; + break; + } + } + + event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC); + if (!event) { + pr_err("Not enough memory to queue event %d\n", event_type); + retval = -ENOMEM; + goto out; + } + + if (!try_module_get(owner)) { + pr_warning("Can't get module reference, dropping event %d\n", + event_type); + kfree(event); + retval = -EINVAL; + goto out; + } + + event->type = event_type; + event->object = object; + event->owner = owner; + + list_add_tail(&event->node, &gameport_event_list); + queue_work(system_long_wq, &gameport_event_work); + +out: + spin_unlock_irqrestore(&gameport_event_lock, flags); + return retval; +} + /* * Remove all events that have been submitted for a given object, * be it a gameport port or a driver. @@ -419,31 +418,19 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent) return child; } -static int gameport_thread(void *nothing) -{ - set_freezable(); - do { - gameport_handle_event(); - wait_event_freezable(gameport_wait, - kthread_should_stop() || !list_empty(&gameport_event_list)); - } while (!kthread_should_stop()); - - return 0; -} - - /* * Gameport port operations */ -static ssize_t gameport_show_description(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t gameport_description_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gameport *gameport = to_gameport_port(dev); return sprintf(buf, "%s\n", gameport->name); } +static DEVICE_ATTR(description, S_IRUGO, gameport_description_show, NULL); -static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct gameport *gameport = to_gameport_port(dev); struct device_driver *drv; @@ -463,7 +450,6 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) { gameport_disconnect_port(gameport); error = gameport_bind_driver(gameport, to_gameport_driver(drv)); - put_driver(drv); } else { error = -EINVAL; } @@ -472,12 +458,14 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut return error ? error : count; } +static DEVICE_ATTR_WO(drvctl); -static struct device_attribute gameport_device_attrs[] = { - __ATTR(description, S_IRUGO, gameport_show_description, NULL), - __ATTR(drvctl, S_IWUSR, NULL, gameport_rebind_driver), - __ATTR_NULL +static struct attribute *gameport_device_attrs[] = { + &dev_attr_description.attr, + &dev_attr_drvctl.attr, + NULL, }; +ATTRIBUTE_GROUPS(gameport_device); static void gameport_release_port(struct device *dev) { @@ -654,16 +642,18 @@ EXPORT_SYMBOL(gameport_unregister_port); * Gameport driver operations */ -static ssize_t gameport_driver_show_description(struct device_driver *drv, char *buf) +static ssize_t description_show(struct device_driver *drv, char *buf) { struct gameport_driver *driver = to_gameport_driver(drv); return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)"); } +static DRIVER_ATTR_RO(description); -static struct driver_attribute gameport_driver_attrs[] = { - __ATTR(description, S_IRUGO, gameport_driver_show_description, NULL), - __ATTR_NULL +static struct attribute *gameport_driver_attrs[] = { + &driver_attr_description.attr, + NULL }; +ATTRIBUTE_GROUPS(gameport_driver); static int gameport_driver_probe(struct device *dev) { @@ -763,8 +753,8 @@ static int gameport_bus_match(struct device *dev, struct device_driver *drv) static struct bus_type gameport_bus = { .name = "gameport", - .dev_attrs = gameport_device_attrs, - .drv_attrs = gameport_driver_attrs, + .dev_groups = gameport_device_groups, + .drv_groups = gameport_driver_groups, .match = gameport_bus_match, .probe = gameport_driver_probe, .remove = gameport_driver_remove, @@ -814,13 +804,6 @@ static int __init gameport_init(void) return error; } - gameport_task = kthread_run(gameport_thread, NULL, "kgameportd"); - if (IS_ERR(gameport_task)) { - bus_unregister(&gameport_bus); - error = PTR_ERR(gameport_task); - pr_err("Failed to start kgameportd, error: %d\n", error); - return error; - } return 0; } @@ -828,7 +811,12 @@ static int __init gameport_init(void) static void __exit gameport_exit(void) { bus_unregister(&gameport_bus); - kthread_stop(gameport_task); + + /* + * There should not be any outstanding events but work may + * still be scheduled so simply cancel it. + */ + cancel_work_sync(&gameport_event_work); } subsys_initcall(gameport_init); |
