📄 serio.c
字号:
/* * The Serio abstraction module * * Copyright (c) 1999-2004 Vojtech Pavlik * Copyright (c) 2004 Dmitry Torokhov * Copyright (c) 2003 Daniele Bellucci *//* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */#include <linux/stddef.h>#include <linux/module.h>#include <linux/serio.h>#include <linux/errno.h>#include <linux/wait.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/kthread.h>MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");MODULE_DESCRIPTION("Serio abstraction core");MODULE_LICENSE("GPL");EXPORT_SYMBOL(serio_interrupt);EXPORT_SYMBOL(__serio_register_port);EXPORT_SYMBOL(serio_unregister_port);EXPORT_SYMBOL(serio_unregister_child_port);EXPORT_SYMBOL(__serio_unregister_port_delayed);EXPORT_SYMBOL(__serio_register_driver);EXPORT_SYMBOL(serio_unregister_driver);EXPORT_SYMBOL(serio_open);EXPORT_SYMBOL(serio_close);EXPORT_SYMBOL(serio_rescan);EXPORT_SYMBOL(serio_reconnect);/* * serio_sem protects entire serio subsystem and is taken every time * serio port or driver registrered or unregistered. */static DECLARE_MUTEX(serio_sem);static LIST_HEAD(serio_list);static struct bus_type serio_bus = { .name = "serio",};static void serio_add_port(struct serio *serio);static void serio_destroy_port(struct serio *serio);static void serio_reconnect_port(struct serio *serio);static void serio_disconnect_port(struct serio *serio);static int serio_connect_driver(struct serio *serio, struct serio_driver *drv){ int retval; down(&serio->drv_sem); retval = drv->connect(serio, drv); up(&serio->drv_sem); return retval;}static int serio_reconnect_driver(struct serio *serio){ int retval = -1; down(&serio->drv_sem); if (serio->drv && serio->drv->reconnect) retval = serio->drv->reconnect(serio); up(&serio->drv_sem); return retval;}static void serio_disconnect_driver(struct serio *serio){ down(&serio->drv_sem); if (serio->drv) serio->drv->disconnect(serio); up(&serio->drv_sem);}static int serio_match_port(const struct serio_device_id *ids, struct serio *serio){ while (ids->type || ids->proto) { if ((ids->type == SERIO_ANY || ids->type == serio->id.type) && (ids->proto == SERIO_ANY || ids->proto == serio->id.proto) && (ids->extra == SERIO_ANY || ids->extra == serio->id.extra) && (ids->id == SERIO_ANY || ids->id == serio->id.id)) return 1; ids++; } return 0;}/* * Basic serio -> driver core mappings */static void serio_bind_driver(struct serio *serio, struct serio_driver *drv){ down_write(&serio_bus.subsys.rwsem); if (serio_match_port(drv->id_table, serio)) { serio->dev.driver = &drv->driver; if (serio_connect_driver(serio, drv)) { serio->dev.driver = NULL; goto out; } device_bind_driver(&serio->dev); }out: up_write(&serio_bus.subsys.rwsem);}static void serio_release_driver(struct serio *serio){ down_write(&serio_bus.subsys.rwsem); device_release_driver(&serio->dev); up_write(&serio_bus.subsys.rwsem);}static void serio_find_driver(struct serio *serio){ down_write(&serio_bus.subsys.rwsem); device_attach(&serio->dev); up_write(&serio_bus.subsys.rwsem);}/* * Serio event processing. */enum serio_event_type { SERIO_RESCAN, SERIO_RECONNECT, SERIO_REGISTER_PORT, SERIO_UNREGISTER_PORT, SERIO_REGISTER_DRIVER,};struct serio_event { enum serio_event_type type; void *object; struct module *owner; struct list_head node;};static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */static LIST_HEAD(serio_event_list);static DECLARE_WAIT_QUEUE_HEAD(serio_wait);static struct task_struct *serio_task;static void serio_queue_event(void *object, struct module *owner, enum serio_event_type event_type){ unsigned long flags; struct serio_event *event; spin_lock_irqsave(&serio_event_lock, flags); /* * Scan event list for the other events for the same serio 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, &serio_event_list, node) { if (event->object == object) { if (event->type == event_type) goto out; break; } } if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) { if (!try_module_get(owner)) { printk(KERN_WARNING "serio: 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, &serio_event_list); wake_up(&serio_wait); } else { printk(KERN_ERR "serio: Not enough memory to queue event %d\n", event_type); }out: spin_unlock_irqrestore(&serio_event_lock, flags);}static void serio_free_event(struct serio_event *event){ module_put(event->owner); kfree(event);}static void serio_remove_duplicate_events(struct serio_event *event){ struct list_head *node, *next; struct serio_event *e; unsigned long flags; spin_lock_irqsave(&serio_event_lock, flags); list_for_each_safe(node, next, &serio_event_list) { e = list_entry(node, struct serio_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); serio_free_event(e); } } spin_unlock_irqrestore(&serio_event_lock, flags);}static struct serio_event *serio_get_event(void){ struct serio_event *event; struct list_head *node; unsigned long flags; spin_lock_irqsave(&serio_event_lock, flags); if (list_empty(&serio_event_list)) { spin_unlock_irqrestore(&serio_event_lock, flags); return NULL; } node = serio_event_list.next; event = list_entry(node, struct serio_event, node); list_del_init(node); spin_unlock_irqrestore(&serio_event_lock, flags); return event;}static void serio_handle_events(void){ struct serio_event *event; struct serio_driver *serio_drv; down(&serio_sem); while ((event = serio_get_event())) { switch (event->type) { case SERIO_REGISTER_PORT: serio_add_port(event->object); break; case SERIO_UNREGISTER_PORT: serio_disconnect_port(event->object); serio_destroy_port(event->object); break; case SERIO_RECONNECT: serio_reconnect_port(event->object); break; case SERIO_RESCAN: serio_disconnect_port(event->object); serio_find_driver(event->object); break; case SERIO_REGISTER_DRIVER: serio_drv = event->object; driver_register(&serio_drv->driver); break; default: break; } serio_remove_duplicate_events(event); serio_free_event(event); } up(&serio_sem);}/* * Remove all events that have been submitted for a given serio port. */static void serio_remove_pending_events(struct serio *serio){ struct list_head *node, *next; struct serio_event *event; unsigned long flags; spin_lock_irqsave(&serio_event_lock, flags); list_for_each_safe(node, next, &serio_event_list) { event = list_entry(node, struct serio_event, node); if (event->object == serio) { list_del_init(node); serio_free_event(event); } } spin_unlock_irqrestore(&serio_event_lock, flags);}/* * Destroy child serio 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 serio *serio_get_pending_child(struct serio *parent){ struct serio_event *event; struct serio *serio, *child = NULL; unsigned long flags; spin_lock_irqsave(&serio_event_lock, flags); list_for_each_entry(event, &serio_event_list, node) { if (event->type == SERIO_REGISTER_PORT) { serio = event->object; if (serio->parent == parent) { child = serio; break; } } } spin_unlock_irqrestore(&serio_event_lock, flags); return child;}static int serio_thread(void *nothing){ do { serio_handle_events(); wait_event_interruptible(serio_wait, kthread_should_stop() || !list_empty(&serio_event_list)); try_to_freeze(); } while (!kthread_should_stop()); printk(KERN_DEBUG "serio: kseriod exiting\n"); return 0;}/* * Serio port operations */static ssize_t serio_show_description(struct device *dev, struct device_attribute *attr, char *buf){ struct serio *serio = to_serio_port(dev); return sprintf(buf, "%s\n", serio->name);}static ssize_t serio_show_modalias(struct device *dev, struct device_attribute *attr, char *buf){ struct serio *serio = to_serio_port(dev); return sprintf(buf, "serio:ty%02Xpr%02Xid%02Xex%02X\n", serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);}static ssize_t serio_show_id_type(struct device *dev, struct device_attribute *attr, char *buf){ struct serio *serio = to_serio_port(dev); return sprintf(buf, "%02x\n", serio->id.type);}static ssize_t serio_show_id_proto(struct device *dev, struct device_attribute *attr, char *buf){ struct serio *serio = to_serio_port(dev); return sprintf(buf, "%02x\n", serio->id.proto);}static ssize_t serio_show_id_id(struct device *dev, struct device_attribute *attr, char *buf){ struct serio *serio = to_serio_port(dev); return sprintf(buf, "%02x\n", serio->id.id);}static ssize_t serio_show_id_extra(struct device *dev, struct device_attribute *attr, char *buf){ struct serio *serio = to_serio_port(dev); return sprintf(buf, "%02x\n", serio->id.extra);}static DEVICE_ATTR(type, S_IRUGO, serio_show_id_type, NULL);static DEVICE_ATTR(proto, S_IRUGO, serio_show_id_proto, NULL);static DEVICE_ATTR(id, S_IRUGO, serio_show_id_id, NULL);static DEVICE_ATTR(extra, S_IRUGO, serio_show_id_extra, NULL);static struct attribute *serio_device_id_attrs[] = { &dev_attr_type.attr, &dev_attr_proto.attr, &dev_attr_id.attr, &dev_attr_extra.attr, NULL};static struct attribute_group serio_id_attr_group = { .name = "id", .attrs = serio_device_id_attrs,};static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct serio *serio = to_serio_port(dev); struct device_driver *drv; int retval; retval = down_interruptible(&serio_sem); if (retval) return retval; retval = count; if (!strncmp(buf, "none", count)) { serio_disconnect_port(serio); } else if (!strncmp(buf, "reconnect", count)) { serio_reconnect_port(serio); } else if (!strncmp(buf, "rescan", count)) { serio_disconnect_port(serio); serio_find_driver(serio); } else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -