⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serio.c

📁 QQ2440板子
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  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 + -