Inter-revision diff: patch 8

Comparing v2 (message) to v1 (message)

--- v2
+++ v1
@@ -1,128 +1,321 @@
-By sending a full frame of events at the same time, the irqsoff
-latency at heavy load is brought down from 200 us to 100 us.
+Preparing to move more repeated code into the mt core, and a flags
+argument to the input_mt_slots_init() function.
 
 Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
 ---
- drivers/input/evdev.c | 68 +++++++++++++++++++++++++++++++++++----------------
- 1 file changed, 47 insertions(+), 21 deletions(-)
+ drivers/hid/hid-magicmouse.c             | 2 +-
+ drivers/hid/hid-multitouch.c             | 2 +-
+ drivers/input/input-mt.c                 | 4 +++-
+ drivers/input/misc/uinput.c              | 2 +-
+ drivers/input/mouse/alps.c               | 2 +-
+ drivers/input/mouse/elantech.c           | 4 ++--
+ drivers/input/mouse/sentelic.c           | 2 +-
+ drivers/input/mouse/synaptics.c          | 4 ++--
+ drivers/input/tablet/wacom_wac.c         | 6 +++---
+ drivers/input/touchscreen/atmel_mxt_ts.c | 2 +-
+ drivers/input/touchscreen/cyttsp_core.c  | 2 +-
+ drivers/input/touchscreen/edt-ft5x06.c   | 2 +-
+ drivers/input/touchscreen/egalax_ts.c    | 2 +-
+ drivers/input/touchscreen/ili210x.c      | 2 +-
+ drivers/input/touchscreen/mms114.c       | 2 +-
+ drivers/input/touchscreen/penmount.c     | 2 +-
+ drivers/input/touchscreen/wacom_w8001.c  | 2 +-
+ include/linux/input/mt.h                 | 5 ++++-
+ 18 files changed, 27 insertions(+), 22 deletions(-)
 
-diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
-index a0692c5..1bf4ce5 100644
---- a/drivers/input/evdev.c
-+++ b/drivers/input/evdev.c
-@@ -54,16 +54,9 @@ struct evdev_client {
- static struct evdev *evdev_table[EVDEV_MINORS];
- static DEFINE_MUTEX(evdev_table_mutex);
- 
--static void evdev_pass_event(struct evdev_client *client,
--			     struct input_event *event,
--			     ktime_t mono, ktime_t real)
-+static void __pass_event(struct evdev_client *client,
-+			 const struct input_event *event)
+diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
+index e425de4..eae3aaa 100644
+--- a/drivers/hid/hid-magicmouse.c
++++ b/drivers/hid/hid-magicmouse.c
+@@ -392,7 +392,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
+ 
+ 	__set_bit(EV_ABS, input->evbit);
+ 
+-	error = input_mt_init_slots(input, 16);
++	error = input_mt_init_slots(input, 16, 0);
+ 	if (error)
+ 		return error;
+ 	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index 59c8b5c..c400d90 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -390,7 +390,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ 		case HID_DG_CONTACTID:
+ 			if (!td->maxcontacts)
+ 				td->maxcontacts = MT_DEFAULT_MAXCONTACT;
+-			input_mt_init_slots(hi->input, td->maxcontacts);
++			input_mt_init_slots(hi->input, td->maxcontacts, 0);
+ 			mt_store_field(usage, td, hi);
+ 			td->last_field_index = field->index;
+ 			td->touches_by_report++;
+diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
+index f956b27..bbb3304 100644
+--- a/drivers/input/input-mt.c
++++ b/drivers/input/input-mt.c
+@@ -25,7 +25,8 @@
+  * May be called repeatedly. Returns -EINVAL if attempting to
+  * reinitialize with a different number of slots.
+  */
+-int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots)
++int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
++			unsigned int flags)
  {
--	event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
--					mono : real);
--
--	/* Interrupts are disabled, just acquire the lock. */
--	spin_lock(&client->buffer_lock);
--
- 	client->buffer[client->head++] = *event;
- 	client->head &= client->bufsize - 1;
- 
-@@ -86,42 +79,74 @@ static void evdev_pass_event(struct evdev_client *client,
- 		client->packet_head = client->head;
- 		kill_fasync(&client->fasync, SIGIO, POLL_IN);
+ 	struct input_mt *mt = dev->mt;
+ 	int i;
+@@ -40,6 +41,7 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots)
+ 		return -ENOMEM;
+ 
+ 	mt->num_slots = num_slots;
++	mt->flags = flags;
+ 	input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
+ 	input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
+ 
+diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
+index 7360568..6b17975 100644
+--- a/drivers/input/misc/uinput.c
++++ b/drivers/input/misc/uinput.c
+@@ -405,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
+ 			goto exit;
+ 		if (test_bit(ABS_MT_SLOT, dev->absbit)) {
+ 			int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
+-			input_mt_init_slots(dev, nslot);
++			input_mt_init_slots(dev, nslot, 0);
+ 		} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+ 			input_set_events_per_packet(dev, 60);
+ 		}
+diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
+index 4a1347e..cf5af1f 100644
+--- a/drivers/input/mouse/alps.c
++++ b/drivers/input/mouse/alps.c
+@@ -1620,7 +1620,7 @@ int alps_init(struct psmouse *psmouse)
+ 	case ALPS_PROTO_V3:
+ 	case ALPS_PROTO_V4:
+ 		set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
+-		input_mt_init_slots(dev1, 2);
++		input_mt_init_slots(dev1, 2, 0);
+ 		input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
+ 		input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0);
+ 
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index 4790110..1e8e42f 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -1004,7 +1004,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
+ 			input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
+ 					     ETP_WMAX_V2, 0, 0);
+ 		}
+-		input_mt_init_slots(dev, 2);
++		input_mt_init_slots(dev, 2, 0);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+ 		break;
+@@ -1035,7 +1035,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
+ 		input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
+ 				     ETP_WMAX_V2, 0, 0);
+ 		/* Multitouch capable pad, up to 5 fingers. */
+-		input_mt_init_slots(dev, ETP_MAX_FINGERS);
++		input_mt_init_slots(dev, ETP_MAX_FINGERS, 0);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+ 		input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
+diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
+index 3f5649f..b47155d 100644
+--- a/drivers/input/mouse/sentelic.c
++++ b/drivers/input/mouse/sentelic.c
+@@ -960,7 +960,7 @@ static int fsp_set_input_params(struct psmouse *psmouse)
+ 
+ 		input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
+ 		input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
+-		input_mt_init_slots(dev, 2);
++		input_mt_init_slots(dev, 2, 0);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
  	}
-+}
-+
-+static void evdev_pass_values(struct evdev_client *client,
-+			      const struct input_value *vals, size_t count,
-+			      ktime_t mono, ktime_t real)
-+{
-+	struct evdev *evdev = client->evdev;
-+	const struct input_value *v;
-+	struct input_event event;
-+	bool wakeup = false;
-+
-+	event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
-+				      mono : real);
-+
-+	/* Interrupts are disabled, just acquire the lock. */
-+	spin_lock(&client->buffer_lock);
-+
-+	for (v = vals; v != vals + count; v++) {
-+		event.type = v->type;
-+		event.code = v->code;
-+		event.value = v->value;
-+		__pass_event(client, &event);
-+		if (v->type == EV_SYN && v->code == SYN_REPORT)
-+			wakeup = true;
-+	}
- 
- 	spin_unlock(&client->buffer_lock);
-+
-+	if (wakeup)
-+		wake_up_interruptible(&evdev->wait);
+diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
+index 14eaece..37033ad 100644
+--- a/drivers/input/mouse/synaptics.c
++++ b/drivers/input/mouse/synaptics.c
+@@ -1232,7 +1232,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+ 	input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+ 
+ 	if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+-		input_mt_init_slots(dev, 2);
++		input_mt_init_slots(dev, 2, 0);
+ 		set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+ 					ABS_MT_POSITION_Y);
+ 		/* Image sensors can report per-contact pressure */
+@@ -1244,7 +1244,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+ 	} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ 		/* Non-image sensors with AGM use semi-mt */
+ 		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+-		input_mt_init_slots(dev, 2);
++		input_mt_init_slots(dev, 2, 0);
+ 		set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+ 					ABS_MT_POSITION_Y);
+ 	}
+diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
+index 0020419..5837d07 100644
+--- a/drivers/input/tablet/wacom_wac.c
++++ b/drivers/input/tablet/wacom_wac.c
+@@ -1530,7 +1530,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
+ 			__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ 			__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
+ 
+-			input_mt_init_slots(input_dev, features->touch_max);
++			input_mt_init_slots(input_dev, features->touch_max, 0);
+ 
+ 			input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 			                     0, 255, 0, 0);
+@@ -1575,7 +1575,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
+ 
+ 	case TABLETPC2FG:
+ 		if (features->device_type == BTN_TOOL_FINGER) {
+-			input_mt_init_slots(input_dev, features->touch_max);
++			input_mt_init_slots(input_dev, features->touch_max, 0);
+ 			input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+ 					0, MT_TOOL_MAX, 0, 0);
+ 			input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+@@ -1631,7 +1631,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
+ 
+ 			__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ 			__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+-			input_mt_init_slots(input_dev, features->touch_max);
++			input_mt_init_slots(input_dev, features->touch_max, 0);
+ 
+ 			if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+ 				__set_bit(BTN_TOOL_TRIPLETAP,
+diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
+index 4623cc6..e92615d 100644
+--- a/drivers/input/touchscreen/atmel_mxt_ts.c
++++ b/drivers/input/touchscreen/atmel_mxt_ts.c
+@@ -1152,7 +1152,7 @@ static int __devinit mxt_probe(struct i2c_client *client,
+ 
+ 	/* For multi touch */
+ 	num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
+-	error = input_mt_init_slots(input_dev, num_mt_slots);
++	error = input_mt_init_slots(input_dev, num_mt_slots, 0);
+ 	if (error)
+ 		goto err_free_object;
+ 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
+index f030d9e..8e60437 100644
+--- a/drivers/input/touchscreen/cyttsp_core.c
++++ b/drivers/input/touchscreen/cyttsp_core.c
+@@ -571,7 +571,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+ 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 			     0, CY_MAXZ, 0, 0);
+ 
+-	input_mt_init_slots(input_dev, CY_MAX_ID);
++	input_mt_init_slots(input_dev, CY_MAX_ID, 0);
+ 
+ 	error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
+ 				     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
+index 9afc777..7b786e7 100644
+--- a/drivers/input/touchscreen/edt-ft5x06.c
++++ b/drivers/input/touchscreen/edt-ft5x06.c
+@@ -778,7 +778,7 @@ static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client,
+ 			     0, tsdata->num_x * 64 - 1, 0, 0);
+ 	input_set_abs_params(input, ABS_MT_POSITION_Y,
+ 			     0, tsdata->num_y * 64 - 1, 0, 0);
+-	error = input_mt_init_slots(input, MAX_SUPPORT_POINTS);
++	error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
+ 	if (error) {
+ 		dev_err(&client->dev, "Unable to init MT slots.\n");
+ 		goto err_free_mem;
+diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
+index 70524dd..c1e3460 100644
+--- a/drivers/input/touchscreen/egalax_ts.c
++++ b/drivers/input/touchscreen/egalax_ts.c
+@@ -204,7 +204,7 @@ static int __devinit egalax_ts_probe(struct i2c_client *client,
+ 			     ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
+ 	input_set_abs_params(input_dev,
+ 			     ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
+-	input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS);
++	input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
+ 
+ 	input_set_drvdata(input_dev, ts);
+ 
+diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
+index c004417..4ac6976 100644
+--- a/drivers/input/touchscreen/ili210x.c
++++ b/drivers/input/touchscreen/ili210x.c
+@@ -252,7 +252,7 @@ static int __devinit ili210x_i2c_probe(struct i2c_client *client,
+ 	input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
+ 
+ 	/* Multi touch */
+-	input_mt_init_slots(input, MAX_TOUCHES);
++	input_mt_init_slots(input, MAX_TOUCHES, 0);
+ 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
+ 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
+ 
+diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c
+index 49c44bb..560cf09 100644
+--- a/drivers/input/touchscreen/mms114.c
++++ b/drivers/input/touchscreen/mms114.c
+@@ -404,7 +404,7 @@ static int __devinit mms114_probe(struct i2c_client *client,
+ 	input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0);
+ 
+ 	/* For multi touch */
+-	input_mt_init_slots(input_dev, MMS114_MAX_TOUCH);
++	input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0);
+ 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 			     0, MMS114_MAX_AREA, 0, 0);
+ 	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
+index 4ccde45..b49f0b8 100644
+--- a/drivers/input/touchscreen/penmount.c
++++ b/drivers/input/touchscreen/penmount.c
+@@ -264,7 +264,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
+ 	input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
+ 
+ 	if (pm->maxcontacts > 1) {
+-		input_mt_init_slots(pm->dev, pm->maxcontacts);
++		input_mt_init_slots(pm->dev, pm->maxcontacts, 0);
+ 		input_set_abs_params(pm->dev,
+ 				     ABS_MT_POSITION_X, 0, max_x, 0, 0);
+ 		input_set_abs_params(pm->dev,
+diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
+index 8f9ad2f..9a83be6 100644
+--- a/drivers/input/touchscreen/wacom_w8001.c
++++ b/drivers/input/touchscreen/wacom_w8001.c
+@@ -471,7 +471,7 @@ static int w8001_setup(struct w8001 *w8001)
+ 		case 5:
+ 			w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+ 
+-			input_mt_init_slots(dev, 2);
++			input_mt_init_slots(dev, 2, 0);
+ 			input_set_abs_params(dev, ABS_MT_POSITION_X,
+ 						0, touch.x, 0, 0);
+ 			input_set_abs_params(dev, ABS_MT_POSITION_Y,
+diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h
+index 4ae275c..03c52ce 100644
+--- a/include/linux/input/mt.h
++++ b/include/linux/input/mt.h
+@@ -27,11 +27,13 @@ struct input_mt_slot {
+  * struct input_mt - state of tracked contacts
+  * @trkid: stores MT tracking ID for the next contact
+  * @num_slots: number of MT slots the device uses
++ * @flags: input_mt operation flags
+  * @slots: array of slots holding current values of tracked contacts
+  */
+ struct input_mt {
+ 	int trkid;
+ 	int num_slots;
++	unsigned int flags;
+ 	struct input_mt_slot slots[];
+ };
+ 
+@@ -47,7 +49,8 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot,
+ 	return slot->abs[code - ABS_MT_FIRST];
  }
  
- /*
-- * Pass incoming event to all connected clients.
-+ * Pass incoming events to all connected clients.
-  */
--static void evdev_event(struct input_handle *handle,
--			unsigned int type, unsigned int code, int value)
-+static void evdev_events(struct input_handle *handle,
-+			 const struct input_value *vals, size_t count)
- {
- 	struct evdev *evdev = handle->private;
- 	struct evdev_client *client;
--	struct input_event event;
- 	ktime_t time_mono, time_real;
- 
- 	time_mono = ktime_get();
- 	time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
- 
--	event.type = type;
--	event.code = code;
--	event.value = value;
--
- 	rcu_read_lock();
- 
- 	client = rcu_dereference(evdev->grab);
- 
- 	if (client)
--		evdev_pass_event(client, &event, time_mono, time_real);
-+		evdev_pass_values(client, vals, count, time_mono, time_real);
- 	else
- 		list_for_each_entry_rcu(client, &evdev->client_list, node)
--			evdev_pass_event(client, &event, time_mono, time_real);
-+			evdev_pass_values(client, vals, count,
-+					  time_mono, time_real);
- 
- 	rcu_read_unlock();
-+}
- 
--	if (type == EV_SYN && code == SYN_REPORT)
--		wake_up_interruptible(&evdev->wait);
-+/*
-+ * Pass incoming event to all connected clients.
-+ */
-+static void evdev_event(struct input_handle *handle,
-+			unsigned int type, unsigned int code, int value)
-+{
-+	struct input_value vals[] = { { type, code, value } };
-+
-+	evdev_events(handle, vals, 1);
- }
- 
- static int evdev_fasync(int fd, struct file *file, int on)
-@@ -1050,6 +1075,7 @@ MODULE_DEVICE_TABLE(input, evdev_ids);
- 
- static struct input_handler evdev_handler = {
- 	.event		= evdev_event,
-+	.events		= evdev_events,
- 	.connect	= evdev_connect,
- 	.disconnect	= evdev_disconnect,
- 	.fops		= &evdev_fops,
+-int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots);
++int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
++			unsigned int flags);
+ void input_mt_destroy_slots(struct input_dev *dev);
+ 
+ static inline int input_mt_new_trkid(struct input_mt *mt)
 -- 
-1.7.12
+1.7.11.4
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help