📄 serio.c
字号:
static void serio_destroy_port(struct serio *serio){ struct serio_driver *drv = serio->drv; unsigned long flags; serio_remove_pending_events(serio); list_del_init(&serio->node); if (drv) { drv->disconnect(serio); down_write(&serio_bus.subsys.rwsem); device_release_driver(&serio->dev); up_write(&serio_bus.subsys.rwsem); put_driver(&drv->driver); } if (serio->parent) { spin_lock_irqsave(&serio->parent->lock, flags); serio->parent->child = NULL; spin_unlock_irqrestore(&serio->parent->lock, flags); } device_unregister(&serio->dev);}/* * serio_connect_port() tries to bind the port and possible all its * children to appropriate drivers. If driver passed in the function will not * try otehr drivers when binding parent port. */static void serio_connect_port(struct serio *serio, struct serio_driver *drv){ WARN_ON(serio->drv); WARN_ON(serio->child); if (drv) serio_bind_driver(serio, drv); else if (!serio->manual_bind) serio_find_driver(serio); /* Ok, now bind children, if any */ while (serio->child) { serio = serio->child; WARN_ON(serio->drv); WARN_ON(serio->child); serio_create_port(serio); if (!serio->manual_bind) { /* * With children we just _prefer_ passed in driver, * but we will try other options in case preferred * is not the one */ if (!drv || !serio_bind_driver(serio, drv)) serio_find_driver(serio); } }}/* * */static void serio_reconnect_port(struct serio *serio){ do { if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) { serio_disconnect_port(serio); serio_connect_port(serio, NULL); /* Ok, old children are now gone, we are done */ break; } serio = serio->child; } while (serio);}/* * serio_disconnect_port() unbinds a port from its driver. As a side effect * all child ports are unbound and destroyed. */static void serio_disconnect_port(struct serio *serio){ struct serio_driver *drv = serio->drv; struct serio *s; if (serio->child) { /* * Children ports should be disconnected and destroyed * first, staring with the leaf one, since we don't want * to do recursion */ do { s = serio->child; } while (s->child); while (s != serio) { s = s->parent; serio_destroy_port(s->child); } } /* * Ok, no children left, now disconnect this port */ if (drv) { drv->disconnect(serio); down_write(&serio_bus.subsys.rwsem); device_release_driver(&serio->dev); up_write(&serio_bus.subsys.rwsem); put_driver(&drv->driver); }}void serio_rescan(struct serio *serio){ serio_queue_event(serio, SERIO_RESCAN);}void serio_reconnect(struct serio *serio){ serio_queue_event(serio, SERIO_RECONNECT);}void serio_register_port(struct serio *serio){ down(&serio_sem); serio_create_port(serio); serio_connect_port(serio, NULL); up(&serio_sem);}/* * Submits register request to kseriod for subsequent execution. * Can be used when it is not obvious whether the serio_sem is * taken or not and when delayed execution is feasible. */void serio_register_port_delayed(struct serio *serio){ serio_queue_event(serio, SERIO_REGISTER_PORT);}void serio_unregister_port(struct serio *serio){ down(&serio_sem); serio_disconnect_port(serio); serio_destroy_port(serio); up(&serio_sem);}/* * Submits unregister request to kseriod for subsequent execution. * Can be used when it is not obvious whether the serio_sem is * taken or not and when delayed execution is feasible. */void serio_unregister_port_delayed(struct serio *serio){ serio_queue_event(serio, SERIO_UNREGISTER_PORT);}/* * Serio driver operations */static ssize_t serio_driver_show_description(struct device_driver *drv, char *buf){ struct serio_driver *driver = to_serio_driver(drv); return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");}static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf){ struct serio_driver *serio_drv = to_serio_driver(drv); return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");}static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count){ struct serio_driver *serio_drv = to_serio_driver(drv); int retval; retval = count; if (!strncmp(buf, "manual", count)) { serio_drv->manual_bind = 1; } else if (!strncmp(buf, "auto", count)) { serio_drv->manual_bind = 0; } else { retval = -EINVAL; } return retval;}static struct driver_attribute serio_driver_attrs[] = { __ATTR(description, S_IRUGO, serio_driver_show_description, NULL), __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_driver_show_bind_mode, serio_driver_set_bind_mode), __ATTR_NULL};void serio_register_driver(struct serio_driver *drv){ struct serio *serio; down(&serio_sem); list_add_tail(&drv->node, &serio_driver_list); drv->driver.bus = &serio_bus; driver_register(&drv->driver); if (drv->manual_bind) goto out;start_over: list_for_each_entry(serio, &serio_list, node) { if (!serio->drv) { serio_connect_port(serio, drv); /* * if new child appeared then the list is changed, * we need to start over */ if (serio->child) goto start_over; } }out: up(&serio_sem);}void serio_unregister_driver(struct serio_driver *drv){ struct serio *serio; down(&serio_sem); list_del_init(&drv->node);start_over: list_for_each_entry(serio, &serio_list, node) { if (serio->drv == drv) { serio_disconnect_port(serio); serio_connect_port(serio, NULL); /* we could've deleted some ports, restart */ goto start_over; } } driver_unregister(&drv->driver); up(&serio_sem);}static void serio_set_drv(struct serio *serio, struct serio_driver *drv){ down(&serio->drv_sem); serio_pause_rx(serio); serio->drv = drv; serio_continue_rx(serio); up(&serio->drv_sem);}/* called from serio_driver->connect/disconnect methods under serio_sem */int serio_open(struct serio *serio, struct serio_driver *drv){ serio_set_drv(serio, drv); if (serio->open && serio->open(serio)) { serio_set_drv(serio, NULL); return -1; } return 0;}/* called from serio_driver->connect/disconnect methods under serio_sem */void serio_close(struct serio *serio){ if (serio->close) serio->close(serio); serio_set_drv(serio, NULL);}irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int dfl, struct pt_regs *regs){ unsigned long flags; irqreturn_t ret = IRQ_NONE; spin_lock_irqsave(&serio->lock, flags); if (likely(serio->drv)) { ret = serio->drv->interrupt(serio, data, dfl, regs); } else { if (!dfl) { if ((serio->type != SERIO_8042 && serio->type != SERIO_8042_XL) || (data == 0xaa)) { serio_rescan(serio); ret = IRQ_HANDLED; } } } spin_unlock_irqrestore(&serio->lock, flags); return ret;}static int __init serio_init(void){ if (!(serio_pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL))) { printk(KERN_WARNING "serio: Failed to start kseriod\n"); return -1; } serio_bus.dev_attrs = serio_device_attrs; serio_bus.drv_attrs = serio_driver_attrs; bus_register(&serio_bus); return 0;}static void __exit serio_exit(void){ bus_unregister(&serio_bus); kill_proc(serio_pid, SIGTERM, 1); wait_for_completion(&serio_exited);}module_init(serio_init);module_exit(serio_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -