xref: /openbmc/linux/drivers/input/serio/serio.c (revision 2a81ada3)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  The Serio abstraction module
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (c) 1999-2004 Vojtech Pavlik
61da177e4SLinus Torvalds  *  Copyright (c) 2004 Dmitry Torokhov
71da177e4SLinus Torvalds  *  Copyright (c) 2003 Daniele Bellucci
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
10cac9169bSDmitry Torokhov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11cac9169bSDmitry Torokhov 
121da177e4SLinus Torvalds #include <linux/stddef.h>
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/serio.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
161da177e4SLinus Torvalds #include <linux/sched.h>
171da177e4SLinus Torvalds #include <linux/slab.h>
188ee294cdSDmitry Torokhov #include <linux/workqueue.h>
19c4e32e9fSArjan van de Ven #include <linux/mutex.h>
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
221da177e4SLinus Torvalds MODULE_DESCRIPTION("Serio abstraction core");
231da177e4SLinus Torvalds MODULE_LICENSE("GPL");
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds /*
26c4e32e9fSArjan van de Ven  * serio_mutex protects entire serio subsystem and is taken every time
278ee294cdSDmitry Torokhov  * serio port or driver registered or unregistered.
281da177e4SLinus Torvalds  */
29c4e32e9fSArjan van de Ven static DEFINE_MUTEX(serio_mutex);
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds static LIST_HEAD(serio_list);
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds static void serio_add_port(struct serio *serio);
346aabcdffSShaohua Li static int serio_reconnect_port(struct serio *serio);
351da177e4SLinus Torvalds static void serio_disconnect_port(struct serio *serio);
3609822582SDmitry Eremin-Solenikov static void serio_reconnect_subtree(struct serio *serio);
37ed7b1f6dSDmitry Torokhov static void serio_attach_driver(struct serio_driver *drv);
381da177e4SLinus Torvalds 
serio_connect_driver(struct serio * serio,struct serio_driver * drv)393c803e8eSLinus Torvalds static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
403c803e8eSLinus Torvalds {
413c803e8eSLinus Torvalds 	int retval;
423c803e8eSLinus Torvalds 
43c4e32e9fSArjan van de Ven 	mutex_lock(&serio->drv_mutex);
443c803e8eSLinus Torvalds 	retval = drv->connect(serio, drv);
45c4e32e9fSArjan van de Ven 	mutex_unlock(&serio->drv_mutex);
463c803e8eSLinus Torvalds 
473c803e8eSLinus Torvalds 	return retval;
483c803e8eSLinus Torvalds }
493c803e8eSLinus Torvalds 
serio_reconnect_driver(struct serio * serio)503c803e8eSLinus Torvalds static int serio_reconnect_driver(struct serio *serio)
513c803e8eSLinus Torvalds {
523c803e8eSLinus Torvalds 	int retval = -1;
533c803e8eSLinus Torvalds 
54c4e32e9fSArjan van de Ven 	mutex_lock(&serio->drv_mutex);
553c803e8eSLinus Torvalds 	if (serio->drv && serio->drv->reconnect)
563c803e8eSLinus Torvalds 		retval = serio->drv->reconnect(serio);
57c4e32e9fSArjan van de Ven 	mutex_unlock(&serio->drv_mutex);
583c803e8eSLinus Torvalds 
593c803e8eSLinus Torvalds 	return retval;
603c803e8eSLinus Torvalds }
613c803e8eSLinus Torvalds 
serio_disconnect_driver(struct serio * serio)623c803e8eSLinus Torvalds static void serio_disconnect_driver(struct serio *serio)
633c803e8eSLinus Torvalds {
64c4e32e9fSArjan van de Ven 	mutex_lock(&serio->drv_mutex);
653c803e8eSLinus Torvalds 	if (serio->drv)
663c803e8eSLinus Torvalds 		serio->drv->disconnect(serio);
67c4e32e9fSArjan van de Ven 	mutex_unlock(&serio->drv_mutex);
683c803e8eSLinus Torvalds }
693c803e8eSLinus Torvalds 
serio_match_port(const struct serio_device_id * ids,struct serio * serio)701da177e4SLinus Torvalds static int serio_match_port(const struct serio_device_id *ids, struct serio *serio)
711da177e4SLinus Torvalds {
721da177e4SLinus Torvalds 	while (ids->type || ids->proto) {
731da177e4SLinus Torvalds 		if ((ids->type == SERIO_ANY || ids->type == serio->id.type) &&
741da177e4SLinus Torvalds 		    (ids->proto == SERIO_ANY || ids->proto == serio->id.proto) &&
751da177e4SLinus Torvalds 		    (ids->extra == SERIO_ANY || ids->extra == serio->id.extra) &&
761da177e4SLinus Torvalds 		    (ids->id == SERIO_ANY || ids->id == serio->id.id))
771da177e4SLinus Torvalds 			return 1;
781da177e4SLinus Torvalds 		ids++;
791da177e4SLinus Torvalds 	}
801da177e4SLinus Torvalds 	return 0;
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds /*
841da177e4SLinus Torvalds  * Basic serio -> driver core mappings
851da177e4SLinus Torvalds  */
861da177e4SLinus Torvalds 
serio_bind_driver(struct serio * serio,struct serio_driver * drv)8770f256fdSDmitry Torokhov static int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
881da177e4SLinus Torvalds {
890a66045bSDmitry Torokhov 	int error;
900a66045bSDmitry Torokhov 
911da177e4SLinus Torvalds 	if (serio_match_port(drv->id_table, serio)) {
9270f256fdSDmitry Torokhov 
931da177e4SLinus Torvalds 		serio->dev.driver = &drv->driver;
943c803e8eSLinus Torvalds 		if (serio_connect_driver(serio, drv)) {
951da177e4SLinus Torvalds 			serio->dev.driver = NULL;
9670f256fdSDmitry Torokhov 			return -ENODEV;
971da177e4SLinus Torvalds 		}
9870f256fdSDmitry Torokhov 
990a66045bSDmitry Torokhov 		error = device_bind_driver(&serio->dev);
1000a66045bSDmitry Torokhov 		if (error) {
101cac9169bSDmitry Torokhov 			dev_warn(&serio->dev,
102cac9169bSDmitry Torokhov 				 "device_bind_driver() failed for %s (%s) and %s, error: %d\n",
1030a66045bSDmitry Torokhov 				 serio->phys, serio->name,
1040a66045bSDmitry Torokhov 				 drv->description, error);
1050a66045bSDmitry Torokhov 			serio_disconnect_driver(serio);
1060a66045bSDmitry Torokhov 			serio->dev.driver = NULL;
10770f256fdSDmitry Torokhov 			return error;
1080a66045bSDmitry Torokhov 		}
1091da177e4SLinus Torvalds 	}
11070f256fdSDmitry Torokhov 	return 0;
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
serio_find_driver(struct serio * serio)1131da177e4SLinus Torvalds static void serio_find_driver(struct serio *serio)
1141da177e4SLinus Torvalds {
11573b59a3bSRandy Dunlap 	int error;
11673b59a3bSRandy Dunlap 
11773b59a3bSRandy Dunlap 	error = device_attach(&serio->dev);
118015bb5e1SGrygorii Strashko 	if (error < 0 && error != -EPROBE_DEFER)
119cac9169bSDmitry Torokhov 		dev_warn(&serio->dev,
120cac9169bSDmitry Torokhov 			 "device_attach() failed for %s (%s), error: %d\n",
12173b59a3bSRandy Dunlap 			 serio->phys, serio->name, error);
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds /*
1261da177e4SLinus Torvalds  * Serio event processing.
1271da177e4SLinus Torvalds  */
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds enum serio_event_type {
130ed7b1f6dSDmitry Torokhov 	SERIO_RESCAN_PORT,
131ed7b1f6dSDmitry Torokhov 	SERIO_RECONNECT_PORT,
13209822582SDmitry Eremin-Solenikov 	SERIO_RECONNECT_SUBTREE,
1331da177e4SLinus Torvalds 	SERIO_REGISTER_PORT,
134ed7b1f6dSDmitry Torokhov 	SERIO_ATTACH_DRIVER,
1351da177e4SLinus Torvalds };
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds struct serio_event {
1381da177e4SLinus Torvalds 	enum serio_event_type type;
1391da177e4SLinus Torvalds 	void *object;
1401da177e4SLinus Torvalds 	struct module *owner;
1411da177e4SLinus Torvalds 	struct list_head node;
1421da177e4SLinus Torvalds };
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds static DEFINE_SPINLOCK(serio_event_lock);	/* protects serio_event_list */
1451da177e4SLinus Torvalds static LIST_HEAD(serio_event_list);
1468ee294cdSDmitry Torokhov 
serio_get_event(void)1478ee294cdSDmitry Torokhov static struct serio_event *serio_get_event(void)
1488ee294cdSDmitry Torokhov {
1498ee294cdSDmitry Torokhov 	struct serio_event *event = NULL;
1508ee294cdSDmitry Torokhov 	unsigned long flags;
1518ee294cdSDmitry Torokhov 
1528ee294cdSDmitry Torokhov 	spin_lock_irqsave(&serio_event_lock, flags);
1538ee294cdSDmitry Torokhov 
1548ee294cdSDmitry Torokhov 	if (!list_empty(&serio_event_list)) {
1558ee294cdSDmitry Torokhov 		event = list_first_entry(&serio_event_list,
1568ee294cdSDmitry Torokhov 					 struct serio_event, node);
1578ee294cdSDmitry Torokhov 		list_del_init(&event->node);
1588ee294cdSDmitry Torokhov 	}
1598ee294cdSDmitry Torokhov 
1608ee294cdSDmitry Torokhov 	spin_unlock_irqrestore(&serio_event_lock, flags);
1618ee294cdSDmitry Torokhov 	return event;
1628ee294cdSDmitry Torokhov }
1638ee294cdSDmitry Torokhov 
serio_free_event(struct serio_event * event)1648ee294cdSDmitry Torokhov static void serio_free_event(struct serio_event *event)
1658ee294cdSDmitry Torokhov {
1668ee294cdSDmitry Torokhov 	module_put(event->owner);
1678ee294cdSDmitry Torokhov 	kfree(event);
1688ee294cdSDmitry Torokhov }
1698ee294cdSDmitry Torokhov 
serio_remove_duplicate_events(void * object,enum serio_event_type type)17019e95541SDuncan Laurie static void serio_remove_duplicate_events(void *object,
17119e95541SDuncan Laurie 					  enum serio_event_type type)
1728ee294cdSDmitry Torokhov {
1738ee294cdSDmitry Torokhov 	struct serio_event *e, *next;
1748ee294cdSDmitry Torokhov 	unsigned long flags;
1758ee294cdSDmitry Torokhov 
1768ee294cdSDmitry Torokhov 	spin_lock_irqsave(&serio_event_lock, flags);
1778ee294cdSDmitry Torokhov 
1788ee294cdSDmitry Torokhov 	list_for_each_entry_safe(e, next, &serio_event_list, node) {
17919e95541SDuncan Laurie 		if (object == e->object) {
1808ee294cdSDmitry Torokhov 			/*
1818ee294cdSDmitry Torokhov 			 * If this event is of different type we should not
1828ee294cdSDmitry Torokhov 			 * look further - we only suppress duplicate events
1838ee294cdSDmitry Torokhov 			 * that were sent back-to-back.
1848ee294cdSDmitry Torokhov 			 */
18519e95541SDuncan Laurie 			if (type != e->type)
1868ee294cdSDmitry Torokhov 				break;
1878ee294cdSDmitry Torokhov 
1888ee294cdSDmitry Torokhov 			list_del_init(&e->node);
1898ee294cdSDmitry Torokhov 			serio_free_event(e);
1908ee294cdSDmitry Torokhov 		}
1918ee294cdSDmitry Torokhov 	}
1928ee294cdSDmitry Torokhov 
1938ee294cdSDmitry Torokhov 	spin_unlock_irqrestore(&serio_event_lock, flags);
1948ee294cdSDmitry Torokhov }
1958ee294cdSDmitry Torokhov 
serio_handle_event(struct work_struct * work)1968ee294cdSDmitry Torokhov static void serio_handle_event(struct work_struct *work)
1978ee294cdSDmitry Torokhov {
1988ee294cdSDmitry Torokhov 	struct serio_event *event;
1998ee294cdSDmitry Torokhov 
2008ee294cdSDmitry Torokhov 	mutex_lock(&serio_mutex);
2018ee294cdSDmitry Torokhov 
2028ee294cdSDmitry Torokhov 	while ((event = serio_get_event())) {
2038ee294cdSDmitry Torokhov 
2048ee294cdSDmitry Torokhov 		switch (event->type) {
2058ee294cdSDmitry Torokhov 
2068ee294cdSDmitry Torokhov 		case SERIO_REGISTER_PORT:
2078ee294cdSDmitry Torokhov 			serio_add_port(event->object);
2088ee294cdSDmitry Torokhov 			break;
2098ee294cdSDmitry Torokhov 
2108ee294cdSDmitry Torokhov 		case SERIO_RECONNECT_PORT:
2118ee294cdSDmitry Torokhov 			serio_reconnect_port(event->object);
2128ee294cdSDmitry Torokhov 			break;
2138ee294cdSDmitry Torokhov 
2148ee294cdSDmitry Torokhov 		case SERIO_RESCAN_PORT:
2158ee294cdSDmitry Torokhov 			serio_disconnect_port(event->object);
2168ee294cdSDmitry Torokhov 			serio_find_driver(event->object);
2178ee294cdSDmitry Torokhov 			break;
2188ee294cdSDmitry Torokhov 
2198ee294cdSDmitry Torokhov 		case SERIO_RECONNECT_SUBTREE:
2208ee294cdSDmitry Torokhov 			serio_reconnect_subtree(event->object);
2218ee294cdSDmitry Torokhov 			break;
2228ee294cdSDmitry Torokhov 
2238ee294cdSDmitry Torokhov 		case SERIO_ATTACH_DRIVER:
2248ee294cdSDmitry Torokhov 			serio_attach_driver(event->object);
2258ee294cdSDmitry Torokhov 			break;
2268ee294cdSDmitry Torokhov 		}
2278ee294cdSDmitry Torokhov 
22819e95541SDuncan Laurie 		serio_remove_duplicate_events(event->object, event->type);
2298ee294cdSDmitry Torokhov 		serio_free_event(event);
2308ee294cdSDmitry Torokhov 	}
2318ee294cdSDmitry Torokhov 
2328ee294cdSDmitry Torokhov 	mutex_unlock(&serio_mutex);
2338ee294cdSDmitry Torokhov }
2348ee294cdSDmitry Torokhov 
2358ee294cdSDmitry Torokhov static DECLARE_WORK(serio_event_work, serio_handle_event);
2361da177e4SLinus Torvalds 
serio_queue_event(void * object,struct module * owner,enum serio_event_type event_type)237ed7b1f6dSDmitry Torokhov static int serio_queue_event(void *object, struct module *owner,
2381da177e4SLinus Torvalds 			     enum serio_event_type event_type)
2391da177e4SLinus Torvalds {
2401da177e4SLinus Torvalds 	unsigned long flags;
2411da177e4SLinus Torvalds 	struct serio_event *event;
242ed7b1f6dSDmitry Torokhov 	int retval = 0;
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	spin_lock_irqsave(&serio_event_lock, flags);
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 	/*
2471da177e4SLinus Torvalds 	 * Scan event list for the other events for the same serio port,
2481da177e4SLinus Torvalds 	 * starting with the most recent one. If event is the same we
2491da177e4SLinus Torvalds 	 * do not need add new one. If event is of different type we
2501da177e4SLinus Torvalds 	 * need to add this event and should not look further because
2511da177e4SLinus Torvalds 	 * we need to preseve sequence of distinct events.
2521da177e4SLinus Torvalds 	 */
2531da177e4SLinus Torvalds 	list_for_each_entry_reverse(event, &serio_event_list, node) {
2541da177e4SLinus Torvalds 		if (event->object == object) {
2551da177e4SLinus Torvalds 			if (event->type == event_type)
2561da177e4SLinus Torvalds 				goto out;
2571da177e4SLinus Torvalds 			break;
2581da177e4SLinus Torvalds 		}
2591da177e4SLinus Torvalds 	}
2601da177e4SLinus Torvalds 
261ed7b1f6dSDmitry Torokhov 	event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
262ed7b1f6dSDmitry Torokhov 	if (!event) {
263cac9169bSDmitry Torokhov 		pr_err("Not enough memory to queue event %d\n", event_type);
264ed7b1f6dSDmitry Torokhov 		retval = -ENOMEM;
265ed7b1f6dSDmitry Torokhov 		goto out;
266ed7b1f6dSDmitry Torokhov 	}
267ed7b1f6dSDmitry Torokhov 
2681da177e4SLinus Torvalds 	if (!try_module_get(owner)) {
269fef5f569SJoe Perches 		pr_warn("Can't get module reference, dropping event %d\n",
270ed7b1f6dSDmitry Torokhov 			event_type);
2719d921116SAdrian Bunk 		kfree(event);
272ed7b1f6dSDmitry Torokhov 		retval = -EINVAL;
2731da177e4SLinus Torvalds 		goto out;
2741da177e4SLinus Torvalds 	}
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	event->type = event_type;
2771da177e4SLinus Torvalds 	event->object = object;
2781da177e4SLinus Torvalds 	event->owner = owner;
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	list_add_tail(&event->node, &serio_event_list);
2811d64b655SDmitry Torokhov 	queue_work(system_long_wq, &serio_event_work);
282ed7b1f6dSDmitry Torokhov 
2831da177e4SLinus Torvalds out:
2841da177e4SLinus Torvalds 	spin_unlock_irqrestore(&serio_event_lock, flags);
285ed7b1f6dSDmitry Torokhov 	return retval;
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds /*
289e8ef4347SDmitry Torokhov  * Remove all events that have been submitted for a given
290e8ef4347SDmitry Torokhov  * object, be it serio port or driver.
2911da177e4SLinus Torvalds  */
serio_remove_pending_events(void * object)292e8ef4347SDmitry Torokhov static void serio_remove_pending_events(void *object)
2931da177e4SLinus Torvalds {
2944516c818SDmitry Torokhov 	struct serio_event *event, *next;
2951da177e4SLinus Torvalds 	unsigned long flags;
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	spin_lock_irqsave(&serio_event_lock, flags);
2981da177e4SLinus Torvalds 
2994516c818SDmitry Torokhov 	list_for_each_entry_safe(event, next, &serio_event_list, node) {
300e8ef4347SDmitry Torokhov 		if (event->object == object) {
3014516c818SDmitry Torokhov 			list_del_init(&event->node);
3021da177e4SLinus Torvalds 			serio_free_event(event);
3031da177e4SLinus Torvalds 		}
3041da177e4SLinus Torvalds 	}
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	spin_unlock_irqrestore(&serio_event_lock, flags);
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds /*
31009822582SDmitry Eremin-Solenikov  * Locate child serio port (if any) that has not been fully registered yet.
3111da177e4SLinus Torvalds  *
31209822582SDmitry Eremin-Solenikov  * Children are registered by driver's connect() handler so there can't be a
31309822582SDmitry Eremin-Solenikov  * grandchild pending registration together with a child.
3141da177e4SLinus Torvalds  */
serio_get_pending_child(struct serio * parent)3151da177e4SLinus Torvalds static struct serio *serio_get_pending_child(struct serio *parent)
3161da177e4SLinus Torvalds {
3171da177e4SLinus Torvalds 	struct serio_event *event;
3181da177e4SLinus Torvalds 	struct serio *serio, *child = NULL;
3191da177e4SLinus Torvalds 	unsigned long flags;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	spin_lock_irqsave(&serio_event_lock, flags);
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds 	list_for_each_entry(event, &serio_event_list, node) {
3241da177e4SLinus Torvalds 		if (event->type == SERIO_REGISTER_PORT) {
3251da177e4SLinus Torvalds 			serio = event->object;
3261da177e4SLinus Torvalds 			if (serio->parent == parent) {
3271da177e4SLinus Torvalds 				child = serio;
3281da177e4SLinus Torvalds 				break;
3291da177e4SLinus Torvalds 			}
3301da177e4SLinus Torvalds 		}
3311da177e4SLinus Torvalds 	}
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	spin_unlock_irqrestore(&serio_event_lock, flags);
3341da177e4SLinus Torvalds 	return child;
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds /*
3381da177e4SLinus Torvalds  * Serio port operations
3391da177e4SLinus Torvalds  */
3401da177e4SLinus Torvalds 
serio_show_description(struct device * dev,struct device_attribute * attr,char * buf)341e404e274SYani Ioannou static ssize_t serio_show_description(struct device *dev, struct device_attribute *attr, char *buf)
3421da177e4SLinus Torvalds {
3431da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
3441da177e4SLinus Torvalds 	return sprintf(buf, "%s\n", serio->name);
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
modalias_show(struct device * dev,struct device_attribute * attr,char * buf)3473778a212SGreg Kroah-Hartman static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
348ae87dff7SDmitry Torokhov {
349ae87dff7SDmitry Torokhov 	struct serio *serio = to_serio_port(dev);
350ae87dff7SDmitry Torokhov 
351ae87dff7SDmitry Torokhov 	return sprintf(buf, "serio:ty%02Xpr%02Xid%02Xex%02X\n",
352ae87dff7SDmitry Torokhov 			serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
353ae87dff7SDmitry Torokhov }
354ae87dff7SDmitry Torokhov 
type_show(struct device * dev,struct device_attribute * attr,char * buf)3557eab8dedSGreg Kroah-Hartman static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf)
3561da177e4SLinus Torvalds {
3571da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
3581da177e4SLinus Torvalds 	return sprintf(buf, "%02x\n", serio->id.type);
3591da177e4SLinus Torvalds }
3601da177e4SLinus Torvalds 
proto_show(struct device * dev,struct device_attribute * attr,char * buf)3617eab8dedSGreg Kroah-Hartman static ssize_t proto_show(struct device *dev, struct device_attribute *attr, char *buf)
3621da177e4SLinus Torvalds {
3631da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
3641da177e4SLinus Torvalds 	return sprintf(buf, "%02x\n", serio->id.proto);
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds 
id_show(struct device * dev,struct device_attribute * attr,char * buf)3677eab8dedSGreg Kroah-Hartman static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)
3681da177e4SLinus Torvalds {
3691da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
3701da177e4SLinus Torvalds 	return sprintf(buf, "%02x\n", serio->id.id);
3711da177e4SLinus Torvalds }
3721da177e4SLinus Torvalds 
extra_show(struct device * dev,struct device_attribute * attr,char * buf)3737eab8dedSGreg Kroah-Hartman static ssize_t extra_show(struct device *dev, struct device_attribute *attr, char *buf)
3741da177e4SLinus Torvalds {
3751da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
3761da177e4SLinus Torvalds 	return sprintf(buf, "%02x\n", serio->id.extra);
3771da177e4SLinus Torvalds }
3781da177e4SLinus Torvalds 
drvctl_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)3793778a212SGreg Kroah-Hartman static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
3801da177e4SLinus Torvalds {
3811da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
3821da177e4SLinus Torvalds 	struct device_driver *drv;
38370f256fdSDmitry Torokhov 	int error;
3841da177e4SLinus Torvalds 
38570f256fdSDmitry Torokhov 	error = mutex_lock_interruptible(&serio_mutex);
38670f256fdSDmitry Torokhov 	if (error)
38770f256fdSDmitry Torokhov 		return error;
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	if (!strncmp(buf, "none", count)) {
3901da177e4SLinus Torvalds 		serio_disconnect_port(serio);
3911da177e4SLinus Torvalds 	} else if (!strncmp(buf, "reconnect", count)) {
39209822582SDmitry Eremin-Solenikov 		serio_reconnect_subtree(serio);
3931da177e4SLinus Torvalds 	} else if (!strncmp(buf, "rescan", count)) {
3941da177e4SLinus Torvalds 		serio_disconnect_port(serio);
3951da177e4SLinus Torvalds 		serio_find_driver(serio);
39619e95541SDuncan Laurie 		serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
3971da177e4SLinus Torvalds 	} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
3981da177e4SLinus Torvalds 		serio_disconnect_port(serio);
39970f256fdSDmitry Torokhov 		error = serio_bind_driver(serio, to_serio_driver(drv));
40019e95541SDuncan Laurie 		serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
4011da177e4SLinus Torvalds 	} else {
40270f256fdSDmitry Torokhov 		error = -EINVAL;
4031da177e4SLinus Torvalds 	}
4041da177e4SLinus Torvalds 
405c4e32e9fSArjan van de Ven 	mutex_unlock(&serio_mutex);
4061da177e4SLinus Torvalds 
40770f256fdSDmitry Torokhov 	return error ? error : count;
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds 
serio_show_bind_mode(struct device * dev,struct device_attribute * attr,char * buf)410e404e274SYani Ioannou static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf)
4111da177e4SLinus Torvalds {
4121da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
4131da177e4SLinus Torvalds 	return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds 
serio_set_bind_mode(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)416e404e274SYani Ioannou static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
4171da177e4SLinus Torvalds {
4181da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
4191da177e4SLinus Torvalds 	int retval;
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	retval = count;
4221da177e4SLinus Torvalds 	if (!strncmp(buf, "manual", count)) {
4237e044e05SDmitry Torokhov 		serio->manual_bind = true;
4241da177e4SLinus Torvalds 	} else if (!strncmp(buf, "auto", count)) {
4257e044e05SDmitry Torokhov 		serio->manual_bind = false;
4261da177e4SLinus Torvalds 	} else {
4271da177e4SLinus Torvalds 		retval = -EINVAL;
4281da177e4SLinus Torvalds 	}
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	return retval;
4311da177e4SLinus Torvalds }
4321da177e4SLinus Torvalds 
firmware_id_show(struct device * dev,struct device_attribute * attr,char * buf)4330456c66fSHans de Goede static ssize_t firmware_id_show(struct device *dev, struct device_attribute *attr, char *buf)
4340456c66fSHans de Goede {
4350456c66fSHans de Goede 	struct serio *serio = to_serio_port(dev);
4360456c66fSHans de Goede 
4370456c66fSHans de Goede 	return sprintf(buf, "%s\n", serio->firmware_id);
4380456c66fSHans de Goede }
4390456c66fSHans de Goede 
4403778a212SGreg Kroah-Hartman static DEVICE_ATTR_RO(type);
4413778a212SGreg Kroah-Hartman static DEVICE_ATTR_RO(proto);
4423778a212SGreg Kroah-Hartman static DEVICE_ATTR_RO(id);
4433778a212SGreg Kroah-Hartman static DEVICE_ATTR_RO(extra);
4443778a212SGreg Kroah-Hartman 
4453778a212SGreg Kroah-Hartman static struct attribute *serio_device_id_attrs[] = {
4463778a212SGreg Kroah-Hartman 	&dev_attr_type.attr,
4473778a212SGreg Kroah-Hartman 	&dev_attr_proto.attr,
4483778a212SGreg Kroah-Hartman 	&dev_attr_id.attr,
4493778a212SGreg Kroah-Hartman 	&dev_attr_extra.attr,
4503778a212SGreg Kroah-Hartman 	NULL
4511da177e4SLinus Torvalds };
4521da177e4SLinus Torvalds 
453547a915dSArvind Yadav static const struct attribute_group serio_id_attr_group = {
4543778a212SGreg Kroah-Hartman 	.name	= "id",
4553778a212SGreg Kroah-Hartman 	.attrs	= serio_device_id_attrs,
4563778a212SGreg Kroah-Hartman };
4573778a212SGreg Kroah-Hartman 
458e696c683SDmitry Torokhov static DEVICE_ATTR_RO(modalias);
459e696c683SDmitry Torokhov static DEVICE_ATTR_WO(drvctl);
460e696c683SDmitry Torokhov static DEVICE_ATTR(description, S_IRUGO, serio_show_description, NULL);
461e696c683SDmitry Torokhov static DEVICE_ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode);
4620456c66fSHans de Goede static DEVICE_ATTR_RO(firmware_id);
463e696c683SDmitry Torokhov 
464e696c683SDmitry Torokhov static struct attribute *serio_device_attrs[] = {
465e696c683SDmitry Torokhov 	&dev_attr_modalias.attr,
466e696c683SDmitry Torokhov 	&dev_attr_description.attr,
467e696c683SDmitry Torokhov 	&dev_attr_drvctl.attr,
468e696c683SDmitry Torokhov 	&dev_attr_bind_mode.attr,
4690456c66fSHans de Goede 	&dev_attr_firmware_id.attr,
470e696c683SDmitry Torokhov 	NULL
471e696c683SDmitry Torokhov };
472e696c683SDmitry Torokhov 
473547a915dSArvind Yadav static const struct attribute_group serio_device_attr_group = {
474e696c683SDmitry Torokhov 	.attrs	= serio_device_attrs,
475e696c683SDmitry Torokhov };
476e696c683SDmitry Torokhov 
4773778a212SGreg Kroah-Hartman static const struct attribute_group *serio_device_attr_groups[] = {
4783778a212SGreg Kroah-Hartman 	&serio_id_attr_group,
479e696c683SDmitry Torokhov 	&serio_device_attr_group,
4803778a212SGreg Kroah-Hartman 	NULL
4813778a212SGreg Kroah-Hartman };
4821da177e4SLinus Torvalds 
serio_release_port(struct device * dev)4831da177e4SLinus Torvalds static void serio_release_port(struct device *dev)
4841da177e4SLinus Torvalds {
4851da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	kfree(serio);
4881da177e4SLinus Torvalds 	module_put(THIS_MODULE);
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds /*
4921da177e4SLinus Torvalds  * Prepare serio port for registration.
4931da177e4SLinus Torvalds  */
serio_init_port(struct serio * serio)4941da177e4SLinus Torvalds static void serio_init_port(struct serio *serio)
4951da177e4SLinus Torvalds {
496939ffb17SAniroop Mathur 	static atomic_t serio_no = ATOMIC_INIT(-1);
4971da177e4SLinus Torvalds 
4981da177e4SLinus Torvalds 	__module_get(THIS_MODULE);
4991da177e4SLinus Torvalds 
50073b59a3bSRandy Dunlap 	INIT_LIST_HEAD(&serio->node);
50109822582SDmitry Eremin-Solenikov 	INIT_LIST_HEAD(&serio->child_node);
50209822582SDmitry Eremin-Solenikov 	INIT_LIST_HEAD(&serio->children);
5031da177e4SLinus Torvalds 	spin_lock_init(&serio->lock);
504c4e32e9fSArjan van de Ven 	mutex_init(&serio->drv_mutex);
5051da177e4SLinus Torvalds 	device_initialize(&serio->dev);
5060224ec9eSRichard Leitner 	dev_set_name(&serio->dev, "serio%lu",
507939ffb17SAniroop Mathur 		     (unsigned long)atomic_inc_return(&serio_no));
5081da177e4SLinus Torvalds 	serio->dev.bus = &serio_bus;
5091da177e4SLinus Torvalds 	serio->dev.release = serio_release_port;
510386d8772SDmitry Torokhov 	serio->dev.groups = serio_device_attr_groups;
51188aa0103SJiri Kosina 	if (serio->parent) {
5121da177e4SLinus Torvalds 		serio->dev.parent = &serio->parent->dev;
51388aa0103SJiri Kosina 		serio->depth = serio->parent->depth + 1;
51488aa0103SJiri Kosina 	} else
51588aa0103SJiri Kosina 		serio->depth = 0;
51688aa0103SJiri Kosina 	lockdep_set_subclass(&serio->lock, serio->depth);
5171da177e4SLinus Torvalds }
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds /*
5201da177e4SLinus Torvalds  * Complete serio port registration.
5211da177e4SLinus Torvalds  * Driver core will attempt to find appropriate driver for the port.
5221da177e4SLinus Torvalds  */
serio_add_port(struct serio * serio)5231da177e4SLinus Torvalds static void serio_add_port(struct serio *serio)
5241da177e4SLinus Torvalds {
52509822582SDmitry Eremin-Solenikov 	struct serio *parent = serio->parent;
52673b59a3bSRandy Dunlap 	int error;
52773b59a3bSRandy Dunlap 
52809822582SDmitry Eremin-Solenikov 	if (parent) {
52909822582SDmitry Eremin-Solenikov 		serio_pause_rx(parent);
53009822582SDmitry Eremin-Solenikov 		list_add_tail(&serio->child_node, &parent->children);
53109822582SDmitry Eremin-Solenikov 		serio_continue_rx(parent);
5321da177e4SLinus Torvalds 	}
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 	list_add_tail(&serio->node, &serio_list);
535386d8772SDmitry Torokhov 
5361da177e4SLinus Torvalds 	if (serio->start)
5371da177e4SLinus Torvalds 		serio->start(serio);
538386d8772SDmitry Torokhov 
53973b59a3bSRandy Dunlap 	error = device_add(&serio->dev);
54073b59a3bSRandy Dunlap 	if (error)
541cac9169bSDmitry Torokhov 		dev_err(&serio->dev,
542cac9169bSDmitry Torokhov 			"device_add() failed for %s (%s), error: %d\n",
54373b59a3bSRandy Dunlap 			serio->phys, serio->name, error);
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds /*
54709822582SDmitry Eremin-Solenikov  * serio_destroy_port() completes unregistration process and removes
5481da177e4SLinus Torvalds  * port from the system
5491da177e4SLinus Torvalds  */
serio_destroy_port(struct serio * serio)5501da177e4SLinus Torvalds static void serio_destroy_port(struct serio *serio)
5511da177e4SLinus Torvalds {
5521da177e4SLinus Torvalds 	struct serio *child;
5531da177e4SLinus Torvalds 
55409822582SDmitry Eremin-Solenikov 	while ((child = serio_get_pending_child(serio)) != NULL) {
5551da177e4SLinus Torvalds 		serio_remove_pending_events(child);
5561da177e4SLinus Torvalds 		put_device(&child->dev);
5571da177e4SLinus Torvalds 	}
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	if (serio->stop)
5601da177e4SLinus Torvalds 		serio->stop(serio);
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds 	if (serio->parent) {
5631da177e4SLinus Torvalds 		serio_pause_rx(serio->parent);
56409822582SDmitry Eremin-Solenikov 		list_del_init(&serio->child_node);
5651da177e4SLinus Torvalds 		serio_continue_rx(serio->parent);
5661da177e4SLinus Torvalds 		serio->parent = NULL;
5671da177e4SLinus Torvalds 	}
5681da177e4SLinus Torvalds 
569ddf1ffbdSDmitry Torokhov 	if (device_is_registered(&serio->dev))
5701da177e4SLinus Torvalds 		device_del(&serio->dev);
5711da177e4SLinus Torvalds 
57273b59a3bSRandy Dunlap 	list_del_init(&serio->node);
5731da177e4SLinus Torvalds 	serio_remove_pending_events(serio);
5741da177e4SLinus Torvalds 	put_device(&serio->dev);
5751da177e4SLinus Torvalds }
5761da177e4SLinus Torvalds 
5771da177e4SLinus Torvalds /*
5786aabcdffSShaohua Li  * Reconnect serio port (re-initialize attached device).
5796aabcdffSShaohua Li  * If reconnect fails (old device is no longer attached or
5806aabcdffSShaohua Li  * there was no device to begin with) we do full rescan in
5816aabcdffSShaohua Li  * hope of finding a driver for the port.
5821da177e4SLinus Torvalds  */
serio_reconnect_port(struct serio * serio)5836aabcdffSShaohua Li static int serio_reconnect_port(struct serio *serio)
5841da177e4SLinus Torvalds {
5856aabcdffSShaohua Li 	int error = serio_reconnect_driver(serio);
5866aabcdffSShaohua Li 
5876aabcdffSShaohua Li 	if (error) {
5881da177e4SLinus Torvalds 		serio_disconnect_port(serio);
5891da177e4SLinus Torvalds 		serio_find_driver(serio);
5906aabcdffSShaohua Li 	}
5916aabcdffSShaohua Li 
5926aabcdffSShaohua Li 	return error;
5936aabcdffSShaohua Li }
5946aabcdffSShaohua Li 
5956aabcdffSShaohua Li /*
59609822582SDmitry Eremin-Solenikov  * Reconnect serio port and all its children (re-initialize attached
59709822582SDmitry Eremin-Solenikov  * devices).
5986aabcdffSShaohua Li  */
serio_reconnect_subtree(struct serio * root)59909822582SDmitry Eremin-Solenikov static void serio_reconnect_subtree(struct serio *root)
6006aabcdffSShaohua Li {
60109822582SDmitry Eremin-Solenikov 	struct serio *s = root;
60209822582SDmitry Eremin-Solenikov 	int error;
60309822582SDmitry Eremin-Solenikov 
6046aabcdffSShaohua Li 	do {
60509822582SDmitry Eremin-Solenikov 		error = serio_reconnect_port(s);
60609822582SDmitry Eremin-Solenikov 		if (!error) {
60709822582SDmitry Eremin-Solenikov 			/*
60809822582SDmitry Eremin-Solenikov 			 * Reconnect was successful, move on to do the
60909822582SDmitry Eremin-Solenikov 			 * first child.
61009822582SDmitry Eremin-Solenikov 			 */
61109822582SDmitry Eremin-Solenikov 			if (!list_empty(&s->children)) {
61209822582SDmitry Eremin-Solenikov 				s = list_first_entry(&s->children,
61309822582SDmitry Eremin-Solenikov 						     struct serio, child_node);
61409822582SDmitry Eremin-Solenikov 				continue;
61509822582SDmitry Eremin-Solenikov 			}
61609822582SDmitry Eremin-Solenikov 		}
61709822582SDmitry Eremin-Solenikov 
61809822582SDmitry Eremin-Solenikov 		/*
61909822582SDmitry Eremin-Solenikov 		 * Either it was a leaf node or reconnect failed and it
62009822582SDmitry Eremin-Solenikov 		 * became a leaf node. Continue reconnecting starting with
62109822582SDmitry Eremin-Solenikov 		 * the next sibling of the parent node.
62209822582SDmitry Eremin-Solenikov 		 */
62309822582SDmitry Eremin-Solenikov 		while (s != root) {
62409822582SDmitry Eremin-Solenikov 			struct serio *parent = s->parent;
62509822582SDmitry Eremin-Solenikov 
62609822582SDmitry Eremin-Solenikov 			if (!list_is_last(&s->child_node, &parent->children)) {
62709822582SDmitry Eremin-Solenikov 				s = list_entry(s->child_node.next,
62809822582SDmitry Eremin-Solenikov 					       struct serio, child_node);
6291da177e4SLinus Torvalds 				break;
6301da177e4SLinus Torvalds 			}
63109822582SDmitry Eremin-Solenikov 
63209822582SDmitry Eremin-Solenikov 			s = parent;
63309822582SDmitry Eremin-Solenikov 		}
63409822582SDmitry Eremin-Solenikov 	} while (s != root);
6351da177e4SLinus Torvalds }
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds /*
6381da177e4SLinus Torvalds  * serio_disconnect_port() unbinds a port from its driver. As a side effect
63909822582SDmitry Eremin-Solenikov  * all children ports are unbound and destroyed.
6401da177e4SLinus Torvalds  */
serio_disconnect_port(struct serio * serio)6411da177e4SLinus Torvalds static void serio_disconnect_port(struct serio *serio)
6421da177e4SLinus Torvalds {
64309822582SDmitry Eremin-Solenikov 	struct serio *s = serio;
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds 	/*
6461da177e4SLinus Torvalds 	 * Children ports should be disconnected and destroyed
64709822582SDmitry Eremin-Solenikov 	 * first; we travel the tree in depth-first order.
6481da177e4SLinus Torvalds 	 */
64909822582SDmitry Eremin-Solenikov 	while (!list_empty(&serio->children)) {
6501da177e4SLinus Torvalds 
65109822582SDmitry Eremin-Solenikov 		/* Locate a leaf */
65209822582SDmitry Eremin-Solenikov 		while (!list_empty(&s->children))
65309822582SDmitry Eremin-Solenikov 			s = list_first_entry(&s->children,
65409822582SDmitry Eremin-Solenikov 					     struct serio, child_node);
65509822582SDmitry Eremin-Solenikov 
65609822582SDmitry Eremin-Solenikov 		/*
65709822582SDmitry Eremin-Solenikov 		 * Prune this leaf node unless it is the one we
65809822582SDmitry Eremin-Solenikov 		 * started with.
65909822582SDmitry Eremin-Solenikov 		 */
66009822582SDmitry Eremin-Solenikov 		if (s != serio) {
66109822582SDmitry Eremin-Solenikov 			struct serio *parent = s->parent;
6621da177e4SLinus Torvalds 
66370f256fdSDmitry Torokhov 			device_release_driver(&s->dev);
6641da177e4SLinus Torvalds 			serio_destroy_port(s);
66509822582SDmitry Eremin-Solenikov 
66609822582SDmitry Eremin-Solenikov 			s = parent;
66709822582SDmitry Eremin-Solenikov 		}
6681da177e4SLinus Torvalds 	}
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds 	/*
67109822582SDmitry Eremin-Solenikov 	 * OK, no children left, now disconnect this port.
6721da177e4SLinus Torvalds 	 */
67370f256fdSDmitry Torokhov 	device_release_driver(&serio->dev);
6741da177e4SLinus Torvalds }
6751da177e4SLinus Torvalds 
serio_rescan(struct serio * serio)6761da177e4SLinus Torvalds void serio_rescan(struct serio *serio)
6771da177e4SLinus Torvalds {
678ed7b1f6dSDmitry Torokhov 	serio_queue_event(serio, NULL, SERIO_RESCAN_PORT);
6791da177e4SLinus Torvalds }
68089b09b99SDmitry Torokhov EXPORT_SYMBOL(serio_rescan);
6811da177e4SLinus Torvalds 
serio_reconnect(struct serio * serio)6821da177e4SLinus Torvalds void serio_reconnect(struct serio *serio)
6831da177e4SLinus Torvalds {
68409822582SDmitry Eremin-Solenikov 	serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
6851da177e4SLinus Torvalds }
68689b09b99SDmitry Torokhov EXPORT_SYMBOL(serio_reconnect);
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds /*
6891da177e4SLinus Torvalds  * Submits register request to kseriod for subsequent execution.
6901da177e4SLinus Torvalds  * Note that port registration is always asynchronous.
6911da177e4SLinus Torvalds  */
__serio_register_port(struct serio * serio,struct module * owner)6921da177e4SLinus Torvalds void __serio_register_port(struct serio *serio, struct module *owner)
6931da177e4SLinus Torvalds {
6941da177e4SLinus Torvalds 	serio_init_port(serio);
6951da177e4SLinus Torvalds 	serio_queue_event(serio, owner, SERIO_REGISTER_PORT);
6961da177e4SLinus Torvalds }
69789b09b99SDmitry Torokhov EXPORT_SYMBOL(__serio_register_port);
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds /*
7001da177e4SLinus Torvalds  * Synchronously unregisters serio port.
7011da177e4SLinus Torvalds  */
serio_unregister_port(struct serio * serio)7021da177e4SLinus Torvalds void serio_unregister_port(struct serio *serio)
7031da177e4SLinus Torvalds {
704c4e32e9fSArjan van de Ven 	mutex_lock(&serio_mutex);
7051da177e4SLinus Torvalds 	serio_disconnect_port(serio);
7061da177e4SLinus Torvalds 	serio_destroy_port(serio);
707c4e32e9fSArjan van de Ven 	mutex_unlock(&serio_mutex);
7081da177e4SLinus Torvalds }
70989b09b99SDmitry Torokhov EXPORT_SYMBOL(serio_unregister_port);
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds /*
71209822582SDmitry Eremin-Solenikov  * Safely unregisters children ports if they are present.
7133c803e8eSLinus Torvalds  */
serio_unregister_child_port(struct serio * serio)7143c803e8eSLinus Torvalds void serio_unregister_child_port(struct serio *serio)
7153c803e8eSLinus Torvalds {
71609822582SDmitry Eremin-Solenikov 	struct serio *s, *next;
71709822582SDmitry Eremin-Solenikov 
718c4e32e9fSArjan van de Ven 	mutex_lock(&serio_mutex);
71909822582SDmitry Eremin-Solenikov 	list_for_each_entry_safe(s, next, &serio->children, child_node) {
72009822582SDmitry Eremin-Solenikov 		serio_disconnect_port(s);
72109822582SDmitry Eremin-Solenikov 		serio_destroy_port(s);
7223c803e8eSLinus Torvalds 	}
723c4e32e9fSArjan van de Ven 	mutex_unlock(&serio_mutex);
7243c803e8eSLinus Torvalds }
72589b09b99SDmitry Torokhov EXPORT_SYMBOL(serio_unregister_child_port);
7263c803e8eSLinus Torvalds 
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds /*
7291da177e4SLinus Torvalds  * Serio driver operations
7301da177e4SLinus Torvalds  */
7311da177e4SLinus Torvalds 
description_show(struct device_driver * drv,char * buf)7327048f5d0SGreg Kroah-Hartman static ssize_t description_show(struct device_driver *drv, char *buf)
7331da177e4SLinus Torvalds {
7341da177e4SLinus Torvalds 	struct serio_driver *driver = to_serio_driver(drv);
7351da177e4SLinus Torvalds 	return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
7361da177e4SLinus Torvalds }
7377048f5d0SGreg Kroah-Hartman static DRIVER_ATTR_RO(description);
7381da177e4SLinus Torvalds 
bind_mode_show(struct device_driver * drv,char * buf)7397048f5d0SGreg Kroah-Hartman static ssize_t bind_mode_show(struct device_driver *drv, char *buf)
7401da177e4SLinus Torvalds {
7411da177e4SLinus Torvalds 	struct serio_driver *serio_drv = to_serio_driver(drv);
7421da177e4SLinus Torvalds 	return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");
7431da177e4SLinus Torvalds }
7441da177e4SLinus Torvalds 
bind_mode_store(struct device_driver * drv,const char * buf,size_t count)7457048f5d0SGreg Kroah-Hartman static ssize_t bind_mode_store(struct device_driver *drv, const char *buf, size_t count)
7461da177e4SLinus Torvalds {
7471da177e4SLinus Torvalds 	struct serio_driver *serio_drv = to_serio_driver(drv);
7481da177e4SLinus Torvalds 	int retval;
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds 	retval = count;
7511da177e4SLinus Torvalds 	if (!strncmp(buf, "manual", count)) {
7527e044e05SDmitry Torokhov 		serio_drv->manual_bind = true;
7531da177e4SLinus Torvalds 	} else if (!strncmp(buf, "auto", count)) {
7547e044e05SDmitry Torokhov 		serio_drv->manual_bind = false;
7551da177e4SLinus Torvalds 	} else {
7561da177e4SLinus Torvalds 		retval = -EINVAL;
7571da177e4SLinus Torvalds 	}
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	return retval;
7601da177e4SLinus Torvalds }
7617048f5d0SGreg Kroah-Hartman static DRIVER_ATTR_RW(bind_mode);
7621da177e4SLinus Torvalds 
7637048f5d0SGreg Kroah-Hartman static struct attribute *serio_driver_attrs[] = {
7647048f5d0SGreg Kroah-Hartman 	&driver_attr_description.attr,
7657048f5d0SGreg Kroah-Hartman 	&driver_attr_bind_mode.attr,
7667048f5d0SGreg Kroah-Hartman 	NULL,
7671da177e4SLinus Torvalds };
7687048f5d0SGreg Kroah-Hartman ATTRIBUTE_GROUPS(serio_driver);
7691da177e4SLinus Torvalds 
serio_driver_probe(struct device * dev)7701da177e4SLinus Torvalds static int serio_driver_probe(struct device *dev)
7711da177e4SLinus Torvalds {
7721da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
7731da177e4SLinus Torvalds 	struct serio_driver *drv = to_serio_driver(dev->driver);
7741da177e4SLinus Torvalds 
7753c803e8eSLinus Torvalds 	return serio_connect_driver(serio, drv);
7761da177e4SLinus Torvalds }
7771da177e4SLinus Torvalds 
serio_driver_remove(struct device * dev)778fc7a6209SUwe Kleine-König static void serio_driver_remove(struct device *dev)
7791da177e4SLinus Torvalds {
7801da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
7811da177e4SLinus Torvalds 
7823c803e8eSLinus Torvalds 	serio_disconnect_driver(serio);
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds 
serio_cleanup(struct serio * serio)78582dd9effSDmitry Torokhov static void serio_cleanup(struct serio *serio)
78682dd9effSDmitry Torokhov {
78733143ea1SDmitry Torokhov 	mutex_lock(&serio->drv_mutex);
78882dd9effSDmitry Torokhov 	if (serio->drv && serio->drv->cleanup)
78982dd9effSDmitry Torokhov 		serio->drv->cleanup(serio);
79033143ea1SDmitry Torokhov 	mutex_unlock(&serio->drv_mutex);
79182dd9effSDmitry Torokhov }
79282dd9effSDmitry Torokhov 
serio_shutdown(struct device * dev)79382dd9effSDmitry Torokhov static void serio_shutdown(struct device *dev)
79482dd9effSDmitry Torokhov {
79582dd9effSDmitry Torokhov 	struct serio *serio = to_serio_port(dev);
79682dd9effSDmitry Torokhov 
79782dd9effSDmitry Torokhov 	serio_cleanup(serio);
79882dd9effSDmitry Torokhov }
79982dd9effSDmitry Torokhov 
serio_attach_driver(struct serio_driver * drv)800ed7b1f6dSDmitry Torokhov static void serio_attach_driver(struct serio_driver *drv)
80173b59a3bSRandy Dunlap {
80273b59a3bSRandy Dunlap 	int error;
80373b59a3bSRandy Dunlap 
804ed7b1f6dSDmitry Torokhov 	error = driver_attach(&drv->driver);
80573b59a3bSRandy Dunlap 	if (error)
806fef5f569SJoe Perches 		pr_warn("driver_attach() failed for %s with error %d\n",
80773b59a3bSRandy Dunlap 			drv->driver.name, error);
80873b59a3bSRandy Dunlap }
80973b59a3bSRandy Dunlap 
__serio_register_driver(struct serio_driver * drv,struct module * owner,const char * mod_name)8104b315627SGreg Kroah-Hartman int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
8111da177e4SLinus Torvalds {
8127e044e05SDmitry Torokhov 	bool manual_bind = drv->manual_bind;
813ed7b1f6dSDmitry Torokhov 	int error;
814ed7b1f6dSDmitry Torokhov 
8151da177e4SLinus Torvalds 	drv->driver.bus = &serio_bus;
8164b315627SGreg Kroah-Hartman 	drv->driver.owner = owner;
8174b315627SGreg Kroah-Hartman 	drv->driver.mod_name = mod_name;
8181da177e4SLinus Torvalds 
819ed7b1f6dSDmitry Torokhov 	/*
820ed7b1f6dSDmitry Torokhov 	 * Temporarily disable automatic binding because probing
821ed7b1f6dSDmitry Torokhov 	 * takes long time and we are better off doing it in kseriod
822ed7b1f6dSDmitry Torokhov 	 */
8237e044e05SDmitry Torokhov 	drv->manual_bind = true;
824ed7b1f6dSDmitry Torokhov 
825ed7b1f6dSDmitry Torokhov 	error = driver_register(&drv->driver);
826ed7b1f6dSDmitry Torokhov 	if (error) {
827cac9169bSDmitry Torokhov 		pr_err("driver_register() failed for %s, error: %d\n",
828ed7b1f6dSDmitry Torokhov 			drv->driver.name, error);
829ed7b1f6dSDmitry Torokhov 		return error;
830ed7b1f6dSDmitry Torokhov 	}
831ed7b1f6dSDmitry Torokhov 
832ed7b1f6dSDmitry Torokhov 	/*
833ed7b1f6dSDmitry Torokhov 	 * Restore original bind mode and let kseriod bind the
834ed7b1f6dSDmitry Torokhov 	 * driver to free ports
835ed7b1f6dSDmitry Torokhov 	 */
836ed7b1f6dSDmitry Torokhov 	if (!manual_bind) {
8377e044e05SDmitry Torokhov 		drv->manual_bind = false;
838ed7b1f6dSDmitry Torokhov 		error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
839ed7b1f6dSDmitry Torokhov 		if (error) {
840ed7b1f6dSDmitry Torokhov 			driver_unregister(&drv->driver);
841ed7b1f6dSDmitry Torokhov 			return error;
842ed7b1f6dSDmitry Torokhov 		}
843ed7b1f6dSDmitry Torokhov 	}
844ed7b1f6dSDmitry Torokhov 
845ed7b1f6dSDmitry Torokhov 	return 0;
8461da177e4SLinus Torvalds }
84789b09b99SDmitry Torokhov EXPORT_SYMBOL(__serio_register_driver);
8481da177e4SLinus Torvalds 
serio_unregister_driver(struct serio_driver * drv)8491da177e4SLinus Torvalds void serio_unregister_driver(struct serio_driver *drv)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	struct serio *serio;
8521da177e4SLinus Torvalds 
853c4e32e9fSArjan van de Ven 	mutex_lock(&serio_mutex);
854e8ef4347SDmitry Torokhov 
8557e044e05SDmitry Torokhov 	drv->manual_bind = true;	/* so serio_find_driver ignores it */
856e8ef4347SDmitry Torokhov 	serio_remove_pending_events(drv);
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds start_over:
8591da177e4SLinus Torvalds 	list_for_each_entry(serio, &serio_list, node) {
8601da177e4SLinus Torvalds 		if (serio->drv == drv) {
8611da177e4SLinus Torvalds 			serio_disconnect_port(serio);
8621da177e4SLinus Torvalds 			serio_find_driver(serio);
8631da177e4SLinus Torvalds 			/* we could've deleted some ports, restart */
8641da177e4SLinus Torvalds 			goto start_over;
8651da177e4SLinus Torvalds 		}
8661da177e4SLinus Torvalds 	}
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds 	driver_unregister(&drv->driver);
869c4e32e9fSArjan van de Ven 	mutex_unlock(&serio_mutex);
8701da177e4SLinus Torvalds }
87189b09b99SDmitry Torokhov EXPORT_SYMBOL(serio_unregister_driver);
8721da177e4SLinus Torvalds 
serio_set_drv(struct serio * serio,struct serio_driver * drv)8731da177e4SLinus Torvalds static void serio_set_drv(struct serio *serio, struct serio_driver *drv)
8741da177e4SLinus Torvalds {
8751da177e4SLinus Torvalds 	serio_pause_rx(serio);
8761da177e4SLinus Torvalds 	serio->drv = drv;
8771da177e4SLinus Torvalds 	serio_continue_rx(serio);
8781da177e4SLinus Torvalds }
8791da177e4SLinus Torvalds 
serio_bus_match(struct device * dev,struct device_driver * drv)8801da177e4SLinus Torvalds static int serio_bus_match(struct device *dev, struct device_driver *drv)
8811da177e4SLinus Torvalds {
8821da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
8831da177e4SLinus Torvalds 	struct serio_driver *serio_drv = to_serio_driver(drv);
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds 	if (serio->manual_bind || serio_drv->manual_bind)
8861da177e4SLinus Torvalds 		return 0;
8871da177e4SLinus Torvalds 
8881da177e4SLinus Torvalds 	return serio_match_port(serio_drv->id_table, serio);
8891da177e4SLinus Torvalds }
8901da177e4SLinus Torvalds 
891312c004dSKay Sievers #define SERIO_ADD_UEVENT_VAR(fmt, val...)				\
8921da177e4SLinus Torvalds 	do {								\
8937eff2e7aSKay Sievers 		int err = add_uevent_var(env, fmt, val);		\
894ae87dff7SDmitry Torokhov 		if (err)						\
895ae87dff7SDmitry Torokhov 			return err;					\
8961da177e4SLinus Torvalds 	} while (0)
897ae87dff7SDmitry Torokhov 
serio_uevent(const struct device * dev,struct kobj_uevent_env * env)898*2a81ada3SGreg Kroah-Hartman static int serio_uevent(const struct device *dev, struct kobj_uevent_env *env)
8991da177e4SLinus Torvalds {
900*2a81ada3SGreg Kroah-Hartman 	const struct serio *serio;
9011da177e4SLinus Torvalds 
9021da177e4SLinus Torvalds 	if (!dev)
9031da177e4SLinus Torvalds 		return -ENODEV;
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 	serio = to_serio_port(dev);
9061da177e4SLinus Torvalds 
907312c004dSKay Sievers 	SERIO_ADD_UEVENT_VAR("SERIO_TYPE=%02x", serio->id.type);
908312c004dSKay Sievers 	SERIO_ADD_UEVENT_VAR("SERIO_PROTO=%02x", serio->id.proto);
909312c004dSKay Sievers 	SERIO_ADD_UEVENT_VAR("SERIO_ID=%02x", serio->id.id);
910312c004dSKay Sievers 	SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra);
9110456c66fSHans de Goede 
912312c004dSKay Sievers 	SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",
913ae87dff7SDmitry Torokhov 				serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
9141da177e4SLinus Torvalds 
9150456c66fSHans de Goede 	if (serio->firmware_id[0])
9160456c66fSHans de Goede 		SERIO_ADD_UEVENT_VAR("SERIO_FIRMWARE_ID=%s",
9170456c66fSHans de Goede 				     serio->firmware_id);
9180456c66fSHans de Goede 
9191da177e4SLinus Torvalds 	return 0;
9201da177e4SLinus Torvalds }
921312c004dSKay Sievers #undef SERIO_ADD_UEVENT_VAR
9221da177e4SLinus Torvalds 
92382dd9effSDmitry Torokhov #ifdef CONFIG_PM
serio_suspend(struct device * dev)924633aae23SDmitry Torokhov static int serio_suspend(struct device *dev)
92582dd9effSDmitry Torokhov {
9267e044e05SDmitry Torokhov 	struct serio *serio = to_serio_port(dev);
92782dd9effSDmitry Torokhov 
9287e044e05SDmitry Torokhov 	serio_cleanup(serio);
929ddaa4343SThadeu Lima de Souza Cascardo 
93082dd9effSDmitry Torokhov 	return 0;
93182dd9effSDmitry Torokhov }
93282dd9effSDmitry Torokhov 
serio_resume(struct device * dev)9331da177e4SLinus Torvalds static int serio_resume(struct device *dev)
9341da177e4SLinus Torvalds {
9357e044e05SDmitry Torokhov 	struct serio *serio = to_serio_port(dev);
9365ea13206SDmitry Torokhov 	int error = -ENOENT;
9377e044e05SDmitry Torokhov 
9385ea13206SDmitry Torokhov 	mutex_lock(&serio->drv_mutex);
9395ea13206SDmitry Torokhov 	if (serio->drv && serio->drv->fast_reconnect) {
9405ea13206SDmitry Torokhov 		error = serio->drv->fast_reconnect(serio);
9415ea13206SDmitry Torokhov 		if (error && error != -ENOENT)
9425ea13206SDmitry Torokhov 			dev_warn(dev, "fast reconnect failed with error %d\n",
9435ea13206SDmitry Torokhov 				 error);
9445ea13206SDmitry Torokhov 	}
9455ea13206SDmitry Torokhov 	mutex_unlock(&serio->drv_mutex);
9465ea13206SDmitry Torokhov 
9475ea13206SDmitry Torokhov 	if (error) {
9481da177e4SLinus Torvalds 		/*
9495ea13206SDmitry Torokhov 		 * Driver reconnect can take a while, so better let
9505ea13206SDmitry Torokhov 		 * kseriod deal with it.
9511da177e4SLinus Torvalds 		 */
9527e044e05SDmitry Torokhov 		serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
9535ea13206SDmitry Torokhov 	}
95482dd9effSDmitry Torokhov 
9551da177e4SLinus Torvalds 	return 0;
9561da177e4SLinus Torvalds }
957633aae23SDmitry Torokhov 
958633aae23SDmitry Torokhov static const struct dev_pm_ops serio_pm_ops = {
959633aae23SDmitry Torokhov 	.suspend	= serio_suspend,
960633aae23SDmitry Torokhov 	.resume		= serio_resume,
961633aae23SDmitry Torokhov 	.poweroff	= serio_suspend,
962633aae23SDmitry Torokhov 	.restore	= serio_resume,
963633aae23SDmitry Torokhov };
96482dd9effSDmitry Torokhov #endif /* CONFIG_PM */
9651da177e4SLinus Torvalds 
966c4e32e9fSArjan van de Ven /* called from serio_driver->connect/disconnect methods under serio_mutex */
serio_open(struct serio * serio,struct serio_driver * drv)9671da177e4SLinus Torvalds int serio_open(struct serio *serio, struct serio_driver *drv)
9681da177e4SLinus Torvalds {
9691da177e4SLinus Torvalds 	serio_set_drv(serio, drv);
9701da177e4SLinus Torvalds 
9711da177e4SLinus Torvalds 	if (serio->open && serio->open(serio)) {
9721da177e4SLinus Torvalds 		serio_set_drv(serio, NULL);
9731da177e4SLinus Torvalds 		return -1;
9741da177e4SLinus Torvalds 	}
9751da177e4SLinus Torvalds 	return 0;
9761da177e4SLinus Torvalds }
97789b09b99SDmitry Torokhov EXPORT_SYMBOL(serio_open);
9781da177e4SLinus Torvalds 
979c4e32e9fSArjan van de Ven /* called from serio_driver->connect/disconnect methods under serio_mutex */
serio_close(struct serio * serio)9801da177e4SLinus Torvalds void serio_close(struct serio *serio)
9811da177e4SLinus Torvalds {
9821da177e4SLinus Torvalds 	if (serio->close)
9831da177e4SLinus Torvalds 		serio->close(serio);
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds 	serio_set_drv(serio, NULL);
9861da177e4SLinus Torvalds }
98789b09b99SDmitry Torokhov EXPORT_SYMBOL(serio_close);
9881da177e4SLinus Torvalds 
serio_interrupt(struct serio * serio,unsigned char data,unsigned int dfl)9891da177e4SLinus Torvalds irqreturn_t serio_interrupt(struct serio *serio,
9907d12e780SDavid Howells 		unsigned char data, unsigned int dfl)
9911da177e4SLinus Torvalds {
9921da177e4SLinus Torvalds 	unsigned long flags;
9931da177e4SLinus Torvalds 	irqreturn_t ret = IRQ_NONE;
9941da177e4SLinus Torvalds 
9951da177e4SLinus Torvalds 	spin_lock_irqsave(&serio->lock, flags);
9961da177e4SLinus Torvalds 
9971da177e4SLinus Torvalds         if (likely(serio->drv)) {
9987d12e780SDavid Howells                 ret = serio->drv->interrupt(serio, data, dfl);
999ddf1ffbdSDmitry Torokhov 	} else if (!dfl && device_is_registered(&serio->dev)) {
10001da177e4SLinus Torvalds 		serio_rescan(serio);
10011da177e4SLinus Torvalds 		ret = IRQ_HANDLED;
10021da177e4SLinus Torvalds 	}
10031da177e4SLinus Torvalds 
10041da177e4SLinus Torvalds 	spin_unlock_irqrestore(&serio->lock, flags);
10051da177e4SLinus Torvalds 
10061da177e4SLinus Torvalds 	return ret;
10071da177e4SLinus Torvalds }
100889b09b99SDmitry Torokhov EXPORT_SYMBOL(serio_interrupt);
10091da177e4SLinus Torvalds 
1010e1443d28SStephen Chandler Paul struct bus_type serio_bus = {
10111ea2a69dSMarton Nemeth 	.name		= "serio",
10127048f5d0SGreg Kroah-Hartman 	.drv_groups	= serio_driver_groups,
10131ea2a69dSMarton Nemeth 	.match		= serio_bus_match,
10141ea2a69dSMarton Nemeth 	.uevent		= serio_uevent,
10151ea2a69dSMarton Nemeth 	.probe		= serio_driver_probe,
10161ea2a69dSMarton Nemeth 	.remove		= serio_driver_remove,
101782dd9effSDmitry Torokhov 	.shutdown	= serio_shutdown,
101882dd9effSDmitry Torokhov #ifdef CONFIG_PM
1019633aae23SDmitry Torokhov 	.pm		= &serio_pm_ops,
102082dd9effSDmitry Torokhov #endif
10211ea2a69dSMarton Nemeth };
1022e1443d28SStephen Chandler Paul EXPORT_SYMBOL(serio_bus);
10231ea2a69dSMarton Nemeth 
serio_init(void)10241da177e4SLinus Torvalds static int __init serio_init(void)
10251da177e4SLinus Torvalds {
102673b59a3bSRandy Dunlap 	int error;
10271da177e4SLinus Torvalds 
102873b59a3bSRandy Dunlap 	error = bus_register(&serio_bus);
102973b59a3bSRandy Dunlap 	if (error) {
1030cac9169bSDmitry Torokhov 		pr_err("Failed to register serio bus, error: %d\n", error);
103173b59a3bSRandy Dunlap 		return error;
103273b59a3bSRandy Dunlap 	}
103373b59a3bSRandy Dunlap 
10341da177e4SLinus Torvalds 	return 0;
10351da177e4SLinus Torvalds }
10361da177e4SLinus Torvalds 
serio_exit(void)10371da177e4SLinus Torvalds static void __exit serio_exit(void)
10381da177e4SLinus Torvalds {
10391da177e4SLinus Torvalds 	bus_unregister(&serio_bus);
10408ee294cdSDmitry Torokhov 
10418ee294cdSDmitry Torokhov 	/*
10428ee294cdSDmitry Torokhov 	 * There should not be any outstanding events but work may
10438ee294cdSDmitry Torokhov 	 * still be scheduled so simply cancel it.
10448ee294cdSDmitry Torokhov 	 */
10458ee294cdSDmitry Torokhov 	cancel_work_sync(&serio_event_work);
10461da177e4SLinus Torvalds }
10471da177e4SLinus Torvalds 
104851c38f9bSDmitry Torokhov subsys_initcall(serio_init);
10491da177e4SLinus Torvalds module_exit(serio_exit);
1050