162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  The Serio abstraction module
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 1999-2004 Vojtech Pavlik
662306a36Sopenharmony_ci *  Copyright (c) 2004 Dmitry Torokhov
762306a36Sopenharmony_ci *  Copyright (c) 2003 Daniele Bellucci
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/stddef.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/serio.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/sched.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/workqueue.h>
1962306a36Sopenharmony_ci#include <linux/mutex.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
2262306a36Sopenharmony_ciMODULE_DESCRIPTION("Serio abstraction core");
2362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * serio_mutex protects entire serio subsystem and is taken every time
2762306a36Sopenharmony_ci * serio port or driver registered or unregistered.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic DEFINE_MUTEX(serio_mutex);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic LIST_HEAD(serio_list);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic void serio_add_port(struct serio *serio);
3462306a36Sopenharmony_cistatic int serio_reconnect_port(struct serio *serio);
3562306a36Sopenharmony_cistatic void serio_disconnect_port(struct serio *serio);
3662306a36Sopenharmony_cistatic void serio_reconnect_subtree(struct serio *serio);
3762306a36Sopenharmony_cistatic void serio_attach_driver(struct serio_driver *drv);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	int retval;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	mutex_lock(&serio->drv_mutex);
4462306a36Sopenharmony_ci	retval = drv->connect(serio, drv);
4562306a36Sopenharmony_ci	mutex_unlock(&serio->drv_mutex);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return retval;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int serio_reconnect_driver(struct serio *serio)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	int retval = -1;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	mutex_lock(&serio->drv_mutex);
5562306a36Sopenharmony_ci	if (serio->drv && serio->drv->reconnect)
5662306a36Sopenharmony_ci		retval = serio->drv->reconnect(serio);
5762306a36Sopenharmony_ci	mutex_unlock(&serio->drv_mutex);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return retval;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void serio_disconnect_driver(struct serio *serio)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	mutex_lock(&serio->drv_mutex);
6562306a36Sopenharmony_ci	if (serio->drv)
6662306a36Sopenharmony_ci		serio->drv->disconnect(serio);
6762306a36Sopenharmony_ci	mutex_unlock(&serio->drv_mutex);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int serio_match_port(const struct serio_device_id *ids, struct serio *serio)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	while (ids->type || ids->proto) {
7362306a36Sopenharmony_ci		if ((ids->type == SERIO_ANY || ids->type == serio->id.type) &&
7462306a36Sopenharmony_ci		    (ids->proto == SERIO_ANY || ids->proto == serio->id.proto) &&
7562306a36Sopenharmony_ci		    (ids->extra == SERIO_ANY || ids->extra == serio->id.extra) &&
7662306a36Sopenharmony_ci		    (ids->id == SERIO_ANY || ids->id == serio->id.id))
7762306a36Sopenharmony_ci			return 1;
7862306a36Sopenharmony_ci		ids++;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * Basic serio -> driver core mappings
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	int error;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (serio_match_port(drv->id_table, serio)) {
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		serio->dev.driver = &drv->driver;
9462306a36Sopenharmony_ci		if (serio_connect_driver(serio, drv)) {
9562306a36Sopenharmony_ci			serio->dev.driver = NULL;
9662306a36Sopenharmony_ci			return -ENODEV;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		error = device_bind_driver(&serio->dev);
10062306a36Sopenharmony_ci		if (error) {
10162306a36Sopenharmony_ci			dev_warn(&serio->dev,
10262306a36Sopenharmony_ci				 "device_bind_driver() failed for %s (%s) and %s, error: %d\n",
10362306a36Sopenharmony_ci				 serio->phys, serio->name,
10462306a36Sopenharmony_ci				 drv->description, error);
10562306a36Sopenharmony_ci			serio_disconnect_driver(serio);
10662306a36Sopenharmony_ci			serio->dev.driver = NULL;
10762306a36Sopenharmony_ci			return error;
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void serio_find_driver(struct serio *serio)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int error;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	error = device_attach(&serio->dev);
11862306a36Sopenharmony_ci	if (error < 0 && error != -EPROBE_DEFER)
11962306a36Sopenharmony_ci		dev_warn(&serio->dev,
12062306a36Sopenharmony_ci			 "device_attach() failed for %s (%s), error: %d\n",
12162306a36Sopenharmony_ci			 serio->phys, serio->name, error);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/*
12662306a36Sopenharmony_ci * Serio event processing.
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cienum serio_event_type {
13062306a36Sopenharmony_ci	SERIO_RESCAN_PORT,
13162306a36Sopenharmony_ci	SERIO_RECONNECT_PORT,
13262306a36Sopenharmony_ci	SERIO_RECONNECT_SUBTREE,
13362306a36Sopenharmony_ci	SERIO_REGISTER_PORT,
13462306a36Sopenharmony_ci	SERIO_ATTACH_DRIVER,
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistruct serio_event {
13862306a36Sopenharmony_ci	enum serio_event_type type;
13962306a36Sopenharmony_ci	void *object;
14062306a36Sopenharmony_ci	struct module *owner;
14162306a36Sopenharmony_ci	struct list_head node;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(serio_event_lock);	/* protects serio_event_list */
14562306a36Sopenharmony_cistatic LIST_HEAD(serio_event_list);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic struct serio_event *serio_get_event(void)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct serio_event *event = NULL;
15062306a36Sopenharmony_ci	unsigned long flags;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	spin_lock_irqsave(&serio_event_lock, flags);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (!list_empty(&serio_event_list)) {
15562306a36Sopenharmony_ci		event = list_first_entry(&serio_event_list,
15662306a36Sopenharmony_ci					 struct serio_event, node);
15762306a36Sopenharmony_ci		list_del_init(&event->node);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	spin_unlock_irqrestore(&serio_event_lock, flags);
16162306a36Sopenharmony_ci	return event;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void serio_free_event(struct serio_event *event)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	module_put(event->owner);
16762306a36Sopenharmony_ci	kfree(event);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void serio_remove_duplicate_events(void *object,
17162306a36Sopenharmony_ci					  enum serio_event_type type)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct serio_event *e, *next;
17462306a36Sopenharmony_ci	unsigned long flags;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	spin_lock_irqsave(&serio_event_lock, flags);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	list_for_each_entry_safe(e, next, &serio_event_list, node) {
17962306a36Sopenharmony_ci		if (object == e->object) {
18062306a36Sopenharmony_ci			/*
18162306a36Sopenharmony_ci			 * If this event is of different type we should not
18262306a36Sopenharmony_ci			 * look further - we only suppress duplicate events
18362306a36Sopenharmony_ci			 * that were sent back-to-back.
18462306a36Sopenharmony_ci			 */
18562306a36Sopenharmony_ci			if (type != e->type)
18662306a36Sopenharmony_ci				break;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci			list_del_init(&e->node);
18962306a36Sopenharmony_ci			serio_free_event(e);
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_unlock_irqrestore(&serio_event_lock, flags);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void serio_handle_event(struct work_struct *work)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct serio_event *event;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	mutex_lock(&serio_mutex);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	while ((event = serio_get_event())) {
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		switch (event->type) {
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		case SERIO_REGISTER_PORT:
20762306a36Sopenharmony_ci			serio_add_port(event->object);
20862306a36Sopenharmony_ci			break;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		case SERIO_RECONNECT_PORT:
21162306a36Sopenharmony_ci			serio_reconnect_port(event->object);
21262306a36Sopenharmony_ci			break;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		case SERIO_RESCAN_PORT:
21562306a36Sopenharmony_ci			serio_disconnect_port(event->object);
21662306a36Sopenharmony_ci			serio_find_driver(event->object);
21762306a36Sopenharmony_ci			break;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		case SERIO_RECONNECT_SUBTREE:
22062306a36Sopenharmony_ci			serio_reconnect_subtree(event->object);
22162306a36Sopenharmony_ci			break;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		case SERIO_ATTACH_DRIVER:
22462306a36Sopenharmony_ci			serio_attach_driver(event->object);
22562306a36Sopenharmony_ci			break;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		serio_remove_duplicate_events(event->object, event->type);
22962306a36Sopenharmony_ci		serio_free_event(event);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	mutex_unlock(&serio_mutex);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic DECLARE_WORK(serio_event_work, serio_handle_event);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int serio_queue_event(void *object, struct module *owner,
23862306a36Sopenharmony_ci			     enum serio_event_type event_type)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	unsigned long flags;
24162306a36Sopenharmony_ci	struct serio_event *event;
24262306a36Sopenharmony_ci	int retval = 0;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	spin_lock_irqsave(&serio_event_lock, flags);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/*
24762306a36Sopenharmony_ci	 * Scan event list for the other events for the same serio port,
24862306a36Sopenharmony_ci	 * starting with the most recent one. If event is the same we
24962306a36Sopenharmony_ci	 * do not need add new one. If event is of different type we
25062306a36Sopenharmony_ci	 * need to add this event and should not look further because
25162306a36Sopenharmony_ci	 * we need to preseve sequence of distinct events.
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	list_for_each_entry_reverse(event, &serio_event_list, node) {
25462306a36Sopenharmony_ci		if (event->object == object) {
25562306a36Sopenharmony_ci			if (event->type == event_type)
25662306a36Sopenharmony_ci				goto out;
25762306a36Sopenharmony_ci			break;
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
26262306a36Sopenharmony_ci	if (!event) {
26362306a36Sopenharmony_ci		pr_err("Not enough memory to queue event %d\n", event_type);
26462306a36Sopenharmony_ci		retval = -ENOMEM;
26562306a36Sopenharmony_ci		goto out;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (!try_module_get(owner)) {
26962306a36Sopenharmony_ci		pr_warn("Can't get module reference, dropping event %d\n",
27062306a36Sopenharmony_ci			event_type);
27162306a36Sopenharmony_ci		kfree(event);
27262306a36Sopenharmony_ci		retval = -EINVAL;
27362306a36Sopenharmony_ci		goto out;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	event->type = event_type;
27762306a36Sopenharmony_ci	event->object = object;
27862306a36Sopenharmony_ci	event->owner = owner;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	list_add_tail(&event->node, &serio_event_list);
28162306a36Sopenharmony_ci	queue_work(system_long_wq, &serio_event_work);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciout:
28462306a36Sopenharmony_ci	spin_unlock_irqrestore(&serio_event_lock, flags);
28562306a36Sopenharmony_ci	return retval;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/*
28962306a36Sopenharmony_ci * Remove all events that have been submitted for a given
29062306a36Sopenharmony_ci * object, be it serio port or driver.
29162306a36Sopenharmony_ci */
29262306a36Sopenharmony_cistatic void serio_remove_pending_events(void *object)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct serio_event *event, *next;
29562306a36Sopenharmony_ci	unsigned long flags;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	spin_lock_irqsave(&serio_event_lock, flags);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	list_for_each_entry_safe(event, next, &serio_event_list, node) {
30062306a36Sopenharmony_ci		if (event->object == object) {
30162306a36Sopenharmony_ci			list_del_init(&event->node);
30262306a36Sopenharmony_ci			serio_free_event(event);
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	spin_unlock_irqrestore(&serio_event_lock, flags);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci/*
31062306a36Sopenharmony_ci * Locate child serio port (if any) that has not been fully registered yet.
31162306a36Sopenharmony_ci *
31262306a36Sopenharmony_ci * Children are registered by driver's connect() handler so there can't be a
31362306a36Sopenharmony_ci * grandchild pending registration together with a child.
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_cistatic struct serio *serio_get_pending_child(struct serio *parent)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct serio_event *event;
31862306a36Sopenharmony_ci	struct serio *serio, *child = NULL;
31962306a36Sopenharmony_ci	unsigned long flags;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	spin_lock_irqsave(&serio_event_lock, flags);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	list_for_each_entry(event, &serio_event_list, node) {
32462306a36Sopenharmony_ci		if (event->type == SERIO_REGISTER_PORT) {
32562306a36Sopenharmony_ci			serio = event->object;
32662306a36Sopenharmony_ci			if (serio->parent == parent) {
32762306a36Sopenharmony_ci				child = serio;
32862306a36Sopenharmony_ci				break;
32962306a36Sopenharmony_ci			}
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	spin_unlock_irqrestore(&serio_event_lock, flags);
33462306a36Sopenharmony_ci	return child;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/*
33862306a36Sopenharmony_ci * Serio port operations
33962306a36Sopenharmony_ci */
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic ssize_t serio_show_description(struct device *dev, struct device_attribute *attr, char *buf)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
34462306a36Sopenharmony_ci	return sprintf(buf, "%s\n", serio->name);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return sprintf(buf, "serio:ty%02Xpr%02Xid%02Xex%02X\n",
35262306a36Sopenharmony_ci			serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
35862306a36Sopenharmony_ci	return sprintf(buf, "%02x\n", serio->id.type);
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic ssize_t proto_show(struct device *dev, struct device_attribute *attr, char *buf)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
36462306a36Sopenharmony_ci	return sprintf(buf, "%02x\n", serio->id.proto);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
37062306a36Sopenharmony_ci	return sprintf(buf, "%02x\n", serio->id.id);
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic ssize_t extra_show(struct device *dev, struct device_attribute *attr, char *buf)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
37662306a36Sopenharmony_ci	return sprintf(buf, "%02x\n", serio->id.extra);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
38262306a36Sopenharmony_ci	struct device_driver *drv;
38362306a36Sopenharmony_ci	int error;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	error = mutex_lock_interruptible(&serio_mutex);
38662306a36Sopenharmony_ci	if (error)
38762306a36Sopenharmony_ci		return error;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (!strncmp(buf, "none", count)) {
39062306a36Sopenharmony_ci		serio_disconnect_port(serio);
39162306a36Sopenharmony_ci	} else if (!strncmp(buf, "reconnect", count)) {
39262306a36Sopenharmony_ci		serio_reconnect_subtree(serio);
39362306a36Sopenharmony_ci	} else if (!strncmp(buf, "rescan", count)) {
39462306a36Sopenharmony_ci		serio_disconnect_port(serio);
39562306a36Sopenharmony_ci		serio_find_driver(serio);
39662306a36Sopenharmony_ci		serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
39762306a36Sopenharmony_ci	} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
39862306a36Sopenharmony_ci		serio_disconnect_port(serio);
39962306a36Sopenharmony_ci		error = serio_bind_driver(serio, to_serio_driver(drv));
40062306a36Sopenharmony_ci		serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
40162306a36Sopenharmony_ci	} else {
40262306a36Sopenharmony_ci		error = -EINVAL;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	mutex_unlock(&serio_mutex);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return error ? error : count;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
41362306a36Sopenharmony_ci	return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
41962306a36Sopenharmony_ci	int retval;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	retval = count;
42262306a36Sopenharmony_ci	if (!strncmp(buf, "manual", count)) {
42362306a36Sopenharmony_ci		serio->manual_bind = true;
42462306a36Sopenharmony_ci	} else if (!strncmp(buf, "auto", count)) {
42562306a36Sopenharmony_ci		serio->manual_bind = false;
42662306a36Sopenharmony_ci	} else {
42762306a36Sopenharmony_ci		retval = -EINVAL;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return retval;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic ssize_t firmware_id_show(struct device *dev, struct device_attribute *attr, char *buf)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return sprintf(buf, "%s\n", serio->firmware_id);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(type);
44162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(proto);
44262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(id);
44362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(extra);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic struct attribute *serio_device_id_attrs[] = {
44662306a36Sopenharmony_ci	&dev_attr_type.attr,
44762306a36Sopenharmony_ci	&dev_attr_proto.attr,
44862306a36Sopenharmony_ci	&dev_attr_id.attr,
44962306a36Sopenharmony_ci	&dev_attr_extra.attr,
45062306a36Sopenharmony_ci	NULL
45162306a36Sopenharmony_ci};
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic const struct attribute_group serio_id_attr_group = {
45462306a36Sopenharmony_ci	.name	= "id",
45562306a36Sopenharmony_ci	.attrs	= serio_device_id_attrs,
45662306a36Sopenharmony_ci};
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
45962306a36Sopenharmony_cistatic DEVICE_ATTR_WO(drvctl);
46062306a36Sopenharmony_cistatic DEVICE_ATTR(description, S_IRUGO, serio_show_description, NULL);
46162306a36Sopenharmony_cistatic DEVICE_ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode);
46262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_id);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic struct attribute *serio_device_attrs[] = {
46562306a36Sopenharmony_ci	&dev_attr_modalias.attr,
46662306a36Sopenharmony_ci	&dev_attr_description.attr,
46762306a36Sopenharmony_ci	&dev_attr_drvctl.attr,
46862306a36Sopenharmony_ci	&dev_attr_bind_mode.attr,
46962306a36Sopenharmony_ci	&dev_attr_firmware_id.attr,
47062306a36Sopenharmony_ci	NULL
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic const struct attribute_group serio_device_attr_group = {
47462306a36Sopenharmony_ci	.attrs	= serio_device_attrs,
47562306a36Sopenharmony_ci};
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic const struct attribute_group *serio_device_attr_groups[] = {
47862306a36Sopenharmony_ci	&serio_id_attr_group,
47962306a36Sopenharmony_ci	&serio_device_attr_group,
48062306a36Sopenharmony_ci	NULL
48162306a36Sopenharmony_ci};
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void serio_release_port(struct device *dev)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	kfree(serio);
48862306a36Sopenharmony_ci	module_put(THIS_MODULE);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci/*
49262306a36Sopenharmony_ci * Prepare serio port for registration.
49362306a36Sopenharmony_ci */
49462306a36Sopenharmony_cistatic void serio_init_port(struct serio *serio)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	static atomic_t serio_no = ATOMIC_INIT(-1);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	__module_get(THIS_MODULE);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	INIT_LIST_HEAD(&serio->node);
50162306a36Sopenharmony_ci	INIT_LIST_HEAD(&serio->child_node);
50262306a36Sopenharmony_ci	INIT_LIST_HEAD(&serio->children);
50362306a36Sopenharmony_ci	spin_lock_init(&serio->lock);
50462306a36Sopenharmony_ci	mutex_init(&serio->drv_mutex);
50562306a36Sopenharmony_ci	device_initialize(&serio->dev);
50662306a36Sopenharmony_ci	dev_set_name(&serio->dev, "serio%lu",
50762306a36Sopenharmony_ci		     (unsigned long)atomic_inc_return(&serio_no));
50862306a36Sopenharmony_ci	serio->dev.bus = &serio_bus;
50962306a36Sopenharmony_ci	serio->dev.release = serio_release_port;
51062306a36Sopenharmony_ci	serio->dev.groups = serio_device_attr_groups;
51162306a36Sopenharmony_ci	if (serio->parent) {
51262306a36Sopenharmony_ci		serio->dev.parent = &serio->parent->dev;
51362306a36Sopenharmony_ci		serio->depth = serio->parent->depth + 1;
51462306a36Sopenharmony_ci	} else
51562306a36Sopenharmony_ci		serio->depth = 0;
51662306a36Sopenharmony_ci	lockdep_set_subclass(&serio->lock, serio->depth);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/*
52062306a36Sopenharmony_ci * Complete serio port registration.
52162306a36Sopenharmony_ci * Driver core will attempt to find appropriate driver for the port.
52262306a36Sopenharmony_ci */
52362306a36Sopenharmony_cistatic void serio_add_port(struct serio *serio)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct serio *parent = serio->parent;
52662306a36Sopenharmony_ci	int error;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (parent) {
52962306a36Sopenharmony_ci		serio_pause_rx(parent);
53062306a36Sopenharmony_ci		list_add_tail(&serio->child_node, &parent->children);
53162306a36Sopenharmony_ci		serio_continue_rx(parent);
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	list_add_tail(&serio->node, &serio_list);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (serio->start)
53762306a36Sopenharmony_ci		serio->start(serio);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	error = device_add(&serio->dev);
54062306a36Sopenharmony_ci	if (error)
54162306a36Sopenharmony_ci		dev_err(&serio->dev,
54262306a36Sopenharmony_ci			"device_add() failed for %s (%s), error: %d\n",
54362306a36Sopenharmony_ci			serio->phys, serio->name, error);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci/*
54762306a36Sopenharmony_ci * serio_destroy_port() completes unregistration process and removes
54862306a36Sopenharmony_ci * port from the system
54962306a36Sopenharmony_ci */
55062306a36Sopenharmony_cistatic void serio_destroy_port(struct serio *serio)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct serio *child;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	while ((child = serio_get_pending_child(serio)) != NULL) {
55562306a36Sopenharmony_ci		serio_remove_pending_events(child);
55662306a36Sopenharmony_ci		put_device(&child->dev);
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (serio->stop)
56062306a36Sopenharmony_ci		serio->stop(serio);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (serio->parent) {
56362306a36Sopenharmony_ci		serio_pause_rx(serio->parent);
56462306a36Sopenharmony_ci		list_del_init(&serio->child_node);
56562306a36Sopenharmony_ci		serio_continue_rx(serio->parent);
56662306a36Sopenharmony_ci		serio->parent = NULL;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (device_is_registered(&serio->dev))
57062306a36Sopenharmony_ci		device_del(&serio->dev);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	list_del_init(&serio->node);
57362306a36Sopenharmony_ci	serio_remove_pending_events(serio);
57462306a36Sopenharmony_ci	put_device(&serio->dev);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/*
57862306a36Sopenharmony_ci * Reconnect serio port (re-initialize attached device).
57962306a36Sopenharmony_ci * If reconnect fails (old device is no longer attached or
58062306a36Sopenharmony_ci * there was no device to begin with) we do full rescan in
58162306a36Sopenharmony_ci * hope of finding a driver for the port.
58262306a36Sopenharmony_ci */
58362306a36Sopenharmony_cistatic int serio_reconnect_port(struct serio *serio)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	int error = serio_reconnect_driver(serio);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (error) {
58862306a36Sopenharmony_ci		serio_disconnect_port(serio);
58962306a36Sopenharmony_ci		serio_find_driver(serio);
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	return error;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci/*
59662306a36Sopenharmony_ci * Reconnect serio port and all its children (re-initialize attached
59762306a36Sopenharmony_ci * devices).
59862306a36Sopenharmony_ci */
59962306a36Sopenharmony_cistatic void serio_reconnect_subtree(struct serio *root)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct serio *s = root;
60262306a36Sopenharmony_ci	int error;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	do {
60562306a36Sopenharmony_ci		error = serio_reconnect_port(s);
60662306a36Sopenharmony_ci		if (!error) {
60762306a36Sopenharmony_ci			/*
60862306a36Sopenharmony_ci			 * Reconnect was successful, move on to do the
60962306a36Sopenharmony_ci			 * first child.
61062306a36Sopenharmony_ci			 */
61162306a36Sopenharmony_ci			if (!list_empty(&s->children)) {
61262306a36Sopenharmony_ci				s = list_first_entry(&s->children,
61362306a36Sopenharmony_ci						     struct serio, child_node);
61462306a36Sopenharmony_ci				continue;
61562306a36Sopenharmony_ci			}
61662306a36Sopenharmony_ci		}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		/*
61962306a36Sopenharmony_ci		 * Either it was a leaf node or reconnect failed and it
62062306a36Sopenharmony_ci		 * became a leaf node. Continue reconnecting starting with
62162306a36Sopenharmony_ci		 * the next sibling of the parent node.
62262306a36Sopenharmony_ci		 */
62362306a36Sopenharmony_ci		while (s != root) {
62462306a36Sopenharmony_ci			struct serio *parent = s->parent;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci			if (!list_is_last(&s->child_node, &parent->children)) {
62762306a36Sopenharmony_ci				s = list_entry(s->child_node.next,
62862306a36Sopenharmony_ci					       struct serio, child_node);
62962306a36Sopenharmony_ci				break;
63062306a36Sopenharmony_ci			}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci			s = parent;
63362306a36Sopenharmony_ci		}
63462306a36Sopenharmony_ci	} while (s != root);
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci/*
63862306a36Sopenharmony_ci * serio_disconnect_port() unbinds a port from its driver. As a side effect
63962306a36Sopenharmony_ci * all children ports are unbound and destroyed.
64062306a36Sopenharmony_ci */
64162306a36Sopenharmony_cistatic void serio_disconnect_port(struct serio *serio)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct serio *s = serio;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/*
64662306a36Sopenharmony_ci	 * Children ports should be disconnected and destroyed
64762306a36Sopenharmony_ci	 * first; we travel the tree in depth-first order.
64862306a36Sopenharmony_ci	 */
64962306a36Sopenharmony_ci	while (!list_empty(&serio->children)) {
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		/* Locate a leaf */
65262306a36Sopenharmony_ci		while (!list_empty(&s->children))
65362306a36Sopenharmony_ci			s = list_first_entry(&s->children,
65462306a36Sopenharmony_ci					     struct serio, child_node);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		/*
65762306a36Sopenharmony_ci		 * Prune this leaf node unless it is the one we
65862306a36Sopenharmony_ci		 * started with.
65962306a36Sopenharmony_ci		 */
66062306a36Sopenharmony_ci		if (s != serio) {
66162306a36Sopenharmony_ci			struct serio *parent = s->parent;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci			device_release_driver(&s->dev);
66462306a36Sopenharmony_ci			serio_destroy_port(s);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci			s = parent;
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/*
67162306a36Sopenharmony_ci	 * OK, no children left, now disconnect this port.
67262306a36Sopenharmony_ci	 */
67362306a36Sopenharmony_ci	device_release_driver(&serio->dev);
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_civoid serio_rescan(struct serio *serio)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	serio_queue_event(serio, NULL, SERIO_RESCAN_PORT);
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ciEXPORT_SYMBOL(serio_rescan);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_civoid serio_reconnect(struct serio *serio)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ciEXPORT_SYMBOL(serio_reconnect);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci/*
68962306a36Sopenharmony_ci * Submits register request to kseriod for subsequent execution.
69062306a36Sopenharmony_ci * Note that port registration is always asynchronous.
69162306a36Sopenharmony_ci */
69262306a36Sopenharmony_civoid __serio_register_port(struct serio *serio, struct module *owner)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	serio_init_port(serio);
69562306a36Sopenharmony_ci	serio_queue_event(serio, owner, SERIO_REGISTER_PORT);
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ciEXPORT_SYMBOL(__serio_register_port);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/*
70062306a36Sopenharmony_ci * Synchronously unregisters serio port.
70162306a36Sopenharmony_ci */
70262306a36Sopenharmony_civoid serio_unregister_port(struct serio *serio)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	mutex_lock(&serio_mutex);
70562306a36Sopenharmony_ci	serio_disconnect_port(serio);
70662306a36Sopenharmony_ci	serio_destroy_port(serio);
70762306a36Sopenharmony_ci	mutex_unlock(&serio_mutex);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ciEXPORT_SYMBOL(serio_unregister_port);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci/*
71262306a36Sopenharmony_ci * Safely unregisters children ports if they are present.
71362306a36Sopenharmony_ci */
71462306a36Sopenharmony_civoid serio_unregister_child_port(struct serio *serio)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct serio *s, *next;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	mutex_lock(&serio_mutex);
71962306a36Sopenharmony_ci	list_for_each_entry_safe(s, next, &serio->children, child_node) {
72062306a36Sopenharmony_ci		serio_disconnect_port(s);
72162306a36Sopenharmony_ci		serio_destroy_port(s);
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci	mutex_unlock(&serio_mutex);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ciEXPORT_SYMBOL(serio_unregister_child_port);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci/*
72962306a36Sopenharmony_ci * Serio driver operations
73062306a36Sopenharmony_ci */
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic ssize_t description_show(struct device_driver *drv, char *buf)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct serio_driver *driver = to_serio_driver(drv);
73562306a36Sopenharmony_ci	return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_cistatic DRIVER_ATTR_RO(description);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic ssize_t bind_mode_show(struct device_driver *drv, char *buf)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct serio_driver *serio_drv = to_serio_driver(drv);
74262306a36Sopenharmony_ci	return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic ssize_t bind_mode_store(struct device_driver *drv, const char *buf, size_t count)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct serio_driver *serio_drv = to_serio_driver(drv);
74862306a36Sopenharmony_ci	int retval;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	retval = count;
75162306a36Sopenharmony_ci	if (!strncmp(buf, "manual", count)) {
75262306a36Sopenharmony_ci		serio_drv->manual_bind = true;
75362306a36Sopenharmony_ci	} else if (!strncmp(buf, "auto", count)) {
75462306a36Sopenharmony_ci		serio_drv->manual_bind = false;
75562306a36Sopenharmony_ci	} else {
75662306a36Sopenharmony_ci		retval = -EINVAL;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return retval;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_cistatic DRIVER_ATTR_RW(bind_mode);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic struct attribute *serio_driver_attrs[] = {
76462306a36Sopenharmony_ci	&driver_attr_description.attr,
76562306a36Sopenharmony_ci	&driver_attr_bind_mode.attr,
76662306a36Sopenharmony_ci	NULL,
76762306a36Sopenharmony_ci};
76862306a36Sopenharmony_ciATTRIBUTE_GROUPS(serio_driver);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int serio_driver_probe(struct device *dev)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
77362306a36Sopenharmony_ci	struct serio_driver *drv = to_serio_driver(dev->driver);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return serio_connect_driver(serio, drv);
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic void serio_driver_remove(struct device *dev)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	serio_disconnect_driver(serio);
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void serio_cleanup(struct serio *serio)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	mutex_lock(&serio->drv_mutex);
78862306a36Sopenharmony_ci	if (serio->drv && serio->drv->cleanup)
78962306a36Sopenharmony_ci		serio->drv->cleanup(serio);
79062306a36Sopenharmony_ci	mutex_unlock(&serio->drv_mutex);
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic void serio_shutdown(struct device *dev)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	serio_cleanup(serio);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic void serio_attach_driver(struct serio_driver *drv)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	int error;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	error = driver_attach(&drv->driver);
80562306a36Sopenharmony_ci	if (error)
80662306a36Sopenharmony_ci		pr_warn("driver_attach() failed for %s with error %d\n",
80762306a36Sopenharmony_ci			drv->driver.name, error);
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ciint __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	bool manual_bind = drv->manual_bind;
81362306a36Sopenharmony_ci	int error;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	drv->driver.bus = &serio_bus;
81662306a36Sopenharmony_ci	drv->driver.owner = owner;
81762306a36Sopenharmony_ci	drv->driver.mod_name = mod_name;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	/*
82062306a36Sopenharmony_ci	 * Temporarily disable automatic binding because probing
82162306a36Sopenharmony_ci	 * takes long time and we are better off doing it in kseriod
82262306a36Sopenharmony_ci	 */
82362306a36Sopenharmony_ci	drv->manual_bind = true;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	error = driver_register(&drv->driver);
82662306a36Sopenharmony_ci	if (error) {
82762306a36Sopenharmony_ci		pr_err("driver_register() failed for %s, error: %d\n",
82862306a36Sopenharmony_ci			drv->driver.name, error);
82962306a36Sopenharmony_ci		return error;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/*
83362306a36Sopenharmony_ci	 * Restore original bind mode and let kseriod bind the
83462306a36Sopenharmony_ci	 * driver to free ports
83562306a36Sopenharmony_ci	 */
83662306a36Sopenharmony_ci	if (!manual_bind) {
83762306a36Sopenharmony_ci		drv->manual_bind = false;
83862306a36Sopenharmony_ci		error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
83962306a36Sopenharmony_ci		if (error) {
84062306a36Sopenharmony_ci			driver_unregister(&drv->driver);
84162306a36Sopenharmony_ci			return error;
84262306a36Sopenharmony_ci		}
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	return 0;
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ciEXPORT_SYMBOL(__serio_register_driver);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_civoid serio_unregister_driver(struct serio_driver *drv)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct serio *serio;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	mutex_lock(&serio_mutex);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	drv->manual_bind = true;	/* so serio_find_driver ignores it */
85662306a36Sopenharmony_ci	serio_remove_pending_events(drv);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistart_over:
85962306a36Sopenharmony_ci	list_for_each_entry(serio, &serio_list, node) {
86062306a36Sopenharmony_ci		if (serio->drv == drv) {
86162306a36Sopenharmony_ci			serio_disconnect_port(serio);
86262306a36Sopenharmony_ci			serio_find_driver(serio);
86362306a36Sopenharmony_ci			/* we could've deleted some ports, restart */
86462306a36Sopenharmony_ci			goto start_over;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	driver_unregister(&drv->driver);
86962306a36Sopenharmony_ci	mutex_unlock(&serio_mutex);
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ciEXPORT_SYMBOL(serio_unregister_driver);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic void serio_set_drv(struct serio *serio, struct serio_driver *drv)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	serio_pause_rx(serio);
87662306a36Sopenharmony_ci	serio->drv = drv;
87762306a36Sopenharmony_ci	serio_continue_rx(serio);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int serio_bus_match(struct device *dev, struct device_driver *drv)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
88362306a36Sopenharmony_ci	struct serio_driver *serio_drv = to_serio_driver(drv);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (serio->manual_bind || serio_drv->manual_bind)
88662306a36Sopenharmony_ci		return 0;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return serio_match_port(serio_drv->id_table, serio);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci#define SERIO_ADD_UEVENT_VAR(fmt, val...)				\
89262306a36Sopenharmony_ci	do {								\
89362306a36Sopenharmony_ci		int err = add_uevent_var(env, fmt, val);		\
89462306a36Sopenharmony_ci		if (err)						\
89562306a36Sopenharmony_ci			return err;					\
89662306a36Sopenharmony_ci	} while (0)
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int serio_uevent(const struct device *dev, struct kobj_uevent_env *env)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	const struct serio *serio;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (!dev)
90362306a36Sopenharmony_ci		return -ENODEV;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	serio = to_serio_port(dev);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	SERIO_ADD_UEVENT_VAR("SERIO_TYPE=%02x", serio->id.type);
90862306a36Sopenharmony_ci	SERIO_ADD_UEVENT_VAR("SERIO_PROTO=%02x", serio->id.proto);
90962306a36Sopenharmony_ci	SERIO_ADD_UEVENT_VAR("SERIO_ID=%02x", serio->id.id);
91062306a36Sopenharmony_ci	SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",
91362306a36Sopenharmony_ci				serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (serio->firmware_id[0])
91662306a36Sopenharmony_ci		SERIO_ADD_UEVENT_VAR("SERIO_FIRMWARE_ID=%s",
91762306a36Sopenharmony_ci				     serio->firmware_id);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	return 0;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci#undef SERIO_ADD_UEVENT_VAR
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci#ifdef CONFIG_PM
92462306a36Sopenharmony_cistatic int serio_suspend(struct device *dev)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	serio_cleanup(serio);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return 0;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_cistatic int serio_resume(struct device *dev)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	struct serio *serio = to_serio_port(dev);
93662306a36Sopenharmony_ci	int error = -ENOENT;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	mutex_lock(&serio->drv_mutex);
93962306a36Sopenharmony_ci	if (serio->drv && serio->drv->fast_reconnect) {
94062306a36Sopenharmony_ci		error = serio->drv->fast_reconnect(serio);
94162306a36Sopenharmony_ci		if (error && error != -ENOENT)
94262306a36Sopenharmony_ci			dev_warn(dev, "fast reconnect failed with error %d\n",
94362306a36Sopenharmony_ci				 error);
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci	mutex_unlock(&serio->drv_mutex);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (error) {
94862306a36Sopenharmony_ci		/*
94962306a36Sopenharmony_ci		 * Driver reconnect can take a while, so better let
95062306a36Sopenharmony_ci		 * kseriod deal with it.
95162306a36Sopenharmony_ci		 */
95262306a36Sopenharmony_ci		serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	return 0;
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic const struct dev_pm_ops serio_pm_ops = {
95962306a36Sopenharmony_ci	.suspend	= serio_suspend,
96062306a36Sopenharmony_ci	.resume		= serio_resume,
96162306a36Sopenharmony_ci	.poweroff	= serio_suspend,
96262306a36Sopenharmony_ci	.restore	= serio_resume,
96362306a36Sopenharmony_ci};
96462306a36Sopenharmony_ci#endif /* CONFIG_PM */
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/* called from serio_driver->connect/disconnect methods under serio_mutex */
96762306a36Sopenharmony_ciint serio_open(struct serio *serio, struct serio_driver *drv)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	serio_set_drv(serio, drv);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	if (serio->open && serio->open(serio)) {
97262306a36Sopenharmony_ci		serio_set_drv(serio, NULL);
97362306a36Sopenharmony_ci		return -1;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci	return 0;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ciEXPORT_SYMBOL(serio_open);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci/* called from serio_driver->connect/disconnect methods under serio_mutex */
98062306a36Sopenharmony_civoid serio_close(struct serio *serio)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	if (serio->close)
98362306a36Sopenharmony_ci		serio->close(serio);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	serio_set_drv(serio, NULL);
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ciEXPORT_SYMBOL(serio_close);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ciirqreturn_t serio_interrupt(struct serio *serio,
99062306a36Sopenharmony_ci		unsigned char data, unsigned int dfl)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	unsigned long flags;
99362306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	spin_lock_irqsave(&serio->lock, flags);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci        if (likely(serio->drv)) {
99862306a36Sopenharmony_ci                ret = serio->drv->interrupt(serio, data, dfl);
99962306a36Sopenharmony_ci	} else if (!dfl && device_is_registered(&serio->dev)) {
100062306a36Sopenharmony_ci		serio_rescan(serio);
100162306a36Sopenharmony_ci		ret = IRQ_HANDLED;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	spin_unlock_irqrestore(&serio->lock, flags);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	return ret;
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ciEXPORT_SYMBOL(serio_interrupt);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistruct bus_type serio_bus = {
101162306a36Sopenharmony_ci	.name		= "serio",
101262306a36Sopenharmony_ci	.drv_groups	= serio_driver_groups,
101362306a36Sopenharmony_ci	.match		= serio_bus_match,
101462306a36Sopenharmony_ci	.uevent		= serio_uevent,
101562306a36Sopenharmony_ci	.probe		= serio_driver_probe,
101662306a36Sopenharmony_ci	.remove		= serio_driver_remove,
101762306a36Sopenharmony_ci	.shutdown	= serio_shutdown,
101862306a36Sopenharmony_ci#ifdef CONFIG_PM
101962306a36Sopenharmony_ci	.pm		= &serio_pm_ops,
102062306a36Sopenharmony_ci#endif
102162306a36Sopenharmony_ci};
102262306a36Sopenharmony_ciEXPORT_SYMBOL(serio_bus);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistatic int __init serio_init(void)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	int error;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	error = bus_register(&serio_bus);
102962306a36Sopenharmony_ci	if (error) {
103062306a36Sopenharmony_ci		pr_err("Failed to register serio bus, error: %d\n", error);
103162306a36Sopenharmony_ci		return error;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	return 0;
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic void __exit serio_exit(void)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	bus_unregister(&serio_bus);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/*
104262306a36Sopenharmony_ci	 * There should not be any outstanding events but work may
104362306a36Sopenharmony_ci	 * still be scheduled so simply cancel it.
104462306a36Sopenharmony_ci	 */
104562306a36Sopenharmony_ci	cancel_work_sync(&serio_event_work);
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cisubsys_initcall(serio_init);
104962306a36Sopenharmony_cimodule_exit(serio_exit);
1050