2019-06-04 16:11:33 +08:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1999-2002 Vojtech Pavlik
|
|
|
|
*/
|
2012-10-13 17:46:48 +08:00
|
|
|
#ifndef _INPUT_H
|
|
|
|
#define _INPUT_H
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/list.h>
|
2012-10-13 17:46:48 +08:00
|
|
|
#include <uapi/linux/input.h>
|
2010-07-16 14:10:10 +08:00
|
|
|
/* Implementation details, userspace should not care about these */
|
|
|
|
#define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR
|
2012-06-27 15:53:47 +08:00
|
|
|
#define ABS_MT_LAST ABS_MT_TOOL_Y
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In-kernel definitions.
|
|
|
|
*/
|
|
|
|
|
2006-04-26 12:14:19 +08:00
|
|
|
#include <linux/device.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/timer.h>
|
2006-04-26 12:14:19 +08:00
|
|
|
#include <linux/mod_devicetable.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-04-19 08:28:30 +08:00
|
|
|
struct input_dev_poller;
|
|
|
|
|
2012-09-15 21:23:35 +08:00
|
|
|
/**
|
|
|
|
* struct input_value - input value representation
|
|
|
|
* @type: type of value (EV_KEY, EV_ABS, etc)
|
|
|
|
* @code: the value code
|
|
|
|
* @value: the value
|
|
|
|
*/
|
|
|
|
struct input_value {
|
|
|
|
__u16 type;
|
|
|
|
__u16 code;
|
|
|
|
__s32 value;
|
|
|
|
};
|
|
|
|
|
2019-07-25 03:26:31 +08:00
|
|
|
enum input_clock_type {
|
|
|
|
INPUT_CLK_REAL = 0,
|
|
|
|
INPUT_CLK_MONO,
|
|
|
|
INPUT_CLK_BOOT,
|
|
|
|
INPUT_CLK_MAX
|
|
|
|
};
|
|
|
|
|
2007-08-30 12:22:11 +08:00
|
|
|
/**
|
|
|
|
* struct input_dev - represents an input device
|
|
|
|
* @name: name of the device
|
|
|
|
* @phys: physical path to the device in the system hierarchy
|
|
|
|
* @uniq: unique identification code for the device (if device has it)
|
|
|
|
* @id: id of the device (struct input_id)
|
2010-12-19 03:51:13 +08:00
|
|
|
* @propbit: bitmap of device properties and quirks
|
2007-08-30 12:22:11 +08:00
|
|
|
* @evbit: bitmap of types of events supported by the device (EV_KEY,
|
|
|
|
* EV_REL, etc.)
|
|
|
|
* @keybit: bitmap of keys/buttons this device has
|
|
|
|
* @relbit: bitmap of relative axes for the device
|
|
|
|
* @absbit: bitmap of absolute axes for the device
|
|
|
|
* @mscbit: bitmap of miscellaneous events supported by the device
|
|
|
|
* @ledbit: bitmap of leds present on the device
|
|
|
|
* @sndbit: bitmap of sound effects supported by the device
|
|
|
|
* @ffbit: bitmap of force feedback effects supported by the device
|
|
|
|
* @swbit: bitmap of switches present on the device
|
2010-06-11 03:05:24 +08:00
|
|
|
* @hint_events_per_packet: average number of events generated by the
|
|
|
|
* device in a packet (between EV_SYN/SYN_REPORT events). Used by
|
|
|
|
* event handlers to estimate size of the buffer needed to hold
|
|
|
|
* events.
|
2007-08-30 12:22:11 +08:00
|
|
|
* @keycodemax: size of keycode table
|
|
|
|
* @keycodesize: size of elements in keycode table
|
|
|
|
* @keycode: map of scancodes to keycodes for this device
|
2010-09-10 12:54:22 +08:00
|
|
|
* @getkeycode: optional legacy method to retrieve current keymap.
|
2007-08-30 12:22:11 +08:00
|
|
|
* @setkeycode: optional method to alter current keymap, used to implement
|
2009-12-02 13:54:35 +08:00
|
|
|
* sparse keymaps. If not supplied default mechanism will be used.
|
|
|
|
* The method is being called while holding event_lock and thus must
|
|
|
|
* not sleep
|
2007-08-30 12:22:11 +08:00
|
|
|
* @ff: force feedback structure associated with the device if device
|
|
|
|
* supports force feedback effects
|
2017-04-19 08:28:30 +08:00
|
|
|
* @poller: poller structure associated with the device if device is
|
|
|
|
* set up to use polling mode
|
2007-08-30 12:22:11 +08:00
|
|
|
* @repeat_key: stores key code of the last key pressed; used to implement
|
|
|
|
* software autorepeat
|
|
|
|
* @timer: timer for software autorepeat
|
|
|
|
* @rep: current values for autorepeat parameters (delay, rate)
|
2012-09-15 21:15:58 +08:00
|
|
|
* @mt: pointer to multitouch state
|
2010-11-30 15:33:04 +08:00
|
|
|
* @absinfo: array of &struct input_absinfo elements holding information
|
2010-08-03 11:18:21 +08:00
|
|
|
* about absolute axes (current value, min, max, flat, fuzz,
|
|
|
|
* resolution)
|
2007-08-30 12:22:11 +08:00
|
|
|
* @key: reflects current state of device's keys/buttons
|
|
|
|
* @led: reflects current state of device's LEDs
|
|
|
|
* @snd: reflects current state of sound effects
|
|
|
|
* @sw: reflects current state of device's switches
|
|
|
|
* @open: this method is called when the very first user calls
|
|
|
|
* input_open_device(). The driver must prepare the device
|
|
|
|
* to start generating events (start polling thread,
|
Input: Add "inhibited" property
Userspace might want to implement a policy to temporarily disregard input
from certain devices, including not treating them as wakeup sources.
An example use case is a laptop, whose keyboard can be folded under the
screen to create tablet-like experience. The user then must hold the laptop
in such a way that it is difficult to avoid pressing the keyboard keys. It
is therefore desirable to temporarily disregard input from the keyboard,
until it is folded back. This obviously is a policy which should be kept
out of the kernel, but the kernel must provide suitable means to implement
such a policy.
This patch adds a sysfs interface for exactly this purpose.
To implement the said interface it adds an "inhibited" property to struct
input_dev, and effectively creates four states a device can be in: closed
uninhibited, closed inhibited, open uninhibited, open inhibited. It also
defers calling driver's ->open() and ->close() to until they are actually
needed, e.g. it makes no sense to prepare the underlying device for
generating events (->open()) if the device is inhibited.
uninhibit
closed <------------ closed
uninhibited ------------> inhibited
| ^ inhibit | ^
1st | | 1st | |
open | | open | |
| | | |
| | last | | last
| | close | | close
v | uninhibit v |
open <------------ open
uninhibited ------------> inhibited
The top inhibit/uninhibit transition happens when users == 0.
The bottom inhibit/uninhibit transition happens when users > 0.
The left open/close transition happens when !inhibited.
The right open/close transition happens when inhibited.
Due to all transitions being serialized with dev->mutex, it is impossible
to have "diagonal" transitions between closed uninhibited and open
inhibited or between open uninhibited and closed inhibited.
No new callbacks are added to drivers, because their open() and close()
serve exactly the purpose to tell the driver to start/stop providing
events to the input core. Consequently, open() and close() - if provided
- are called in both inhibit and uninhibit paths.
Signed-off-by: Patrik Fimml <patrikf@chromium.org>
Co-developed-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Link: https://lore.kernel.org/r/20200608112211.12125-8-andrzej.p@collabora.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2020-12-03 06:42:04 +08:00
|
|
|
* request an IRQ, submit URB, etc.). The meaning of open() is
|
|
|
|
* to start providing events to the input core.
|
2007-08-30 12:22:11 +08:00
|
|
|
* @close: this method is called when the very last user calls
|
Input: Add "inhibited" property
Userspace might want to implement a policy to temporarily disregard input
from certain devices, including not treating them as wakeup sources.
An example use case is a laptop, whose keyboard can be folded under the
screen to create tablet-like experience. The user then must hold the laptop
in such a way that it is difficult to avoid pressing the keyboard keys. It
is therefore desirable to temporarily disregard input from the keyboard,
until it is folded back. This obviously is a policy which should be kept
out of the kernel, but the kernel must provide suitable means to implement
such a policy.
This patch adds a sysfs interface for exactly this purpose.
To implement the said interface it adds an "inhibited" property to struct
input_dev, and effectively creates four states a device can be in: closed
uninhibited, closed inhibited, open uninhibited, open inhibited. It also
defers calling driver's ->open() and ->close() to until they are actually
needed, e.g. it makes no sense to prepare the underlying device for
generating events (->open()) if the device is inhibited.
uninhibit
closed <------------ closed
uninhibited ------------> inhibited
| ^ inhibit | ^
1st | | 1st | |
open | | open | |
| | | |
| | last | | last
| | close | | close
v | uninhibit v |
open <------------ open
uninhibited ------------> inhibited
The top inhibit/uninhibit transition happens when users == 0.
The bottom inhibit/uninhibit transition happens when users > 0.
The left open/close transition happens when !inhibited.
The right open/close transition happens when inhibited.
Due to all transitions being serialized with dev->mutex, it is impossible
to have "diagonal" transitions between closed uninhibited and open
inhibited or between open uninhibited and closed inhibited.
No new callbacks are added to drivers, because their open() and close()
serve exactly the purpose to tell the driver to start/stop providing
events to the input core. Consequently, open() and close() - if provided
- are called in both inhibit and uninhibit paths.
Signed-off-by: Patrik Fimml <patrikf@chromium.org>
Co-developed-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Link: https://lore.kernel.org/r/20200608112211.12125-8-andrzej.p@collabora.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2020-12-03 06:42:04 +08:00
|
|
|
* input_close_device(). The meaning of close() is to stop
|
|
|
|
* providing events to the input core.
|
2007-08-30 12:22:11 +08:00
|
|
|
* @flush: purges the device. Most commonly used to get rid of force
|
|
|
|
* feedback effects loaded into the device when disconnecting
|
|
|
|
* from it
|
|
|
|
* @event: event handler for events sent _to_ the device, like EV_LED
|
|
|
|
* or EV_SND. The device is expected to carry out the requested
|
|
|
|
* action (turn on a LED, play sound, etc.) The call is protected
|
|
|
|
* by @event_lock and must not sleep
|
|
|
|
* @grab: input handle that currently has the device grabbed (via
|
|
|
|
* EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
|
|
|
|
* recipient for all input events coming from the device
|
2016-07-14 02:11:36 +08:00
|
|
|
* @event_lock: this spinlock is taken when input core receives
|
2007-08-30 12:22:11 +08:00
|
|
|
* and processes a new event for the device (in input_event()).
|
|
|
|
* Code that accesses and/or modifies parameters of a device
|
|
|
|
* (such as keymap or absmin, absmax, absfuzz, etc.) after device
|
|
|
|
* has been registered with input core must take this lock.
|
|
|
|
* @mutex: serializes calls to open(), close() and flush() methods
|
|
|
|
* @users: stores number of users (input handlers) that opened this
|
|
|
|
* device. It is used by input_open_device() and input_close_device()
|
|
|
|
* to make sure that dev->open() is only called when the first
|
|
|
|
* user opens device and dev->close() is called when the very
|
|
|
|
* last user closes the device
|
|
|
|
* @going_away: marks devices that are in a middle of unregistering and
|
|
|
|
* causes input_open_device*() fail with -ENODEV.
|
|
|
|
* @dev: driver model's view of this device
|
|
|
|
* @h_list: list of input handles associated with the device. When
|
|
|
|
* accessing the list dev->mutex must be held
|
|
|
|
* @node: used to place the device onto input_dev_list
|
2012-11-10 16:32:36 +08:00
|
|
|
* @num_vals: number of values queued in the current frame
|
|
|
|
* @max_vals: maximum number of values queued in a frame
|
|
|
|
* @vals: array of values queued in the current frame
|
2012-11-04 03:16:12 +08:00
|
|
|
* @devres_managed: indicates that devices is managed with devres framework
|
|
|
|
* and needs not be explicitly unregistered or freed.
|
2019-07-25 03:26:31 +08:00
|
|
|
* @timestamp: storage for a timestamp set by input_set_timestamp called
|
|
|
|
* by a driver
|
Input: Add "inhibited" property
Userspace might want to implement a policy to temporarily disregard input
from certain devices, including not treating them as wakeup sources.
An example use case is a laptop, whose keyboard can be folded under the
screen to create tablet-like experience. The user then must hold the laptop
in such a way that it is difficult to avoid pressing the keyboard keys. It
is therefore desirable to temporarily disregard input from the keyboard,
until it is folded back. This obviously is a policy which should be kept
out of the kernel, but the kernel must provide suitable means to implement
such a policy.
This patch adds a sysfs interface for exactly this purpose.
To implement the said interface it adds an "inhibited" property to struct
input_dev, and effectively creates four states a device can be in: closed
uninhibited, closed inhibited, open uninhibited, open inhibited. It also
defers calling driver's ->open() and ->close() to until they are actually
needed, e.g. it makes no sense to prepare the underlying device for
generating events (->open()) if the device is inhibited.
uninhibit
closed <------------ closed
uninhibited ------------> inhibited
| ^ inhibit | ^
1st | | 1st | |
open | | open | |
| | | |
| | last | | last
| | close | | close
v | uninhibit v |
open <------------ open
uninhibited ------------> inhibited
The top inhibit/uninhibit transition happens when users == 0.
The bottom inhibit/uninhibit transition happens when users > 0.
The left open/close transition happens when !inhibited.
The right open/close transition happens when inhibited.
Due to all transitions being serialized with dev->mutex, it is impossible
to have "diagonal" transitions between closed uninhibited and open
inhibited or between open uninhibited and closed inhibited.
No new callbacks are added to drivers, because their open() and close()
serve exactly the purpose to tell the driver to start/stop providing
events to the input core. Consequently, open() and close() - if provided
- are called in both inhibit and uninhibit paths.
Signed-off-by: Patrik Fimml <patrikf@chromium.org>
Co-developed-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Link: https://lore.kernel.org/r/20200608112211.12125-8-andrzej.p@collabora.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2020-12-03 06:42:04 +08:00
|
|
|
* @inhibited: indicates that the input device is inhibited. If that is
|
|
|
|
* the case then input core ignores any events generated by the device.
|
|
|
|
* Device's close() is called when it is being inhibited and its open()
|
|
|
|
* is called when it is being uninhibited.
|
2007-08-30 12:22:11 +08:00
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
struct input_dev {
|
2005-06-30 13:50:38 +08:00
|
|
|
const char *name;
|
|
|
|
const char *phys;
|
|
|
|
const char *uniq;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct input_id id;
|
|
|
|
|
2010-12-19 03:51:13 +08:00
|
|
|
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
|
|
|
|
|
2007-10-19 14:40:32 +08:00
|
|
|
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
|
|
|
|
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
|
|
|
|
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
|
|
|
|
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
|
|
|
|
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
|
|
|
|
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
|
|
|
|
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
|
|
|
|
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
|
|
|
|
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-11 03:05:24 +08:00
|
|
|
unsigned int hint_events_per_packet;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int keycodemax;
|
|
|
|
unsigned int keycodesize;
|
|
|
|
void *keycode;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-03-09 14:37:10 +08:00
|
|
|
int (*setkeycode)(struct input_dev *dev,
|
2011-02-01 13:06:39 +08:00
|
|
|
const struct input_keymap_entry *ke,
|
|
|
|
unsigned int *old_keycode);
|
2010-03-09 14:37:10 +08:00
|
|
|
int (*getkeycode)(struct input_dev *dev,
|
2011-02-01 13:06:39 +08:00
|
|
|
struct input_keymap_entry *ke);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-07-19 13:40:22 +08:00
|
|
|
struct ff_device *ff;
|
|
|
|
|
2017-04-19 08:28:30 +08:00
|
|
|
struct input_dev_poller *poller;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int repeat_key;
|
|
|
|
struct timer_list timer;
|
|
|
|
|
2010-08-03 11:18:21 +08:00
|
|
|
int rep[REP_CNT];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-09-15 21:15:58 +08:00
|
|
|
struct input_mt *mt;
|
2010-07-16 14:10:10 +08:00
|
|
|
|
2010-08-03 11:18:21 +08:00
|
|
|
struct input_absinfo *absinfo;
|
|
|
|
|
2007-10-19 14:40:32 +08:00
|
|
|
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
|
|
|
|
unsigned long led[BITS_TO_LONGS(LED_CNT)];
|
|
|
|
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
|
|
|
|
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int (*open)(struct input_dev *dev);
|
|
|
|
void (*close)(struct input_dev *dev);
|
|
|
|
int (*flush)(struct input_dev *dev, struct file *file);
|
|
|
|
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
|
|
|
|
|
2010-03-04 22:50:28 +08:00
|
|
|
struct input_handle __rcu *grab;
|
2005-05-29 15:29:25 +08:00
|
|
|
|
2007-08-30 12:22:11 +08:00
|
|
|
spinlock_t event_lock;
|
|
|
|
struct mutex mutex;
|
|
|
|
|
2005-05-29 15:29:25 +08:00
|
|
|
unsigned int users;
|
2009-09-16 16:06:43 +08:00
|
|
|
bool going_away;
|
2005-05-29 15:29:25 +08:00
|
|
|
|
2007-06-15 11:32:24 +08:00
|
|
|
struct device dev;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
struct list_head h_list;
|
|
|
|
struct list_head node;
|
2012-09-15 21:23:35 +08:00
|
|
|
|
|
|
|
unsigned int num_vals;
|
|
|
|
unsigned int max_vals;
|
|
|
|
struct input_value *vals;
|
2012-11-04 03:16:12 +08:00
|
|
|
|
|
|
|
bool devres_managed;
|
2019-07-25 03:26:31 +08:00
|
|
|
|
|
|
|
ktime_t timestamp[INPUT_CLK_MAX];
|
Input: Add "inhibited" property
Userspace might want to implement a policy to temporarily disregard input
from certain devices, including not treating them as wakeup sources.
An example use case is a laptop, whose keyboard can be folded under the
screen to create tablet-like experience. The user then must hold the laptop
in such a way that it is difficult to avoid pressing the keyboard keys. It
is therefore desirable to temporarily disregard input from the keyboard,
until it is folded back. This obviously is a policy which should be kept
out of the kernel, but the kernel must provide suitable means to implement
such a policy.
This patch adds a sysfs interface for exactly this purpose.
To implement the said interface it adds an "inhibited" property to struct
input_dev, and effectively creates four states a device can be in: closed
uninhibited, closed inhibited, open uninhibited, open inhibited. It also
defers calling driver's ->open() and ->close() to until they are actually
needed, e.g. it makes no sense to prepare the underlying device for
generating events (->open()) if the device is inhibited.
uninhibit
closed <------------ closed
uninhibited ------------> inhibited
| ^ inhibit | ^
1st | | 1st | |
open | | open | |
| | | |
| | last | | last
| | close | | close
v | uninhibit v |
open <------------ open
uninhibited ------------> inhibited
The top inhibit/uninhibit transition happens when users == 0.
The bottom inhibit/uninhibit transition happens when users > 0.
The left open/close transition happens when !inhibited.
The right open/close transition happens when inhibited.
Due to all transitions being serialized with dev->mutex, it is impossible
to have "diagonal" transitions between closed uninhibited and open
inhibited or between open uninhibited and closed inhibited.
No new callbacks are added to drivers, because their open() and close()
serve exactly the purpose to tell the driver to start/stop providing
events to the input core. Consequently, open() and close() - if provided
- are called in both inhibit and uninhibit paths.
Signed-off-by: Patrik Fimml <patrikf@chromium.org>
Co-developed-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Link: https://lore.kernel.org/r/20200608112211.12125-8-andrzej.p@collabora.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2020-12-03 06:42:04 +08:00
|
|
|
|
|
|
|
bool inhibited;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
2007-06-15 11:32:24 +08:00
|
|
|
#define to_input_dev(d) container_of(d, struct input_dev, dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-04-26 12:14:19 +08:00
|
|
|
/*
|
|
|
|
* Verify that we are in sync with input_device_id mod_devicetable.h #defines
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if EV_MAX != INPUT_DEVICE_ID_EV_MAX
|
|
|
|
#error "EV_MAX and INPUT_DEVICE_ID_EV_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
2007-03-10 02:59:06 +08:00
|
|
|
#if KEY_MIN_INTERESTING != INPUT_DEVICE_ID_KEY_MIN_INTERESTING
|
|
|
|
#error "KEY_MIN_INTERESTING and INPUT_DEVICE_ID_KEY_MIN_INTERESTING do not match"
|
|
|
|
#endif
|
|
|
|
|
2006-04-26 12:14:19 +08:00
|
|
|
#if KEY_MAX != INPUT_DEVICE_ID_KEY_MAX
|
|
|
|
#error "KEY_MAX and INPUT_DEVICE_ID_KEY_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if REL_MAX != INPUT_DEVICE_ID_REL_MAX
|
|
|
|
#error "REL_MAX and INPUT_DEVICE_ID_REL_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ABS_MAX != INPUT_DEVICE_ID_ABS_MAX
|
|
|
|
#error "ABS_MAX and INPUT_DEVICE_ID_ABS_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if MSC_MAX != INPUT_DEVICE_ID_MSC_MAX
|
|
|
|
#error "MSC_MAX and INPUT_DEVICE_ID_MSC_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if LED_MAX != INPUT_DEVICE_ID_LED_MAX
|
|
|
|
#error "LED_MAX and INPUT_DEVICE_ID_LED_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if SND_MAX != INPUT_DEVICE_ID_SND_MAX
|
|
|
|
#error "SND_MAX and INPUT_DEVICE_ID_SND_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if FF_MAX != INPUT_DEVICE_ID_FF_MAX
|
|
|
|
#error "FF_MAX and INPUT_DEVICE_ID_FF_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if SW_MAX != INPUT_DEVICE_ID_SW_MAX
|
|
|
|
#error "SW_MAX and INPUT_DEVICE_ID_SW_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
2017-10-10 03:01:14 +08:00
|
|
|
#if INPUT_PROP_MAX != INPUT_DEVICE_ID_PROP_MAX
|
|
|
|
#error "INPUT_PROP_MAX and INPUT_DEVICE_ID_PROP_MAX do not match"
|
|
|
|
#endif
|
|
|
|
|
2006-04-26 12:14:19 +08:00
|
|
|
#define INPUT_DEVICE_ID_MATCH_DEVICE \
|
2005-04-17 06:20:36 +08:00
|
|
|
(INPUT_DEVICE_ID_MATCH_BUS | INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT)
|
2006-04-26 12:14:19 +08:00
|
|
|
#define INPUT_DEVICE_ID_MATCH_DEVICE_AND_VERSION \
|
2005-04-17 06:20:36 +08:00
|
|
|
(INPUT_DEVICE_ID_MATCH_DEVICE | INPUT_DEVICE_ID_MATCH_VERSION)
|
|
|
|
|
|
|
|
struct input_handle;
|
|
|
|
|
2006-07-06 12:21:03 +08:00
|
|
|
/**
|
|
|
|
* struct input_handler - implements one of interfaces for input devices
|
|
|
|
* @private: driver-specific data
|
2007-08-30 12:22:11 +08:00
|
|
|
* @event: event handler. This method is being called by input core with
|
|
|
|
* interrupts disabled and dev->event_lock spinlock held and so
|
|
|
|
* it may not sleep
|
2012-09-15 21:23:35 +08:00
|
|
|
* @events: event sequence handler. This method is being called by
|
|
|
|
* input core with interrupts disabled and dev->event_lock
|
|
|
|
* spinlock held and so it may not sleep
|
2010-01-30 15:59:12 +08:00
|
|
|
* @filter: similar to @event; separates normal event handlers from
|
|
|
|
* "filters".
|
2010-02-03 13:08:26 +08:00
|
|
|
* @match: called after comparing device's id with handler's id_table
|
|
|
|
* to perform fine-grained matching between device and handler
|
2006-07-06 12:21:03 +08:00
|
|
|
* @connect: called when attaching a handler to an input device
|
|
|
|
* @disconnect: disconnects a handler from input device
|
|
|
|
* @start: starts handler for given handle. This function is called by
|
|
|
|
* input core right after connect() method and also when a process
|
|
|
|
* that "grabbed" a device releases it
|
2012-10-09 00:07:24 +08:00
|
|
|
* @legacy_minors: set to %true by drivers using legacy minor ranges
|
|
|
|
* @minor: beginning of range of 32 legacy minors for devices this driver
|
2006-07-06 12:21:03 +08:00
|
|
|
* can provide
|
|
|
|
* @name: name of the handler, to be shown in /proc/bus/input/handlers
|
|
|
|
* @id_table: pointer to a table of input_device_ids this driver can
|
|
|
|
* handle
|
|
|
|
* @h_list: list of input handles associated with the handler
|
|
|
|
* @node: for placing the driver onto input_handler_list
|
2007-08-30 12:22:11 +08:00
|
|
|
*
|
|
|
|
* Input handlers attach to input devices and create input handles. There
|
|
|
|
* are likely several handlers attached to any given input device at the
|
|
|
|
* same time. All of them will get their copy of input event generated by
|
|
|
|
* the device.
|
|
|
|
*
|
2010-01-30 15:59:12 +08:00
|
|
|
* The very same structure is used to implement input filters. Input core
|
|
|
|
* allows filters to run first and will not pass event to regular handlers
|
|
|
|
* if any of the filters indicate that the event should be filtered (by
|
|
|
|
* returning %true from their filter() method).
|
|
|
|
*
|
2007-08-30 12:22:11 +08:00
|
|
|
* Note that input core serializes calls to connect() and disconnect()
|
|
|
|
* methods.
|
2006-07-06 12:21:03 +08:00
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
struct input_handler {
|
|
|
|
|
|
|
|
void *private;
|
|
|
|
|
|
|
|
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
2012-09-15 21:23:35 +08:00
|
|
|
void (*events)(struct input_handle *handle,
|
|
|
|
const struct input_value *vals, unsigned int count);
|
2010-01-30 15:59:12 +08:00
|
|
|
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
2010-02-03 13:08:26 +08:00
|
|
|
bool (*match)(struct input_handler *handler, struct input_dev *dev);
|
2007-04-12 13:29:46 +08:00
|
|
|
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
|
2005-04-17 06:20:36 +08:00
|
|
|
void (*disconnect)(struct input_handle *handle);
|
2006-07-06 12:21:03 +08:00
|
|
|
void (*start)(struct input_handle *handle);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-10-09 00:07:24 +08:00
|
|
|
bool legacy_minors;
|
2005-04-17 06:20:36 +08:00
|
|
|
int minor;
|
2006-09-14 13:31:59 +08:00
|
|
|
const char *name;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-14 13:31:59 +08:00
|
|
|
const struct input_device_id *id_table;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
struct list_head h_list;
|
|
|
|
struct list_head node;
|
|
|
|
};
|
|
|
|
|
2007-08-30 12:22:11 +08:00
|
|
|
/**
|
|
|
|
* struct input_handle - links input device with an input handler
|
|
|
|
* @private: handler-specific data
|
|
|
|
* @open: counter showing whether the handle is 'open', i.e. should deliver
|
|
|
|
* events from its device
|
|
|
|
* @name: name given to the handle by handler that created it
|
|
|
|
* @dev: input device the handle is attached to
|
|
|
|
* @handler: handler that works with the device through this handle
|
|
|
|
* @d_node: used to put the handle on device's list of attached handles
|
|
|
|
* @h_node: used to put the handle on handler's list of handles from which
|
|
|
|
* it gets events
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
struct input_handle {
|
|
|
|
|
|
|
|
void *private;
|
|
|
|
|
|
|
|
int open;
|
2006-09-14 13:31:59 +08:00
|
|
|
const char *name;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
struct input_dev *dev;
|
|
|
|
struct input_handler *handler;
|
|
|
|
|
|
|
|
struct list_head d_node;
|
|
|
|
struct list_head h_node;
|
|
|
|
};
|
|
|
|
|
2012-11-04 03:16:12 +08:00
|
|
|
struct input_dev __must_check *input_allocate_device(void);
|
|
|
|
struct input_dev __must_check *devm_input_allocate_device(struct device *);
|
2006-06-26 13:48:36 +08:00
|
|
|
void input_free_device(struct input_dev *dev);
|
2005-09-15 15:01:39 +08:00
|
|
|
|
|
|
|
static inline struct input_dev *input_get_device(struct input_dev *dev)
|
|
|
|
{
|
2008-04-01 12:22:53 +08:00
|
|
|
return dev ? to_input_dev(get_device(&dev->dev)) : NULL;
|
2005-09-15 15:01:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void input_put_device(struct input_dev *dev)
|
|
|
|
{
|
2008-04-01 12:22:53 +08:00
|
|
|
if (dev)
|
|
|
|
put_device(&dev->dev);
|
2005-09-15 15:01:39 +08:00
|
|
|
}
|
|
|
|
|
2007-04-12 13:33:51 +08:00
|
|
|
static inline void *input_get_drvdata(struct input_dev *dev)
|
|
|
|
{
|
2008-04-02 12:41:00 +08:00
|
|
|
return dev_get_drvdata(&dev->dev);
|
2007-04-12 13:33:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void input_set_drvdata(struct input_dev *dev, void *data)
|
|
|
|
{
|
2008-04-02 12:41:00 +08:00
|
|
|
dev_set_drvdata(&dev->dev, data);
|
2007-04-12 13:33:51 +08:00
|
|
|
}
|
|
|
|
|
2007-07-18 13:20:58 +08:00
|
|
|
int __must_check input_register_device(struct input_dev *);
|
2005-04-17 06:20:36 +08:00
|
|
|
void input_unregister_device(struct input_dev *);
|
|
|
|
|
2010-11-04 02:02:31 +08:00
|
|
|
void input_reset_device(struct input_dev *);
|
|
|
|
|
2017-04-19 08:28:30 +08:00
|
|
|
int input_setup_polling(struct input_dev *dev,
|
|
|
|
void (*poll_fn)(struct input_dev *dev));
|
|
|
|
void input_set_poll_interval(struct input_dev *dev, unsigned int interval);
|
|
|
|
void input_set_min_poll_interval(struct input_dev *dev, unsigned int interval);
|
|
|
|
void input_set_max_poll_interval(struct input_dev *dev, unsigned int interval);
|
2019-10-03 01:20:19 +08:00
|
|
|
int input_get_poll_interval(struct input_dev *dev);
|
2017-04-19 08:28:30 +08:00
|
|
|
|
2007-07-18 13:20:58 +08:00
|
|
|
int __must_check input_register_handler(struct input_handler *);
|
2005-04-17 06:20:36 +08:00
|
|
|
void input_unregister_handler(struct input_handler *);
|
|
|
|
|
2012-10-09 00:07:24 +08:00
|
|
|
int __must_check input_get_new_minor(int legacy_base, unsigned int legacy_num,
|
|
|
|
bool allow_dynamic);
|
|
|
|
void input_free_minor(unsigned int minor);
|
|
|
|
|
2009-12-02 13:54:35 +08:00
|
|
|
int input_handler_for_each_handle(struct input_handler *, void *data,
|
|
|
|
int (*fn)(struct input_handle *, void *));
|
|
|
|
|
2007-04-12 13:29:46 +08:00
|
|
|
int input_register_handle(struct input_handle *);
|
|
|
|
void input_unregister_handle(struct input_handle *);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int input_grab_device(struct input_handle *);
|
|
|
|
void input_release_device(struct input_handle *);
|
|
|
|
|
|
|
|
int input_open_device(struct input_handle *);
|
|
|
|
void input_close_device(struct input_handle *);
|
|
|
|
|
2010-11-04 02:02:31 +08:00
|
|
|
int input_flush_device(struct input_handle *handle, struct file *file);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-07-25 03:26:31 +08:00
|
|
|
void input_set_timestamp(struct input_dev *dev, ktime_t timestamp);
|
|
|
|
ktime_t *input_get_timestamp(struct input_dev *dev);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
|
2006-07-06 12:22:43 +08:00
|
|
|
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
|
|
|
|
{
|
|
|
|
input_event(dev, EV_KEY, code, !!value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
|
|
|
|
{
|
|
|
|
input_event(dev, EV_REL, code, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
|
|
|
|
{
|
|
|
|
input_event(dev, EV_ABS, code, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
|
|
|
|
{
|
|
|
|
input_event(dev, EV_FF_STATUS, code, value);
|
|
|
|
}
|
|
|
|
|
2005-09-07 06:19:06 +08:00
|
|
|
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
|
|
|
|
{
|
|
|
|
input_event(dev, EV_SW, code, !!value);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline void input_sync(struct input_dev *dev)
|
|
|
|
{
|
|
|
|
input_event(dev, EV_SYN, SYN_REPORT, 0);
|
|
|
|
}
|
|
|
|
|
Input: add detailed multi-touch finger data report protocol
In order to utilize the full power of the new multi-touch devices, a
way to report detailed finger data to user space is needed. This patch
adds a multi-touch (MT) protocol which allows drivers to report details
for an arbitrary number of fingers.
The driver sends a SYN_MT_REPORT event via the input_mt_sync() function
when a complete finger has been reported.
In order to stay compatible with existing applications, the data
reported in a finger packet must not be recognized as single-touch
events. In addition, all finger data must bypass input filtering,
since subsequent events of the same type refer to different fingers.
A set of ABS_MT events with the desired properties are defined. The
events are divided into categories, to allow for partial implementation.
The minimum set consists of ABS_MT_TOUCH_MAJOR, ABS_MT_POSITION_X and
ABS_MT_POSITION_Y, which allows for multiple fingers to be tracked.
If the device supports it, the ABS_MT_WIDTH_MAJOR may be used to provide
the size of the approaching finger. Anisotropy and direction may be
specified with ABS_MT_TOUCH_MINOR, ABS_MT_WIDTH_MINOR and
ABS_MT_ORIENTATION. Devices with more granular information may specify
general shapes as blobs, i.e., as a sequence of rectangular shapes
grouped together by a ABS_MT_BLOB_ID. Finally, the ABS_MT_TOOL_TYPE
may be used to specify whether the touching tool is a finger or a pen.
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
2009-04-28 22:47:33 +08:00
|
|
|
static inline void input_mt_sync(struct input_dev *dev)
|
|
|
|
{
|
|
|
|
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
|
|
|
|
}
|
|
|
|
|
2007-04-25 12:53:18 +08:00
|
|
|
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);
|
|
|
|
|
2010-06-11 03:05:24 +08:00
|
|
|
/**
|
|
|
|
* input_set_events_per_packet - tell handlers about the driver event rate
|
|
|
|
* @dev: the input device used by the driver
|
|
|
|
* @n_events: the average number of events between calls to input_sync()
|
|
|
|
*
|
|
|
|
* If the event rate sent from a device is unusually large, use this
|
|
|
|
* function to set the expected event rate. This will allow handlers
|
|
|
|
* to set up an appropriate buffer size for the event stream, in order
|
|
|
|
* to minimize information loss.
|
|
|
|
*/
|
|
|
|
static inline void input_set_events_per_packet(struct input_dev *dev, int n_events)
|
|
|
|
{
|
|
|
|
dev->hint_events_per_packet = n_events;
|
|
|
|
}
|
|
|
|
|
2010-08-03 11:18:21 +08:00
|
|
|
void input_alloc_absinfo(struct input_dev *dev);
|
|
|
|
void input_set_abs_params(struct input_dev *dev, unsigned int axis,
|
|
|
|
int min, int max, int fuzz, int flat);
|
2022-03-01 14:58:28 +08:00
|
|
|
void input_copy_abs(struct input_dev *dst, unsigned int dst_axis,
|
|
|
|
const struct input_dev *src, unsigned int src_axis);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 10:33:51 +08:00
|
|
|
#define INPUT_GENERATE_ABS_ACCESSORS(_suffix, _item) \
|
|
|
|
static inline int input_abs_get_##_suffix(struct input_dev *dev, \
|
|
|
|
unsigned int axis) \
|
|
|
|
{ \
|
2010-08-03 11:18:21 +08:00
|
|
|
return dev->absinfo ? dev->absinfo[axis]._item : 0; \
|
2010-08-03 10:33:51 +08:00
|
|
|
} \
|
|
|
|
\
|
|
|
|
static inline void input_abs_set_##_suffix(struct input_dev *dev, \
|
|
|
|
unsigned int axis, int val) \
|
|
|
|
{ \
|
2010-08-03 11:18:21 +08:00
|
|
|
input_alloc_absinfo(dev); \
|
|
|
|
if (dev->absinfo) \
|
|
|
|
dev->absinfo[axis]._item = val; \
|
2010-08-03 10:33:51 +08:00
|
|
|
}
|
|
|
|
|
2010-08-03 11:18:21 +08:00
|
|
|
INPUT_GENERATE_ABS_ACCESSORS(val, value)
|
|
|
|
INPUT_GENERATE_ABS_ACCESSORS(min, minimum)
|
|
|
|
INPUT_GENERATE_ABS_ACCESSORS(max, maximum)
|
2010-08-03 10:33:51 +08:00
|
|
|
INPUT_GENERATE_ABS_ACCESSORS(fuzz, fuzz)
|
|
|
|
INPUT_GENERATE_ABS_ACCESSORS(flat, flat)
|
2010-08-03 11:18:21 +08:00
|
|
|
INPUT_GENERATE_ABS_ACCESSORS(res, resolution)
|
2010-08-03 10:33:51 +08:00
|
|
|
|
2010-09-10 12:54:22 +08:00
|
|
|
int input_scancode_to_scalar(const struct input_keymap_entry *ke,
|
|
|
|
unsigned int *scancode);
|
|
|
|
|
|
|
|
int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke);
|
2010-03-09 14:37:10 +08:00
|
|
|
int input_set_keycode(struct input_dev *dev,
|
2010-09-10 12:54:22 +08:00
|
|
|
const struct input_keymap_entry *ke);
|
2007-11-04 12:41:12 +08:00
|
|
|
|
2017-10-10 02:09:33 +08:00
|
|
|
bool input_match_device_id(const struct input_dev *dev,
|
|
|
|
const struct input_device_id *id);
|
|
|
|
|
2015-10-14 14:13:55 +08:00
|
|
|
void input_enable_softrepeat(struct input_dev *dev, int delay, int period);
|
|
|
|
|
2020-10-05 12:15:46 +08:00
|
|
|
bool input_device_enabled(struct input_dev *dev);
|
|
|
|
|
2005-10-28 13:25:43 +08:00
|
|
|
extern struct class input_class;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-07-19 13:40:22 +08:00
|
|
|
/**
|
|
|
|
* struct ff_device - force-feedback part of an input device
|
|
|
|
* @upload: Called to upload an new effect into device
|
|
|
|
* @erase: Called to erase an effect from device
|
|
|
|
* @playback: Called to request device to start playing specified effect
|
|
|
|
* @set_gain: Called to set specified gain
|
|
|
|
* @set_autocenter: Called to auto-center device
|
|
|
|
* @destroy: called by input core when parent input device is being
|
|
|
|
* destroyed
|
|
|
|
* @private: driver-specific data, will be freed automatically
|
|
|
|
* @ffbit: bitmap of force feedback capabilities truly supported by
|
|
|
|
* device (not emulated like ones in input_dev->ffbit)
|
|
|
|
* @mutex: mutex for serializing access to the device
|
|
|
|
* @max_effects: maximum number of effects supported by device
|
|
|
|
* @effects: pointer to an array of effects currently loaded into device
|
|
|
|
* @effect_owners: array of effect owners; when file handle owning
|
2007-08-30 12:22:11 +08:00
|
|
|
* an effect gets closed the effect is automatically erased
|
2006-07-19 13:40:22 +08:00
|
|
|
*
|
|
|
|
* Every force-feedback device must implement upload() and playback()
|
|
|
|
* methods; erase() is optional. set_gain() and set_autocenter() need
|
|
|
|
* only be implemented if driver sets up FF_GAIN and FF_AUTOCENTER
|
|
|
|
* bits.
|
2009-11-07 13:39:07 +08:00
|
|
|
*
|
|
|
|
* Note that playback(), set_gain() and set_autocenter() are called with
|
|
|
|
* dev->event_lock spinlock held and interrupts off and thus may not
|
|
|
|
* sleep.
|
2006-07-19 13:40:22 +08:00
|
|
|
*/
|
|
|
|
struct ff_device {
|
|
|
|
int (*upload)(struct input_dev *dev, struct ff_effect *effect,
|
|
|
|
struct ff_effect *old);
|
|
|
|
int (*erase)(struct input_dev *dev, int effect_id);
|
|
|
|
|
|
|
|
int (*playback)(struct input_dev *dev, int effect_id, int value);
|
|
|
|
void (*set_gain)(struct input_dev *dev, u16 gain);
|
|
|
|
void (*set_autocenter)(struct input_dev *dev, u16 magnitude);
|
|
|
|
|
|
|
|
void (*destroy)(struct ff_device *);
|
|
|
|
|
|
|
|
void *private;
|
|
|
|
|
2007-10-19 14:40:32 +08:00
|
|
|
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
|
2006-07-19 13:40:22 +08:00
|
|
|
|
|
|
|
struct mutex mutex;
|
|
|
|
|
|
|
|
int max_effects;
|
|
|
|
struct ff_effect *effects;
|
|
|
|
struct file *effect_owners[];
|
|
|
|
};
|
|
|
|
|
2011-10-13 12:05:53 +08:00
|
|
|
int input_ff_create(struct input_dev *dev, unsigned int max_effects);
|
2006-07-19 13:40:22 +08:00
|
|
|
void input_ff_destroy(struct input_dev *dev);
|
|
|
|
|
|
|
|
int input_ff_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
|
|
|
|
|
|
|
|
int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, struct file *file);
|
|
|
|
int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
|
2017-09-02 08:13:43 +08:00
|
|
|
int input_ff_flush(struct input_dev *dev, struct file *file);
|
2006-07-19 13:40:22 +08:00
|
|
|
|
2006-07-19 13:40:30 +08:00
|
|
|
int input_ff_create_memless(struct input_dev *dev, void *data,
|
|
|
|
int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|