📄 gameport.c
字号:
list_del_init(node); gameport_free_event(event); } } spin_unlock_irqrestore(&gameport_event_lock, flags);}/* * Destroy child gameport port (if any) that has not been fully registered yet. * * Note that we rely on the fact that port can have only one child and therefore * only one child registration request can be pending. Additionally, children * are registered by driver's connect() handler so there can't be a grandchild * pending registration together with a child. */static struct gameport *gameport_get_pending_child(struct gameport *parent){ struct gameport_event *event; struct gameport *gameport, *child = NULL; unsigned long flags; spin_lock_irqsave(&gameport_event_lock, flags); list_for_each_entry(event, &gameport_event_list, node) { if (event->type == GAMEPORT_REGISTER_PORT) { gameport = event->object; if (gameport->parent == parent) { child = gameport; break; } } } spin_unlock_irqrestore(&gameport_event_lock, flags); return child;}static int gameport_thread(void *nothing){ do { gameport_handle_event(); wait_event_interruptible(gameport_wait, kthread_should_stop() || !list_empty(&gameport_event_list)); try_to_freeze(); } while (!kthread_should_stop()); printk(KERN_DEBUG "gameport: kgameportd exiting\n"); return 0;}/* * Gameport port operations */static ssize_t gameport_show_description(struct device *dev, struct device_attribute *attr, char *buf){ struct gameport *gameport = to_gameport_port(dev); return sprintf(buf, "%s\n", gameport->name);}static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct gameport *gameport = to_gameport_port(dev); struct device_driver *drv; int retval; retval = down_interruptible(&gameport_sem); if (retval) return retval; retval = count; if (!strncmp(buf, "none", count)) { gameport_disconnect_port(gameport); } else if (!strncmp(buf, "reconnect", count)) { gameport_reconnect_port(gameport); } else if (!strncmp(buf, "rescan", count)) { gameport_disconnect_port(gameport); gameport_find_driver(gameport); } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) { gameport_disconnect_port(gameport); gameport_bind_driver(gameport, to_gameport_driver(drv)); put_driver(drv); } else { retval = -EINVAL; } up(&gameport_sem); return retval;}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 void gameport_release_port(struct device *dev){ struct gameport *gameport = to_gameport_port(dev); kfree(gameport); module_put(THIS_MODULE);}void gameport_set_phys(struct gameport *gameport, const char *fmt, ...){ va_list args; va_start(args, fmt); vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args); va_end(args);}/* * Prepare gameport port for registration. */static void gameport_init_port(struct gameport *gameport){ static atomic_t gameport_no = ATOMIC_INIT(0); __module_get(THIS_MODULE); init_MUTEX(&gameport->drv_sem); device_initialize(&gameport->dev); snprintf(gameport->dev.bus_id, sizeof(gameport->dev.bus_id), "gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1); gameport->dev.bus = &gameport_bus; gameport->dev.release = gameport_release_port; if (gameport->parent) gameport->dev.parent = &gameport->parent->dev; spin_lock_init(&gameport->timer_lock); init_timer(&gameport->poll_timer); gameport->poll_timer.function = gameport_run_poll_handler; gameport->poll_timer.data = (unsigned long)gameport;}/* * Complete gameport port registration. * Driver core will attempt to find appropriate driver for the port. */static void gameport_add_port(struct gameport *gameport){ if (gameport->parent) gameport->parent->child = gameport; gameport->speed = gameport_measure_speed(gameport); list_add_tail(&gameport->node, &gameport_list); if (gameport->io) printk(KERN_INFO "gameport: %s is %s, io %#x, speed %dkHz\n", gameport->name, gameport->phys, gameport->io, gameport->speed); else printk(KERN_INFO "gameport: %s is %s, speed %dkHz\n", gameport->name, gameport->phys, gameport->speed); device_add(&gameport->dev); gameport->registered = 1;}/* * gameport_destroy_port() completes deregistration process and removes * port from the system */static void gameport_destroy_port(struct gameport *gameport){ struct gameport *child; child = gameport_get_pending_child(gameport); if (child) { gameport_remove_pending_events(child); put_device(&child->dev); } if (gameport->parent) { gameport->parent->child = NULL; gameport->parent = NULL; } if (gameport->registered) { device_del(&gameport->dev); list_del_init(&gameport->node); gameport->registered = 0; } gameport_remove_pending_events(gameport); put_device(&gameport->dev);}/* * Reconnect gameport port and all its children (re-initialize attached devices) */static void gameport_reconnect_port(struct gameport *gameport){ do { if (!gameport->drv || !gameport->drv->reconnect || gameport->drv->reconnect(gameport)) { gameport_disconnect_port(gameport); gameport_find_driver(gameport); /* Ok, old children are now gone, we are done */ break; } gameport = gameport->child; } while (gameport);}/* * gameport_disconnect_port() unbinds a port from its driver. As a side effect * all child ports are unbound and destroyed. */static void gameport_disconnect_port(struct gameport *gameport){ struct gameport *s, *parent; if (gameport->child) { /* * Children ports should be disconnected and destroyed * first, staring with the leaf one, since we don't want * to do recursion */ for (s = gameport; s->child; s = s->child) /* empty */; do { parent = s->parent; gameport_release_driver(s); gameport_destroy_port(s); } while ((s = parent) != gameport); } /* * Ok, no children left, now disconnect this port */ gameport_release_driver(gameport);}void gameport_rescan(struct gameport *gameport){ gameport_queue_event(gameport, NULL, GAMEPORT_RESCAN);}void gameport_reconnect(struct gameport *gameport){ gameport_queue_event(gameport, NULL, GAMEPORT_RECONNECT);}/* * Submits register request to kgameportd for subsequent execution. * Note that port registration is always asynchronous. */void __gameport_register_port(struct gameport *gameport, struct module *owner){ gameport_init_port(gameport); gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT);}/* * Synchronously unregisters gameport port. */void gameport_unregister_port(struct gameport *gameport){ down(&gameport_sem); gameport_disconnect_port(gameport); gameport_destroy_port(gameport); up(&gameport_sem);}/* * Gameport driver operations */static ssize_t gameport_driver_show_description(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 struct driver_attribute gameport_driver_attrs[] = { __ATTR(description, S_IRUGO, gameport_driver_show_description, NULL), __ATTR_NULL};static int gameport_driver_probe(struct device *dev){ struct gameport *gameport = to_gameport_port(dev); struct gameport_driver *drv = to_gameport_driver(dev->driver); drv->connect(gameport, drv); return gameport->drv ? 0 : -ENODEV;}static int gameport_driver_remove(struct device *dev){ struct gameport *gameport = to_gameport_port(dev); struct gameport_driver *drv = to_gameport_driver(dev->driver); drv->disconnect(gameport); return 0;}void __gameport_register_driver(struct gameport_driver *drv, struct module *owner){ drv->driver.bus = &gameport_bus; drv->driver.probe = gameport_driver_probe; drv->driver.remove = gameport_driver_remove; gameport_queue_event(drv, owner, GAMEPORT_REGISTER_DRIVER);}void gameport_unregister_driver(struct gameport_driver *drv){ struct gameport *gameport; down(&gameport_sem); drv->ignore = 1; /* so gameport_find_driver ignores it */start_over: list_for_each_entry(gameport, &gameport_list, node) { if (gameport->drv == drv) { gameport_disconnect_port(gameport); gameport_find_driver(gameport); /* we could've deleted some ports, restart */ goto start_over; } } driver_unregister(&drv->driver); up(&gameport_sem);}static int gameport_bus_match(struct device *dev, struct device_driver *drv){ struct gameport_driver *gameport_drv = to_gameport_driver(drv); return !gameport_drv->ignore;}static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv){ down(&gameport->drv_sem); gameport->drv = drv; up(&gameport->drv_sem);}int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode){ if (gameport->open) { if (gameport->open(gameport, mode)) { return -1; } } else { if (mode != GAMEPORT_MODE_RAW) return -1; } gameport_set_drv(gameport, drv); return 0;}void gameport_close(struct gameport *gameport){ del_timer_sync(&gameport->poll_timer); gameport->poll_handler = NULL; gameport->poll_interval = 0; gameport_set_drv(gameport, NULL); if (gameport->close) gameport->close(gameport);}static int __init gameport_init(void){ gameport_task = kthread_run(gameport_thread, NULL, "kgameportd"); if (IS_ERR(gameport_task)) { printk(KERN_ERR "gameport: Failed to start kgameportd\n"); return PTR_ERR(gameport_task); } gameport_bus.dev_attrs = gameport_device_attrs; gameport_bus.drv_attrs = gameport_driver_attrs; gameport_bus.match = gameport_bus_match; bus_register(&gameport_bus); return 0;}static void __exit gameport_exit(void){ bus_unregister(&gameport_bus); kthread_stop(gameport_task);}module_init(gameport_init);module_exit(gameport_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -