brintos

brintos / linux-shallow public Read only

0
0
Text · 20.4 KiB · 10cc958 Raw
886 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Generic gameport layer4 *5 * Copyright (c) 1999-2002 Vojtech Pavlik6 * Copyright (c) 2005 Dmitry Torokhov7 */8 9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt11 12#include <linux/stddef.h>13#include <linux/module.h>14#include <linux/io.h>15#include <linux/ioport.h>16#include <linux/init.h>17#include <linux/gameport.h>18#include <linux/slab.h>19#include <linux/delay.h>20#include <linux/workqueue.h>21#include <linux/sched.h>	/* HZ */22#include <linux/mutex.h>23#include <linux/timekeeping.h>24 25MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");26MODULE_DESCRIPTION("Generic gameport layer");27MODULE_LICENSE("GPL");28 29static bool use_ktime = true;30module_param(use_ktime, bool, 0400);31MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed");32 33/*34 * gameport_mutex protects entire gameport subsystem and is taken35 * every time gameport port or driver registrered or unregistered.36 */37static DEFINE_MUTEX(gameport_mutex);38 39static LIST_HEAD(gameport_list);40 41static const struct bus_type gameport_bus;42 43static void gameport_add_port(struct gameport *gameport);44static void gameport_attach_driver(struct gameport_driver *drv);45static void gameport_reconnect_port(struct gameport *gameport);46static void gameport_disconnect_port(struct gameport *gameport);47 48#if defined(__i386__)49 50#include <linux/i8253.h>51 52#define DELTA(x,y)      ((y)-(x)+((y)<(x)?1193182/HZ:0))53#define GET_TIME(x)     do { x = get_time_pit(); } while (0)54 55static unsigned int get_time_pit(void)56{57	unsigned long flags;58	unsigned int count;59 60	raw_spin_lock_irqsave(&i8253_lock, flags);61	outb_p(0x00, 0x43);62	count = inb_p(0x40);63	count |= inb_p(0x40) << 8;64	raw_spin_unlock_irqrestore(&i8253_lock, flags);65 66	return count;67}68 69#endif70 71 72 73/*74 * gameport_measure_speed() measures the gameport i/o speed.75 */76 77static int gameport_measure_speed(struct gameport *gameport)78{79	unsigned int i, t, tx;80	u64 t1, t2, t3;81	unsigned long flags;82 83	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))84		return 0;85 86	tx = ~0;87 88	for (i = 0; i < 50; i++) {89		local_irq_save(flags);90		t1 = ktime_get_ns();91		for (t = 0; t < 50; t++)92			gameport_read(gameport);93		t2 = ktime_get_ns();94		t3 = ktime_get_ns();95		local_irq_restore(flags);96		udelay(i * 10);97		t = (t2 - t1) - (t3 - t2);98		if (t < tx)99			tx = t;100	}101 102	gameport_close(gameport);103	t = 1000000 * 50;104	if (tx)105		t /= tx;106	return t;107}108 109static int old_gameport_measure_speed(struct gameport *gameport)110{111#if defined(__i386__)112 113	unsigned int i, t, t1, t2, t3, tx;114	unsigned long flags;115 116	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))117		return 0;118 119	tx = 1 << 30;120 121	for(i = 0; i < 50; i++) {122		local_irq_save(flags);123		GET_TIME(t1);124		for (t = 0; t < 50; t++) gameport_read(gameport);125		GET_TIME(t2);126		GET_TIME(t3);127		local_irq_restore(flags);128		udelay(i * 10);129		if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t;130	}131 132	gameport_close(gameport);133	return 59659 / (tx < 1 ? 1 : tx);134 135#elif defined (__x86_64__)136 137	unsigned int i, t;138	unsigned long tx, t1, t2, flags;139 140	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))141		return 0;142 143	tx = 1 << 30;144 145	for(i = 0; i < 50; i++) {146		local_irq_save(flags);147		t1 = rdtsc();148		for (t = 0; t < 50; t++) gameport_read(gameport);149		t2 = rdtsc();150		local_irq_restore(flags);151		udelay(i * 10);152		if (t2 - t1 < tx) tx = t2 - t1;153	}154 155	gameport_close(gameport);156	return (this_cpu_read(cpu_info.loops_per_jiffy) *157		(unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx);158 159#else160 161	unsigned int j, t = 0;162 163	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))164		return 0;165 166	j = jiffies; while (j == jiffies);167	j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); }168 169	gameport_close(gameport);170	return t * HZ / 1000;171 172#endif173}174 175void gameport_start_polling(struct gameport *gameport)176{177	spin_lock(&gameport->timer_lock);178 179	if (!gameport->poll_cnt++) {180		BUG_ON(!gameport->poll_handler);181		BUG_ON(!gameport->poll_interval);182		mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));183	}184 185	spin_unlock(&gameport->timer_lock);186}187EXPORT_SYMBOL(gameport_start_polling);188 189void gameport_stop_polling(struct gameport *gameport)190{191	spin_lock(&gameport->timer_lock);192 193	if (!--gameport->poll_cnt)194		del_timer(&gameport->poll_timer);195 196	spin_unlock(&gameport->timer_lock);197}198EXPORT_SYMBOL(gameport_stop_polling);199 200static void gameport_run_poll_handler(struct timer_list *t)201{202	struct gameport *gameport = from_timer(gameport, t, poll_timer);203 204	gameport->poll_handler(gameport);205	if (gameport->poll_cnt)206		mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));207}208 209/*210 * Basic gameport -> driver core mappings211 */212 213static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)214{215	int error;216 217	gameport->dev.driver = &drv->driver;218	if (drv->connect(gameport, drv)) {219		gameport->dev.driver = NULL;220		return -ENODEV;221	}222 223	error = device_bind_driver(&gameport->dev);224	if (error) {225		dev_warn(&gameport->dev,226			 "device_bind_driver() failed for %s (%s) and %s, error: %d\n",227			gameport->phys, gameport->name,228			drv->description, error);229		drv->disconnect(gameport);230		gameport->dev.driver = NULL;231		return error;232	}233 234	return 0;235}236 237static void gameport_find_driver(struct gameport *gameport)238{239	int error;240 241	error = device_attach(&gameport->dev);242	if (error < 0)243		dev_warn(&gameport->dev,244			 "device_attach() failed for %s (%s), error: %d\n",245			 gameport->phys, gameport->name, error);246}247 248 249/*250 * Gameport event processing.251 */252 253enum gameport_event_type {254	GAMEPORT_REGISTER_PORT,255	GAMEPORT_ATTACH_DRIVER,256};257 258struct gameport_event {259	enum gameport_event_type type;260	void *object;261	struct module *owner;262	struct list_head node;263};264 265static DEFINE_SPINLOCK(gameport_event_lock);	/* protects gameport_event_list */266static LIST_HEAD(gameport_event_list);267 268static struct gameport_event *gameport_get_event(void)269{270	struct gameport_event *event = NULL;271	unsigned long flags;272 273	spin_lock_irqsave(&gameport_event_lock, flags);274 275	if (!list_empty(&gameport_event_list)) {276		event = list_first_entry(&gameport_event_list,277					 struct gameport_event, node);278		list_del_init(&event->node);279	}280 281	spin_unlock_irqrestore(&gameport_event_lock, flags);282	return event;283}284 285static void gameport_free_event(struct gameport_event *event)286{287	module_put(event->owner);288	kfree(event);289}290 291static void gameport_remove_duplicate_events(struct gameport_event *event)292{293	struct gameport_event *e, *next;294	unsigned long flags;295 296	spin_lock_irqsave(&gameport_event_lock, flags);297 298	list_for_each_entry_safe(e, next, &gameport_event_list, node) {299		if (event->object == e->object) {300			/*301			 * If this event is of different type we should not302			 * look further - we only suppress duplicate events303			 * that were sent back-to-back.304			 */305			if (event->type != e->type)306				break;307 308			list_del_init(&e->node);309			gameport_free_event(e);310		}311	}312 313	spin_unlock_irqrestore(&gameport_event_lock, flags);314}315 316 317static void gameport_handle_events(struct work_struct *work)318{319	struct gameport_event *event;320 321	mutex_lock(&gameport_mutex);322 323	/*324	 * Note that we handle only one event here to give swsusp325	 * a chance to freeze kgameportd thread. Gameport events326	 * should be pretty rare so we are not concerned about327	 * taking performance hit.328	 */329	if ((event = gameport_get_event())) {330 331		switch (event->type) {332 333		case GAMEPORT_REGISTER_PORT:334			gameport_add_port(event->object);335			break;336 337		case GAMEPORT_ATTACH_DRIVER:338			gameport_attach_driver(event->object);339			break;340		}341 342		gameport_remove_duplicate_events(event);343		gameport_free_event(event);344	}345 346	mutex_unlock(&gameport_mutex);347}348 349static DECLARE_WORK(gameport_event_work, gameport_handle_events);350 351static int gameport_queue_event(void *object, struct module *owner,352				enum gameport_event_type event_type)353{354	unsigned long flags;355	struct gameport_event *event;356	int retval = 0;357 358	spin_lock_irqsave(&gameport_event_lock, flags);359 360	/*361	 * Scan event list for the other events for the same gameport port,362	 * starting with the most recent one. If event is the same we363	 * do not need add new one. If event is of different type we364	 * need to add this event and should not look further because365	 * we need to preserve sequence of distinct events.366	 */367	list_for_each_entry_reverse(event, &gameport_event_list, node) {368		if (event->object == object) {369			if (event->type == event_type)370				goto out;371			break;372		}373	}374 375	event = kmalloc(sizeof(*event), GFP_ATOMIC);376	if (!event) {377		pr_err("Not enough memory to queue event %d\n", event_type);378		retval = -ENOMEM;379		goto out;380	}381 382	if (!try_module_get(owner)) {383		pr_warn("Can't get module reference, dropping event %d\n",384			event_type);385		kfree(event);386		retval = -EINVAL;387		goto out;388	}389 390	event->type = event_type;391	event->object = object;392	event->owner = owner;393 394	list_add_tail(&event->node, &gameport_event_list);395	queue_work(system_long_wq, &gameport_event_work);396 397out:398	spin_unlock_irqrestore(&gameport_event_lock, flags);399	return retval;400}401 402/*403 * Remove all events that have been submitted for a given object,404 * be it a gameport port or a driver.405 */406static void gameport_remove_pending_events(void *object)407{408	struct gameport_event *event, *next;409	unsigned long flags;410 411	spin_lock_irqsave(&gameport_event_lock, flags);412 413	list_for_each_entry_safe(event, next, &gameport_event_list, node) {414		if (event->object == object) {415			list_del_init(&event->node);416			gameport_free_event(event);417		}418	}419 420	spin_unlock_irqrestore(&gameport_event_lock, flags);421}422 423/*424 * Destroy child gameport port (if any) that has not been fully registered yet.425 *426 * Note that we rely on the fact that port can have only one child and therefore427 * only one child registration request can be pending. Additionally, children428 * are registered by driver's connect() handler so there can't be a grandchild429 * pending registration together with a child.430 */431static struct gameport *gameport_get_pending_child(struct gameport *parent)432{433	struct gameport_event *event;434	struct gameport *gameport, *child = NULL;435	unsigned long flags;436 437	spin_lock_irqsave(&gameport_event_lock, flags);438 439	list_for_each_entry(event, &gameport_event_list, node) {440		if (event->type == GAMEPORT_REGISTER_PORT) {441			gameport = event->object;442			if (gameport->parent == parent) {443				child = gameport;444				break;445			}446		}447	}448 449	spin_unlock_irqrestore(&gameport_event_lock, flags);450	return child;451}452 453/*454 * Gameport port operations455 */456 457static ssize_t gameport_description_show(struct device *dev, struct device_attribute *attr, char *buf)458{459	struct gameport *gameport = to_gameport_port(dev);460 461	return sprintf(buf, "%s\n", gameport->name);462}463static DEVICE_ATTR(description, S_IRUGO, gameport_description_show, NULL);464 465static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)466{467	struct gameport *gameport = to_gameport_port(dev);468	struct device_driver *drv;469	int error;470 471	error = mutex_lock_interruptible(&gameport_mutex);472	if (error)473		return error;474 475	if (!strncmp(buf, "none", count)) {476		gameport_disconnect_port(gameport);477	} else if (!strncmp(buf, "reconnect", count)) {478		gameport_reconnect_port(gameport);479	} else if (!strncmp(buf, "rescan", count)) {480		gameport_disconnect_port(gameport);481		gameport_find_driver(gameport);482	} else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {483		gameport_disconnect_port(gameport);484		error = gameport_bind_driver(gameport, to_gameport_driver(drv));485	} else {486		error = -EINVAL;487	}488 489	mutex_unlock(&gameport_mutex);490 491	return error ? error : count;492}493static DEVICE_ATTR_WO(drvctl);494 495static struct attribute *gameport_device_attrs[] = {496	&dev_attr_description.attr,497	&dev_attr_drvctl.attr,498	NULL,499};500ATTRIBUTE_GROUPS(gameport_device);501 502static void gameport_release_port(struct device *dev)503{504	struct gameport *gameport = to_gameport_port(dev);505 506	kfree(gameport);507	module_put(THIS_MODULE);508}509 510void gameport_set_phys(struct gameport *gameport, const char *fmt, ...)511{512	va_list args;513 514	va_start(args, fmt);515	vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args);516	va_end(args);517}518EXPORT_SYMBOL(gameport_set_phys);519 520static void gameport_default_trigger(struct gameport *gameport)521{522#ifdef CONFIG_HAS_IOPORT523	outb(0xff, gameport->io);524#endif525}526 527static unsigned char gameport_default_read(struct gameport *gameport)528{529#ifdef CONFIG_HAS_IOPORT530	return inb(gameport->io);531#else532	return 0xff;533#endif534}535 536static void gameport_setup_default_handlers(struct gameport *gameport)537{538	if ((!gameport->trigger || !gameport->read) &&539	    !IS_ENABLED(CONFIG_HAS_IOPORT))540		dev_err(&gameport->dev,541			"I/O port access is required for %s (%s) but is not available\n",542			gameport->phys, gameport->name);543 544	if (!gameport->trigger)545		gameport->trigger = gameport_default_trigger;546	if (!gameport->read)547		gameport->read = gameport_default_read;548}549 550/*551 * Prepare gameport port for registration.552 */553static void gameport_init_port(struct gameport *gameport)554{555	static atomic_t gameport_no = ATOMIC_INIT(-1);556 557	__module_get(THIS_MODULE);558 559	mutex_init(&gameport->drv_mutex);560	device_initialize(&gameport->dev);561	dev_set_name(&gameport->dev, "gameport%lu",562			(unsigned long)atomic_inc_return(&gameport_no));563	gameport->dev.bus = &gameport_bus;564	gameport->dev.release = gameport_release_port;565	if (gameport->parent)566		gameport->dev.parent = &gameport->parent->dev;567 568	gameport_setup_default_handlers(gameport);569	INIT_LIST_HEAD(&gameport->node);570	spin_lock_init(&gameport->timer_lock);571	timer_setup(&gameport->poll_timer, gameport_run_poll_handler, 0);572}573 574/*575 * Complete gameport port registration.576 * Driver core will attempt to find appropriate driver for the port.577 */578static void gameport_add_port(struct gameport *gameport)579{580	int error;581 582	if (gameport->parent)583		gameport->parent->child = gameport;584 585	gameport->speed = use_ktime ?586		gameport_measure_speed(gameport) :587		old_gameport_measure_speed(gameport);588 589	list_add_tail(&gameport->node, &gameport_list);590 591	if (gameport->io)592		dev_info(&gameport->dev, "%s is %s, io %#x, speed %dkHz\n",593			 gameport->name, gameport->phys, gameport->io, gameport->speed);594	else595		dev_info(&gameport->dev, "%s is %s, speed %dkHz\n",596			gameport->name, gameport->phys, gameport->speed);597 598	error = device_add(&gameport->dev);599	if (error)600		dev_err(&gameport->dev,601			"device_add() failed for %s (%s), error: %d\n",602			gameport->phys, gameport->name, error);603}604 605/*606 * gameport_destroy_port() completes deregistration process and removes607 * port from the system608 */609static void gameport_destroy_port(struct gameport *gameport)610{611	struct gameport *child;612 613	child = gameport_get_pending_child(gameport);614	if (child) {615		gameport_remove_pending_events(child);616		put_device(&child->dev);617	}618 619	if (gameport->parent) {620		gameport->parent->child = NULL;621		gameport->parent = NULL;622	}623 624	if (device_is_registered(&gameport->dev))625		device_del(&gameport->dev);626 627	list_del_init(&gameport->node);628 629	gameport_remove_pending_events(gameport);630	put_device(&gameport->dev);631}632 633/*634 * Reconnect gameport port and all its children (re-initialize attached devices)635 */636static void gameport_reconnect_port(struct gameport *gameport)637{638	do {639		if (!gameport->drv || !gameport->drv->reconnect || gameport->drv->reconnect(gameport)) {640			gameport_disconnect_port(gameport);641			gameport_find_driver(gameport);642			/* Ok, old children are now gone, we are done */643			break;644		}645		gameport = gameport->child;646	} while (gameport);647}648 649/*650 * gameport_disconnect_port() unbinds a port from its driver. As a side effect651 * all child ports are unbound and destroyed.652 */653static void gameport_disconnect_port(struct gameport *gameport)654{655	struct gameport *s, *parent;656 657	if (gameport->child) {658		/*659		 * Children ports should be disconnected and destroyed660		 * first, staring with the leaf one, since we don't want661		 * to do recursion662		 */663		for (s = gameport; s->child; s = s->child)664			/* empty */;665 666		do {667			parent = s->parent;668 669			device_release_driver(&s->dev);670			gameport_destroy_port(s);671		} while ((s = parent) != gameport);672	}673 674	/*675	 * Ok, no children left, now disconnect this port676	 */677	device_release_driver(&gameport->dev);678}679 680/*681 * Submits register request to kgameportd for subsequent execution.682 * Note that port registration is always asynchronous.683 */684void __gameport_register_port(struct gameport *gameport, struct module *owner)685{686	gameport_init_port(gameport);687	gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT);688}689EXPORT_SYMBOL(__gameport_register_port);690 691/*692 * Synchronously unregisters gameport port.693 */694void gameport_unregister_port(struct gameport *gameport)695{696	mutex_lock(&gameport_mutex);697	gameport_disconnect_port(gameport);698	gameport_destroy_port(gameport);699	mutex_unlock(&gameport_mutex);700}701EXPORT_SYMBOL(gameport_unregister_port);702 703 704/*705 * Gameport driver operations706 */707 708static ssize_t description_show(struct device_driver *drv, char *buf)709{710	struct gameport_driver *driver = to_gameport_driver(drv);711	return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");712}713static DRIVER_ATTR_RO(description);714 715static struct attribute *gameport_driver_attrs[] = {716	&driver_attr_description.attr,717	NULL718};719ATTRIBUTE_GROUPS(gameport_driver);720 721static int gameport_driver_probe(struct device *dev)722{723	struct gameport *gameport = to_gameport_port(dev);724	struct gameport_driver *drv = to_gameport_driver(dev->driver);725 726	drv->connect(gameport, drv);727	return gameport->drv ? 0 : -ENODEV;728}729 730static void gameport_driver_remove(struct device *dev)731{732	struct gameport *gameport = to_gameport_port(dev);733	struct gameport_driver *drv = to_gameport_driver(dev->driver);734 735	drv->disconnect(gameport);736}737 738static void gameport_attach_driver(struct gameport_driver *drv)739{740	int error;741 742	error = driver_attach(&drv->driver);743	if (error)744		pr_err("driver_attach() failed for %s, error: %d\n",745			drv->driver.name, error);746}747 748int __gameport_register_driver(struct gameport_driver *drv, struct module *owner,749				const char *mod_name)750{751	int error;752 753	drv->driver.bus = &gameport_bus;754	drv->driver.owner = owner;755	drv->driver.mod_name = mod_name;756 757	/*758	 * Temporarily disable automatic binding because probing759	 * takes long time and we are better off doing it in kgameportd760	 */761	drv->ignore = true;762 763	error = driver_register(&drv->driver);764	if (error) {765		pr_err("driver_register() failed for %s, error: %d\n",766			drv->driver.name, error);767		return error;768	}769 770	/*771	 * Reset ignore flag and let kgameportd bind the driver to free ports772	 */773	drv->ignore = false;774	error = gameport_queue_event(drv, NULL, GAMEPORT_ATTACH_DRIVER);775	if (error) {776		driver_unregister(&drv->driver);777		return error;778	}779 780	return 0;781}782EXPORT_SYMBOL(__gameport_register_driver);783 784void gameport_unregister_driver(struct gameport_driver *drv)785{786	struct gameport *gameport;787 788	mutex_lock(&gameport_mutex);789 790	drv->ignore = true;	/* so gameport_find_driver ignores it */791	gameport_remove_pending_events(drv);792 793start_over:794	list_for_each_entry(gameport, &gameport_list, node) {795		if (gameport->drv == drv) {796			gameport_disconnect_port(gameport);797			gameport_find_driver(gameport);798			/* we could've deleted some ports, restart */799			goto start_over;800		}801	}802 803	driver_unregister(&drv->driver);804 805	mutex_unlock(&gameport_mutex);806}807EXPORT_SYMBOL(gameport_unregister_driver);808 809static int gameport_bus_match(struct device *dev, const struct device_driver *drv)810{811	const struct gameport_driver *gameport_drv = to_gameport_driver(drv);812 813	return !gameport_drv->ignore;814}815 816static const struct bus_type gameport_bus = {817	.name		= "gameport",818	.dev_groups	= gameport_device_groups,819	.drv_groups	= gameport_driver_groups,820	.match		= gameport_bus_match,821	.probe		= gameport_driver_probe,822	.remove		= gameport_driver_remove,823};824 825static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv)826{827	mutex_lock(&gameport->drv_mutex);828	gameport->drv = drv;829	mutex_unlock(&gameport->drv_mutex);830}831 832int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode)833{834	if (gameport->open) {835		if (gameport->open(gameport, mode)) {836			return -1;837		}838	} else {839		if (mode != GAMEPORT_MODE_RAW)840			return -1;841	}842 843	gameport_set_drv(gameport, drv);844	return 0;845}846EXPORT_SYMBOL(gameport_open);847 848void gameport_close(struct gameport *gameport)849{850	del_timer_sync(&gameport->poll_timer);851	gameport->poll_handler = NULL;852	gameport->poll_interval = 0;853	gameport_set_drv(gameport, NULL);854	if (gameport->close)855		gameport->close(gameport);856}857EXPORT_SYMBOL(gameport_close);858 859static int __init gameport_init(void)860{861	int error;862 863	error = bus_register(&gameport_bus);864	if (error) {865		pr_err("failed to register gameport bus, error: %d\n", error);866		return error;867	}868 869 870	return 0;871}872 873static void __exit gameport_exit(void)874{875	bus_unregister(&gameport_bus);876 877	/*878	 * There should not be any outstanding events but work may879	 * still be scheduled so simply cancel it.880	 */881	cancel_work_sync(&gameport_event_work);882}883 884subsys_initcall(gameport_init);885module_exit(gameport_exit);886