aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/serio
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-10-25 10:59:01 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-25 10:59:01 -0400
commit3a99c6319064af3f2e18eb929f638d555dbf7a62 (patch)
treee611927f41142123dc8efed7e07a3a91151edb01 /drivers/input/serio
parent1dfd166e93f98892aa4427069a23ed73259983c8 (diff)
parent49327ad2bbbaf1945d5ba431522201574219d150 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (75 commits) Input: wacom - specify Cinitq supported tools Input: ab8500-ponkey - fix IRQ freeing in error path Input: adp5588-keys - use more obvious i2c_device_id name string Input: ad7877 - switch to using threaded IRQ Input: ad7877 - use attribute group to control visibility of attributes Input: serio - add support for PS2Mult multiplexer protocol Input: wacom - properly enable runtime PM Input: ad7877 - filter events where pressure is beyond the maximum Input: ad7877 - implement EV_KEY:BTN_TOUCH reporting Input: ad7877 - implement specified chip select behavior Input: hp680_ts_input - use cancel_delayed_work_sync() Input: mousedev - correct lockdep annotation Input: ads7846 - switch to using threaded IRQ Input: serio - support multiple child devices per single parent Input: synaptics - simplify pass-through port handling Input: add ROHM BU21013 touch panel controller support Input: omap4-keypad - wake-up on events & long presses Input: omap4-keypad - fix interrupt line configuration Input: omap4-keypad - SYSCONFIG register configuration Input: omap4-keypad - use platform device helpers ...
Diffstat (limited to 'drivers/input/serio')
-rw-r--r--drivers/input/serio/Kconfig9
-rw-r--r--drivers/input/serio/Makefile1
-rw-r--r--drivers/input/serio/i8042.c2
-rw-r--r--drivers/input/serio/ps2mult.c318
-rw-r--r--drivers/input/serio/serio.c125
5 files changed, 410 insertions, 45 deletions
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 3bfe8fafc6ad..6256233d2bfb 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -226,4 +226,13 @@ config SERIO_AMS_DELTA
226 To compile this driver as a module, choose M here; 226 To compile this driver as a module, choose M here;
227 the module will be called ams_delta_serio. 227 the module will be called ams_delta_serio.
228 228
229config SERIO_PS2MULT
230 tristate "TQC PS/2 multiplexer"
231 help
232 Say Y here if you have the PS/2 line multiplexer like the one
233 present on TQC boads.
234
235 To compile this driver as a module, choose M here: the
236 module will be called ps2mult.
237
229endif 238endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 84c80bf7185e..dbbe37616c92 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o
18obj-$(CONFIG_HP_SDC) += hp_sdc.o 18obj-$(CONFIG_HP_SDC) += hp_sdc.o
19obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o 19obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o
20obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o 20obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
21obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o
21obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o 22obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
22obj-$(CONFIG_SERIO_LIBPS2) += libps2.o 23obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
23obj-$(CONFIG_SERIO_RAW) += serio_raw.o 24obj-$(CONFIG_SERIO_RAW) += serio_raw.o
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index f58513160480..18db5a8c7478 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -1063,7 +1063,7 @@ static long i8042_panic_blink(int state)
1063#ifdef CONFIG_X86 1063#ifdef CONFIG_X86
1064static void i8042_dritek_enable(void) 1064static void i8042_dritek_enable(void)
1065{ 1065{
1066 char param = 0x90; 1066 unsigned char param = 0x90;
1067 int error; 1067 int error;
1068 1068
1069 error = i8042_command(&param, 0x1059); 1069 error = i8042_command(&param, 0x1059);
diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c
new file mode 100644
index 000000000000..6bce22e4e495
--- /dev/null
+++ b/drivers/input/serio/ps2mult.c
@@ -0,0 +1,318 @@
1/*
2 * TQC PS/2 Multiplexer driver
3 *
4 * Copyright (C) 2010 Dmitry Eremin-Solenikov
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 */
10
11
12#include <linux/kernel.h>
13#include <linux/slab.h>
14#include <linux/module.h>
15#include <linux/serio.h>
16
17MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
18MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
19MODULE_LICENSE("GPL");
20
21#define PS2MULT_KB_SELECTOR 0xA0
22#define PS2MULT_MS_SELECTOR 0xA1
23#define PS2MULT_ESCAPE 0x7D
24#define PS2MULT_BSYNC 0x7E
25#define PS2MULT_SESSION_START 0x55
26#define PS2MULT_SESSION_END 0x56
27
28struct ps2mult_port {
29 struct serio *serio;
30 unsigned char sel;
31 bool registered;
32};
33
34#define PS2MULT_NUM_PORTS 2
35#define PS2MULT_KBD_PORT 0
36#define PS2MULT_MOUSE_PORT 1
37
38struct ps2mult {
39 struct serio *mx_serio;
40 struct ps2mult_port ports[PS2MULT_NUM_PORTS];
41
42 spinlock_t lock;
43 struct ps2mult_port *in_port;
44 struct ps2mult_port *out_port;
45 bool escape;
46};
47
48/* First MUST come PS2MULT_NUM_PORTS selectors */
49static const unsigned char ps2mult_controls[] = {
50 PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
51 PS2MULT_ESCAPE, PS2MULT_BSYNC,
52 PS2MULT_SESSION_START, PS2MULT_SESSION_END,
53};
54
55static const struct serio_device_id ps2mult_serio_ids[] = {
56 {
57 .type = SERIO_RS232,
58 .proto = SERIO_PS2MULT,
59 .id = SERIO_ANY,
60 .extra = SERIO_ANY,
61 },
62 { 0 }
63};
64
65MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
66
67static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
68{
69 struct serio *mx_serio = psm->mx_serio;
70
71 serio_write(mx_serio, port->sel);
72 psm->out_port = port;
73 dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
74}
75
76static int ps2mult_serio_write(struct serio *serio, unsigned char data)
77{
78 struct serio *mx_port = serio->parent;
79 struct ps2mult *psm = serio_get_drvdata(mx_port);
80 struct ps2mult_port *port = serio->port_data;
81 bool need_escape;
82 unsigned long flags;
83
84 spin_lock_irqsave(&psm->lock, flags);
85
86 if (psm->out_port != port)
87 ps2mult_select_port(psm, port);
88
89 need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
90
91 dev_dbg(&serio->dev,
92 "write: %s%02x\n", need_escape ? "ESC " : "", data);
93
94 if (need_escape)
95 serio_write(mx_port, PS2MULT_ESCAPE);
96
97 serio_write(mx_port, data);
98
99 spin_unlock_irqrestore(&psm->lock, flags);
100
101 return 0;
102}
103
104static int ps2mult_serio_start(struct serio *serio)
105{
106 struct ps2mult *psm = serio_get_drvdata(serio->parent);
107 struct ps2mult_port *port = serio->port_data;
108 unsigned long flags;
109
110 spin_lock_irqsave(&psm->lock, flags);
111 port->registered = true;
112 spin_unlock_irqrestore(&psm->lock, flags);
113
114 return 0;
115}
116
117static void ps2mult_serio_stop(struct serio *serio)
118{
119 struct ps2mult *psm = serio_get_drvdata(serio->parent);
120 struct ps2mult_port *port = serio->port_data;
121 unsigned long flags;
122
123 spin_lock_irqsave(&psm->lock, flags);
124 port->registered = false;
125 spin_unlock_irqrestore(&psm->lock, flags);
126}
127
128static int ps2mult_create_port(struct ps2mult *psm, int i)
129{
130 struct serio *mx_serio = psm->mx_serio;
131 struct serio *serio;
132
133 serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
134 if (!serio)
135 return -ENOMEM;
136
137 strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
138 snprintf(serio->phys, sizeof(serio->phys),
139 "%s/port%d", mx_serio->phys, i);
140 serio->id.type = SERIO_8042;
141 serio->write = ps2mult_serio_write;
142 serio->start = ps2mult_serio_start;
143 serio->stop = ps2mult_serio_stop;
144 serio->parent = psm->mx_serio;
145 serio->port_data = &psm->ports[i];
146
147 psm->ports[i].serio = serio;
148
149 return 0;
150}
151
152static void ps2mult_reset(struct ps2mult *psm)
153{
154 unsigned long flags;
155
156 spin_lock_irqsave(&psm->lock, flags);
157
158 serio_write(psm->mx_serio, PS2MULT_SESSION_END);
159 serio_write(psm->mx_serio, PS2MULT_SESSION_START);
160
161 ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
162
163 spin_unlock_irqrestore(&psm->lock, flags);
164}
165
166static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
167{
168 struct ps2mult *psm;
169 int i;
170 int error;
171
172 if (!serio->write)
173 return -EINVAL;
174
175 psm = kzalloc(sizeof(*psm), GFP_KERNEL);
176 if (!psm)
177 return -ENOMEM;
178
179 spin_lock_init(&psm->lock);
180 psm->mx_serio = serio;
181
182 for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
183 psm->ports[i].sel = ps2mult_controls[i];
184 error = ps2mult_create_port(psm, i);
185 if (error)
186 goto err_out;
187 }
188
189 psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
190
191 serio_set_drvdata(serio, psm);
192 error = serio_open(serio, drv);
193 if (error)
194 goto err_out;
195
196 ps2mult_reset(psm);
197
198 for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
199 struct serio *s = psm->ports[i].serio;
200
201 dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
202 serio_register_port(s);
203 }
204
205 return 0;
206
207err_out:
208 while (--i >= 0)
209 kfree(psm->ports[i].serio);
210 kfree(serio);
211 return error;
212}
213
214static void ps2mult_disconnect(struct serio *serio)
215{
216 struct ps2mult *psm = serio_get_drvdata(serio);
217
218 /* Note that serio core already take care of children ports */
219 serio_write(serio, PS2MULT_SESSION_END);
220 serio_close(serio);
221 kfree(psm);
222
223 serio_set_drvdata(serio, NULL);
224}
225
226static int ps2mult_reconnect(struct serio *serio)
227{
228 struct ps2mult *psm = serio_get_drvdata(serio);
229
230 ps2mult_reset(psm);
231
232 return 0;
233}
234
235static irqreturn_t ps2mult_interrupt(struct serio *serio,
236 unsigned char data, unsigned int dfl)
237{
238 struct ps2mult *psm = serio_get_drvdata(serio);
239 struct ps2mult_port *in_port;
240 unsigned long flags;
241
242 dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
243
244 spin_lock_irqsave(&psm->lock, flags);
245
246 if (psm->escape) {
247 psm->escape = false;
248 in_port = psm->in_port;
249 if (in_port->registered)
250 serio_interrupt(in_port->serio, data, dfl);
251 goto out;
252 }
253
254 switch (data) {
255 case PS2MULT_ESCAPE:
256 dev_dbg(&serio->dev, "ESCAPE\n");
257 psm->escape = true;
258 break;
259
260 case PS2MULT_BSYNC:
261 dev_dbg(&serio->dev, "BSYNC\n");
262 psm->in_port = psm->out_port;
263 break;
264
265 case PS2MULT_SESSION_START:
266 dev_dbg(&serio->dev, "SS\n");
267 break;
268
269 case PS2MULT_SESSION_END:
270 dev_dbg(&serio->dev, "SE\n");
271 break;
272
273 case PS2MULT_KB_SELECTOR:
274 dev_dbg(&serio->dev, "KB\n");
275 psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
276 break;
277
278 case PS2MULT_MS_SELECTOR:
279 dev_dbg(&serio->dev, "MS\n");
280 psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
281 break;
282
283 default:
284 in_port = psm->in_port;
285 if (in_port->registered)
286 serio_interrupt(in_port->serio, data, dfl);
287 break;
288 }
289
290 out:
291 spin_unlock_irqrestore(&psm->lock, flags);
292 return IRQ_HANDLED;
293}
294
295static struct serio_driver ps2mult_drv = {
296 .driver = {
297 .name = "ps2mult",
298 },
299 .description = "TQC PS/2 Multiplexer driver",
300 .id_table = ps2mult_serio_ids,
301 .interrupt = ps2mult_interrupt,
302 .connect = ps2mult_connect,
303 .disconnect = ps2mult_disconnect,
304 .reconnect = ps2mult_reconnect,
305};
306
307static int __init ps2mult_init(void)
308{
309 return serio_register_driver(&ps2mult_drv);
310}
311
312static void __exit ps2mult_exit(void)
313{
314 serio_unregister_driver(&ps2mult_drv);
315}
316
317module_init(ps2mult_init);
318module_exit(ps2mult_exit);
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index c3b626e9eae7..405bf214527c 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -37,7 +37,6 @@
37#include <linux/slab.h> 37#include <linux/slab.h>
38#include <linux/kthread.h> 38#include <linux/kthread.h>
39#include <linux/mutex.h> 39#include <linux/mutex.h>
40#include <linux/freezer.h>
41 40
42MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 41MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
43MODULE_DESCRIPTION("Serio abstraction core"); 42MODULE_DESCRIPTION("Serio abstraction core");
@@ -56,7 +55,7 @@ static struct bus_type serio_bus;
56static void serio_add_port(struct serio *serio); 55static void serio_add_port(struct serio *serio);
57static int serio_reconnect_port(struct serio *serio); 56static int serio_reconnect_port(struct serio *serio);
58static void serio_disconnect_port(struct serio *serio); 57static void serio_disconnect_port(struct serio *serio);
59static void serio_reconnect_chain(struct serio *serio); 58static void serio_reconnect_subtree(struct serio *serio);
60static void serio_attach_driver(struct serio_driver *drv); 59static void serio_attach_driver(struct serio_driver *drv);
61 60
62static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) 61static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
@@ -152,7 +151,7 @@ static void serio_find_driver(struct serio *serio)
152enum serio_event_type { 151enum serio_event_type {
153 SERIO_RESCAN_PORT, 152 SERIO_RESCAN_PORT,
154 SERIO_RECONNECT_PORT, 153 SERIO_RECONNECT_PORT,
155 SERIO_RECONNECT_CHAIN, 154 SERIO_RECONNECT_SUBTREE,
156 SERIO_REGISTER_PORT, 155 SERIO_REGISTER_PORT,
157 SERIO_ATTACH_DRIVER, 156 SERIO_ATTACH_DRIVER,
158}; 157};
@@ -292,8 +291,8 @@ static void serio_handle_event(void)
292 serio_find_driver(event->object); 291 serio_find_driver(event->object);
293 break; 292 break;
294 293
295 case SERIO_RECONNECT_CHAIN: 294 case SERIO_RECONNECT_SUBTREE:
296 serio_reconnect_chain(event->object); 295 serio_reconnect_subtree(event->object);
297 break; 296 break;
298 297
299 case SERIO_ATTACH_DRIVER: 298 case SERIO_ATTACH_DRIVER:
@@ -330,12 +329,10 @@ static void serio_remove_pending_events(void *object)
330} 329}
331 330
332/* 331/*
333 * Destroy child serio port (if any) that has not been fully registered yet. 332 * Locate child serio port (if any) that has not been fully registered yet.
334 * 333 *
335 * Note that we rely on the fact that port can have only one child and therefore 334 * Children are registered by driver's connect() handler so there can't be a
336 * only one child registration request can be pending. Additionally, children 335 * grandchild pending registration together with a child.
337 * are registered by driver's connect() handler so there can't be a grandchild
338 * pending registration together with a child.
339 */ 336 */
340static struct serio *serio_get_pending_child(struct serio *parent) 337static struct serio *serio_get_pending_child(struct serio *parent)
341{ 338{
@@ -449,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
449 if (!strncmp(buf, "none", count)) { 446 if (!strncmp(buf, "none", count)) {
450 serio_disconnect_port(serio); 447 serio_disconnect_port(serio);
451 } else if (!strncmp(buf, "reconnect", count)) { 448 } else if (!strncmp(buf, "reconnect", count)) {
452 serio_reconnect_chain(serio); 449 serio_reconnect_subtree(serio);
453 } else if (!strncmp(buf, "rescan", count)) { 450 } else if (!strncmp(buf, "rescan", count)) {
454 serio_disconnect_port(serio); 451 serio_disconnect_port(serio);
455 serio_find_driver(serio); 452 serio_find_driver(serio);
@@ -516,6 +513,8 @@ static void serio_init_port(struct serio *serio)
516 __module_get(THIS_MODULE); 513 __module_get(THIS_MODULE);
517 514
518 INIT_LIST_HEAD(&serio->node); 515 INIT_LIST_HEAD(&serio->node);
516 INIT_LIST_HEAD(&serio->child_node);
517 INIT_LIST_HEAD(&serio->children);
519 spin_lock_init(&serio->lock); 518 spin_lock_init(&serio->lock);
520 mutex_init(&serio->drv_mutex); 519 mutex_init(&serio->drv_mutex);
521 device_initialize(&serio->dev); 520 device_initialize(&serio->dev);
@@ -538,12 +537,13 @@ static void serio_init_port(struct serio *serio)
538 */ 537 */
539static void serio_add_port(struct serio *serio) 538static void serio_add_port(struct serio *serio)
540{ 539{
540 struct serio *parent = serio->parent;
541 int error; 541 int error;
542 542
543 if (serio->parent) { 543 if (parent) {
544 serio_pause_rx(serio->parent); 544 serio_pause_rx(parent);
545 serio->parent->child = serio; 545 list_add_tail(&serio->child_node, &parent->children);
546 serio_continue_rx(serio->parent); 546 serio_continue_rx(parent);
547 } 547 }
548 548
549 list_add_tail(&serio->node, &serio_list); 549 list_add_tail(&serio->node, &serio_list);
@@ -559,15 +559,14 @@ static void serio_add_port(struct serio *serio)
559} 559}
560 560
561/* 561/*
562 * serio_destroy_port() completes deregistration process and removes 562 * serio_destroy_port() completes unregistration process and removes
563 * port from the system 563 * port from the system
564 */ 564 */
565static void serio_destroy_port(struct serio *serio) 565static void serio_destroy_port(struct serio *serio)
566{ 566{
567 struct serio *child; 567 struct serio *child;
568 568
569 child = serio_get_pending_child(serio); 569 while ((child = serio_get_pending_child(serio)) != NULL) {
570 if (child) {
571 serio_remove_pending_events(child); 570 serio_remove_pending_events(child);
572 put_device(&child->dev); 571 put_device(&child->dev);
573 } 572 }
@@ -577,7 +576,7 @@ static void serio_destroy_port(struct serio *serio)
577 576
578 if (serio->parent) { 577 if (serio->parent) {
579 serio_pause_rx(serio->parent); 578 serio_pause_rx(serio->parent);
580 serio->parent->child = NULL; 579 list_del_init(&serio->child_node);
581 serio_continue_rx(serio->parent); 580 serio_continue_rx(serio->parent);
582 serio->parent = NULL; 581 serio->parent = NULL;
583 } 582 }
@@ -609,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio)
609} 608}
610 609
611/* 610/*
612 * Reconnect serio port and all its children (re-initialize attached devices) 611 * Reconnect serio port and all its children (re-initialize attached
612 * devices).
613 */ 613 */
614static void serio_reconnect_chain(struct serio *serio) 614static void serio_reconnect_subtree(struct serio *root)
615{ 615{
616 struct serio *s = root;
617 int error;
618
616 do { 619 do {
617 if (serio_reconnect_port(serio)) { 620 error = serio_reconnect_port(s);
618 /* Ok, old children are now gone, we are done */ 621 if (!error) {
619 break; 622 /*
623 * Reconnect was successful, move on to do the
624 * first child.
625 */
626 if (!list_empty(&s->children)) {
627 s = list_first_entry(&s->children,
628 struct serio, child_node);
629 continue;
630 }
620 } 631 }
621 serio = serio->child; 632
622 } while (serio); 633 /*
634 * Either it was a leaf node or reconnect failed and it
635 * became a leaf node. Continue reconnecting starting with
636 * the next sibling of the parent node.
637 */
638 while (s != root) {
639 struct serio *parent = s->parent;
640
641 if (!list_is_last(&s->child_node, &parent->children)) {
642 s = list_entry(s->child_node.next,
643 struct serio, child_node);
644 break;
645 }
646
647 s = parent;
648 }
649 } while (s != root);
623} 650}
624 651
625/* 652/*
626 * serio_disconnect_port() unbinds a port from its driver. As a side effect 653 * serio_disconnect_port() unbinds a port from its driver. As a side effect
627 * all child ports are unbound and destroyed. 654 * all children ports are unbound and destroyed.
628 */ 655 */
629static void serio_disconnect_port(struct serio *serio) 656static void serio_disconnect_port(struct serio *serio)
630{ 657{
631 struct serio *s, *parent; 658 struct serio *s = serio;
659
660 /*
661 * Children ports should be disconnected and destroyed
662 * first; we travel the tree in depth-first order.
663 */
664 while (!list_empty(&serio->children)) {
665
666 /* Locate a leaf */
667 while (!list_empty(&s->children))
668 s = list_first_entry(&s->children,
669 struct serio, child_node);
632 670
633 if (serio->child) {
634 /* 671 /*
635 * Children ports should be disconnected and destroyed 672 * Prune this leaf node unless it is the one we
636 * first, staring with the leaf one, since we don't want 673 * started with.
637 * to do recursion
638 */ 674 */
639 for (s = serio; s->child; s = s->child) 675 if (s != serio) {
640 /* empty */; 676 struct serio *parent = s->parent;
641
642 do {
643 parent = s->parent;
644 677
645 device_release_driver(&s->dev); 678 device_release_driver(&s->dev);
646 serio_destroy_port(s); 679 serio_destroy_port(s);
647 } while ((s = parent) != serio); 680
681 s = parent;
682 }
648 } 683 }
649 684
650 /* 685 /*
651 * Ok, no children left, now disconnect this port 686 * OK, no children left, now disconnect this port.
652 */ 687 */
653 device_release_driver(&serio->dev); 688 device_release_driver(&serio->dev);
654} 689}
@@ -661,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan);
661 696
662void serio_reconnect(struct serio *serio) 697void serio_reconnect(struct serio *serio)
663{ 698{
664 serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN); 699 serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
665} 700}
666EXPORT_SYMBOL(serio_reconnect); 701EXPORT_SYMBOL(serio_reconnect);
667 702
@@ -689,14 +724,16 @@ void serio_unregister_port(struct serio *serio)
689EXPORT_SYMBOL(serio_unregister_port); 724EXPORT_SYMBOL(serio_unregister_port);
690 725
691/* 726/*
692 * Safely unregisters child port if one is present. 727 * Safely unregisters children ports if they are present.
693 */ 728 */
694void serio_unregister_child_port(struct serio *serio) 729void serio_unregister_child_port(struct serio *serio)
695{ 730{
731 struct serio *s, *next;
732
696 mutex_lock(&serio_mutex); 733 mutex_lock(&serio_mutex);
697 if (serio->child) { 734 list_for_each_entry_safe(s, next, &serio->children, child_node) {
698 serio_disconnect_port(serio->child); 735 serio_disconnect_port(s);
699 serio_destroy_port(serio->child); 736 serio_destroy_port(s);
700 } 737 }
701 mutex_unlock(&serio_mutex); 738 mutex_unlock(&serio_mutex);
702} 739}