📄 gameport.c
字号:
/* * Generic gameport layer * * Copyright (c) 1999-2002 Vojtech Pavlik * Copyright (c) 2005 Dmitry Torokhov *//* * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */#include <linux/stddef.h>#include <linux/module.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/gameport.h>#include <linux/wait.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/kthread.h>#include <linux/sched.h> /* HZ *//*#include <asm/io.h>*/MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");MODULE_DESCRIPTION("Generic gameport layer");MODULE_LICENSE("GPL");EXPORT_SYMBOL(__gameport_register_port);EXPORT_SYMBOL(gameport_unregister_port);EXPORT_SYMBOL(__gameport_register_driver);EXPORT_SYMBOL(gameport_unregister_driver);EXPORT_SYMBOL(gameport_open);EXPORT_SYMBOL(gameport_close);EXPORT_SYMBOL(gameport_rescan);EXPORT_SYMBOL(gameport_cooked_read);EXPORT_SYMBOL(gameport_set_name);EXPORT_SYMBOL(gameport_set_phys);EXPORT_SYMBOL(gameport_start_polling);EXPORT_SYMBOL(gameport_stop_polling);/* * gameport_sem protects entire gameport subsystem and is taken * every time gameport port or driver registrered or unregistered. */static DECLARE_MUTEX(gameport_sem);static LIST_HEAD(gameport_list);static struct bus_type gameport_bus = { .name = "gameport",};static void gameport_add_port(struct gameport *gameport);static void gameport_destroy_port(struct gameport *gameport);static void gameport_reconnect_port(struct gameport *gameport);static void gameport_disconnect_port(struct gameport *gameport);#if defined(__i386__)#include <asm/i8253.h>#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182/HZ:0))#define GET_TIME(x) do { x = get_time_pit(); } while (0)static unsigned int get_time_pit(void){ unsigned long flags; unsigned int count; spin_lock_irqsave(&i8253_lock, flags); outb_p(0x00, 0x43); count = inb_p(0x40); count |= inb_p(0x40) << 8; spin_unlock_irqrestore(&i8253_lock, flags); return count;}#endif/* * gameport_measure_speed() measures the gameport i/o speed. */static int gameport_measure_speed(struct gameport *gameport){#if defined(__i386__) unsigned int i, t, t1, t2, t3, tx; unsigned long flags; if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) return 0; tx = 1 << 30; for(i = 0; i < 50; i++) { local_irq_save(flags); GET_TIME(t1); for (t = 0; t < 50; t++) gameport_read(gameport); GET_TIME(t2); GET_TIME(t3); local_irq_restore(flags); udelay(i * 10); if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; } gameport_close(gameport); return 59659 / (tx < 1 ? 1 : tx);#elif defined (__x86_64__) unsigned int i, t; unsigned long tx, t1, t2, flags; if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) return 0; tx = 1 << 30; for(i = 0; i < 50; i++) { local_irq_save(flags); rdtscl(t1); for (t = 0; t < 50; t++) gameport_read(gameport); rdtscl(t2); local_irq_restore(flags); udelay(i * 10); if (t2 - t1 < tx) tx = t2 - t1; } gameport_close(gameport); return (cpu_data[raw_smp_processor_id()].loops_per_jiffy * (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx);#else unsigned int j, t = 0; if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) return 0; j = jiffies; while (j == jiffies); j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); } gameport_close(gameport); return t * HZ / 1000;#endif}void gameport_start_polling(struct gameport *gameport){ spin_lock(&gameport->timer_lock); if (!gameport->poll_cnt++) { BUG_ON(!gameport->poll_handler); BUG_ON(!gameport->poll_interval); mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval)); } spin_unlock(&gameport->timer_lock);}void gameport_stop_polling(struct gameport *gameport){ spin_lock(&gameport->timer_lock); if (!--gameport->poll_cnt) del_timer(&gameport->poll_timer); spin_unlock(&gameport->timer_lock);}static void gameport_run_poll_handler(unsigned long d){ struct gameport *gameport = (struct gameport *)d; gameport->poll_handler(gameport); if (gameport->poll_cnt) mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));}/* * Basic gameport -> driver core mappings */static void gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv){ down_write(&gameport_bus.subsys.rwsem); gameport->dev.driver = &drv->driver; if (drv->connect(gameport, drv)) { gameport->dev.driver = NULL; goto out; } device_bind_driver(&gameport->dev);out: up_write(&gameport_bus.subsys.rwsem);}static void gameport_release_driver(struct gameport *gameport){ down_write(&gameport_bus.subsys.rwsem); device_release_driver(&gameport->dev); up_write(&gameport_bus.subsys.rwsem);}static void gameport_find_driver(struct gameport *gameport){ down_write(&gameport_bus.subsys.rwsem); device_attach(&gameport->dev); up_write(&gameport_bus.subsys.rwsem);}/* * Gameport event processing. */enum gameport_event_type { GAMEPORT_RESCAN, GAMEPORT_RECONNECT, GAMEPORT_REGISTER_PORT, GAMEPORT_REGISTER_DRIVER,};struct gameport_event { enum gameport_event_type type; void *object; struct module *owner; struct list_head node;};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 void gameport_queue_event(void *object, struct module *owner, enum gameport_event_type event_type){ unsigned long flags; struct gameport_event *event; 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; } } if ((event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC))) { if (!try_module_get(owner)) { printk(KERN_WARNING "gameport: Can't get module reference, dropping event %d\n", event_type); goto out; } event->type = event_type; event->object = object; event->owner = owner; list_add_tail(&event->node, &gameport_event_list); wake_up(&gameport_wait); } else { printk(KERN_ERR "gameport: Not enough memory to queue event %d\n", event_type); }out: spin_unlock_irqrestore(&gameport_event_lock, flags);}static void gameport_free_event(struct gameport_event *event){ module_put(event->owner); kfree(event);}static void gameport_remove_duplicate_events(struct gameport_event *event){ struct list_head *node, *next; struct gameport_event *e; unsigned long flags; spin_lock_irqsave(&gameport_event_lock, flags); list_for_each_safe(node, next, &gameport_event_list) { e = list_entry(node, struct gameport_event, node); if (event->object == e->object) { /* * If this event is of different type we should not * look further - we only suppress duplicate events * that were sent back-to-back. */ if (event->type != e->type) break; list_del_init(node); gameport_free_event(e); } } spin_unlock_irqrestore(&gameport_event_lock, flags);}static struct gameport_event *gameport_get_event(void){ struct gameport_event *event; struct list_head *node; unsigned long flags; spin_lock_irqsave(&gameport_event_lock, flags); if (list_empty(&gameport_event_list)) { spin_unlock_irqrestore(&gameport_event_lock, flags); return NULL; } node = gameport_event_list.next; event = list_entry(node, struct gameport_event, node); list_del_init(node); spin_unlock_irqrestore(&gameport_event_lock, flags); return event;}static void gameport_handle_event(void){ struct gameport_event *event; struct gameport_driver *gameport_drv; down(&gameport_sem); /* * Note that we handle only one event here to give swsusp * a chance to freeze kgameportd thread. Gameport events * should be pretty rare so we are not concerned about * taking performance hit. */ if ((event = gameport_get_event())) { switch (event->type) { case GAMEPORT_REGISTER_PORT: gameport_add_port(event->object); break; case GAMEPORT_RECONNECT: gameport_reconnect_port(event->object); break; case GAMEPORT_RESCAN: gameport_disconnect_port(event->object); gameport_find_driver(event->object); break; case GAMEPORT_REGISTER_DRIVER: gameport_drv = event->object; driver_register(&gameport_drv->driver); break; default: break; } gameport_remove_duplicate_events(event); gameport_free_event(event); } up(&gameport_sem);}/* * Remove all events that have been submitted for a given gameport port. */static void gameport_remove_pending_events(struct gameport *gameport){ struct list_head *node, *next; struct gameport_event *event; unsigned long flags; spin_lock_irqsave(&gameport_event_lock, flags); list_for_each_safe(node, next, &gameport_event_list) { event = list_entry(node, struct gameport_event, node); if (event->object == gameport) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -