Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
6addb1d6
K
Kernel
项目概览
openeuler
/
Kernel
接近 2 年 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
6addb1d6
编写于
8月 30, 2007
作者:
D
Dmitry Torokhov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Input: evdev - implement proper locking
Signed-off-by:
N
Dmitry Torokhov
<
dtor@mail.ru
>
上级
8006479c
变更
1
显示空白变更内容
内联
并排
Showing
1 changed file
with
473 addition
and
240 deletion
+473
-240
drivers/input/evdev.c
drivers/input/evdev.c
+473
-240
未找到文件。
drivers/input/evdev.c
浏览文件 @
6addb1d6
...
@@ -30,6 +30,8 @@ struct evdev {
...
@@ -30,6 +30,8 @@ struct evdev {
wait_queue_head_t
wait
;
wait_queue_head_t
wait
;
struct
evdev_client
*
grab
;
struct
evdev_client
*
grab
;
struct
list_head
client_list
;
struct
list_head
client_list
;
spinlock_t
client_lock
;
/* protects client_list */
struct
mutex
mutex
;
struct
device
dev
;
struct
device
dev
;
};
};
...
@@ -37,39 +39,53 @@ struct evdev_client {
...
@@ -37,39 +39,53 @@ struct evdev_client {
struct
input_event
buffer
[
EVDEV_BUFFER_SIZE
];
struct
input_event
buffer
[
EVDEV_BUFFER_SIZE
];
int
head
;
int
head
;
int
tail
;
int
tail
;
spinlock_t
buffer_lock
;
/* protects access to buffer, head and tail */
struct
fasync_struct
*
fasync
;
struct
fasync_struct
*
fasync
;
struct
evdev
*
evdev
;
struct
evdev
*
evdev
;
struct
list_head
node
;
struct
list_head
node
;
};
};
static
struct
evdev
*
evdev_table
[
EVDEV_MINORS
];
static
struct
evdev
*
evdev_table
[
EVDEV_MINORS
];
static
DEFINE_MUTEX
(
evdev_table_mutex
);
static
void
evdev_event
(
struct
input_handle
*
handle
,
unsigned
int
type
,
unsigned
int
code
,
int
value
)
static
void
evdev_pass_event
(
struct
evdev_client
*
client
,
struct
input_event
*
event
)
{
{
struct
evdev
*
evdev
=
handle
->
private
;
/*
struct
evdev_client
*
client
;
* Interrupts are disabled, just acquire the lock
*/
if
(
evdev
->
grab
)
{
spin_lock
(
&
client
->
buffer_lock
);
client
=
evdev
->
grab
;
client
->
buffer
[
client
->
head
++
]
=
*
event
;
client
->
head
&=
EVDEV_BUFFER_SIZE
-
1
;
do_gettimeofday
(
&
client
->
buffer
[
client
->
head
].
time
);
spin_unlock
(
&
client
->
buffer_lock
);
client
->
buffer
[
client
->
head
].
type
=
type
;
client
->
buffer
[
client
->
head
].
code
=
code
;
client
->
buffer
[
client
->
head
].
value
=
value
;
client
->
head
=
(
client
->
head
+
1
)
&
(
EVDEV_BUFFER_SIZE
-
1
);
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_IN
);
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_IN
);
}
else
}
list_for_each_entry
(
client
,
&
evdev
->
client_list
,
node
)
{
do_gettimeofday
(
&
client
->
buffer
[
client
->
head
].
time
);
/*
client
->
buffer
[
client
->
head
].
type
=
type
;
* Pass incoming event to all connected clients. Note that we are
client
->
buffer
[
client
->
head
].
code
=
code
;
* caleld under a spinlock with interrupts off so we don't need
client
->
buffer
[
client
->
head
].
value
=
value
;
* to use rcu_read_lock() here. Writers will be using syncronize_sched()
client
->
head
=
(
client
->
head
+
1
)
&
(
EVDEV_BUFFER_SIZE
-
1
);
* instead of synchrnoize_rcu().
*/
static
void
evdev_event
(
struct
input_handle
*
handle
,
unsigned
int
type
,
unsigned
int
code
,
int
value
)
{
struct
evdev
*
evdev
=
handle
->
private
;
struct
evdev_client
*
client
;
struct
input_event
event
;
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_IN
);
do_gettimeofday
(
&
event
.
time
);
}
event
.
type
=
type
;
event
.
code
=
code
;
event
.
value
=
value
;
client
=
rcu_dereference
(
evdev
->
grab
);
if
(
client
)
evdev_pass_event
(
client
,
&
event
);
else
list_for_each_entry_rcu
(
client
,
&
evdev
->
client_list
,
node
)
evdev_pass_event
(
client
,
&
event
);
wake_up_interruptible
(
&
evdev
->
wait
);
wake_up_interruptible
(
&
evdev
->
wait
);
}
}
...
@@ -88,38 +104,142 @@ static int evdev_flush(struct file *file, fl_owner_t id)
...
@@ -88,38 +104,142 @@ static int evdev_flush(struct file *file, fl_owner_t id)
{
{
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev
*
evdev
=
client
->
evdev
;
struct
evdev
*
evdev
=
client
->
evdev
;
int
retval
;
retval
=
mutex_lock_interruptible
(
&
evdev
->
mutex
);
if
(
retval
)
return
retval
;
if
(
!
evdev
->
exist
)
if
(
!
evdev
->
exist
)
return
-
ENODEV
;
retval
=
-
ENODEV
;
else
retval
=
input_flush_device
(
&
evdev
->
handle
,
file
);
return
input_flush_device
(
&
evdev
->
handle
,
file
);
mutex_unlock
(
&
evdev
->
mutex
);
return
retval
;
}
}
static
void
evdev_free
(
struct
device
*
dev
)
static
void
evdev_free
(
struct
device
*
dev
)
{
{
struct
evdev
*
evdev
=
container_of
(
dev
,
struct
evdev
,
dev
);
struct
evdev
*
evdev
=
container_of
(
dev
,
struct
evdev
,
dev
);
evdev_table
[
evdev
->
minor
]
=
NULL
;
kfree
(
evdev
);
kfree
(
evdev
);
}
}
/*
* Grabs an event device (along with underlying input device).
* This function is called with evdev->mutex taken.
*/
static
int
evdev_grab
(
struct
evdev
*
evdev
,
struct
evdev_client
*
client
)
{
int
error
;
if
(
evdev
->
grab
)
return
-
EBUSY
;
error
=
input_grab_device
(
&
evdev
->
handle
);
if
(
error
)
return
error
;
rcu_assign_pointer
(
evdev
->
grab
,
client
);
/*
* We don't use synchronize_rcu() here because read-side
* critical section is protected by a spinlock instead
* of rcu_read_lock().
*/
synchronize_sched
();
return
0
;
}
static
int
evdev_ungrab
(
struct
evdev
*
evdev
,
struct
evdev_client
*
client
)
{
if
(
evdev
->
grab
!=
client
)
return
-
EINVAL
;
rcu_assign_pointer
(
evdev
->
grab
,
NULL
);
synchronize_sched
();
input_release_device
(
&
evdev
->
handle
);
return
0
;
}
static
void
evdev_attach_client
(
struct
evdev
*
evdev
,
struct
evdev_client
*
client
)
{
spin_lock
(
&
evdev
->
client_lock
);
list_add_tail_rcu
(
&
client
->
node
,
&
evdev
->
client_list
);
spin_unlock
(
&
evdev
->
client_lock
);
synchronize_sched
();
}
static
void
evdev_detach_client
(
struct
evdev
*
evdev
,
struct
evdev_client
*
client
)
{
spin_lock
(
&
evdev
->
client_lock
);
list_del_rcu
(
&
client
->
node
);
spin_unlock
(
&
evdev
->
client_lock
);
synchronize_sched
();
}
static
int
evdev_open_device
(
struct
evdev
*
evdev
)
{
int
retval
;
retval
=
mutex_lock_interruptible
(
&
evdev
->
mutex
);
if
(
retval
)
return
retval
;
if
(
!
evdev
->
exist
)
retval
=
-
ENODEV
;
else
if
(
!
evdev
->
open
++
)
retval
=
input_open_device
(
&
evdev
->
handle
);
mutex_unlock
(
&
evdev
->
mutex
);
return
retval
;
}
static
void
evdev_close_device
(
struct
evdev
*
evdev
)
{
mutex_lock
(
&
evdev
->
mutex
);
if
(
evdev
->
exist
&&
!--
evdev
->
open
)
input_close_device
(
&
evdev
->
handle
);
mutex_unlock
(
&
evdev
->
mutex
);
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device.
*/
static
void
evdev_hangup
(
struct
evdev
*
evdev
)
{
struct
evdev_client
*
client
;
spin_lock
(
&
evdev
->
client_lock
);
list_for_each_entry
(
client
,
&
evdev
->
client_list
,
node
)
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_HUP
);
spin_unlock
(
&
evdev
->
client_lock
);
wake_up_interruptible
(
&
evdev
->
wait
);
}
static
int
evdev_release
(
struct
inode
*
inode
,
struct
file
*
file
)
static
int
evdev_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
{
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev
*
evdev
=
client
->
evdev
;
struct
evdev
*
evdev
=
client
->
evdev
;
if
(
evdev
->
grab
==
client
)
{
mutex_lock
(
&
evdev
->
mutex
);
input_release_device
(
&
evdev
->
handle
);
if
(
evdev
->
grab
==
client
)
evdev
->
grab
=
NULL
;
evdev
_ungrab
(
evdev
,
client
)
;
}
mutex_unlock
(
&
evdev
->
mutex
);
evdev_fasync
(
-
1
,
file
,
0
);
evdev_fasync
(
-
1
,
file
,
0
);
list_del
(
&
client
->
node
);
evdev_detach_client
(
evdev
,
client
);
kfree
(
client
);
kfree
(
client
);
if
(
!--
evdev
->
open
&&
evdev
->
exist
)
evdev_close_device
(
evdev
);
input_close_device
(
&
evdev
->
handle
);
put_device
(
&
evdev
->
dev
);
put_device
(
&
evdev
->
dev
);
return
0
;
return
0
;
...
@@ -127,41 +247,44 @@ static int evdev_release(struct inode *inode, struct file *file)
...
@@ -127,41 +247,44 @@ static int evdev_release(struct inode *inode, struct file *file)
static
int
evdev_open
(
struct
inode
*
inode
,
struct
file
*
file
)
static
int
evdev_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
{
struct
evdev_client
*
client
;
struct
evdev
*
evdev
;
struct
evdev
*
evdev
;
struct
evdev_client
*
client
;
int
i
=
iminor
(
inode
)
-
EVDEV_MINOR_BASE
;
int
i
=
iminor
(
inode
)
-
EVDEV_MINOR_BASE
;
int
error
;
int
error
;
if
(
i
>=
EVDEV_MINORS
)
if
(
i
>=
EVDEV_MINORS
)
return
-
ENODEV
;
return
-
ENODEV
;
error
=
mutex_lock_interruptible
(
&
evdev_table_mutex
);
if
(
error
)
return
error
;
evdev
=
evdev_table
[
i
];
evdev
=
evdev_table
[
i
];
if
(
evdev
)
get_device
(
&
evdev
->
dev
);
mutex_unlock
(
&
evdev_table_mutex
);
if
(
!
evdev
||
!
evdev
->
exist
)
if
(
!
evdev
)
return
-
ENODEV
;
return
-
ENODEV
;
get_device
(
&
evdev
->
dev
);
client
=
kzalloc
(
sizeof
(
struct
evdev_client
),
GFP_KERNEL
);
client
=
kzalloc
(
sizeof
(
struct
evdev_client
),
GFP_KERNEL
);
if
(
!
client
)
{
if
(
!
client
)
{
error
=
-
ENOMEM
;
error
=
-
ENOMEM
;
goto
err_put_evdev
;
goto
err_put_evdev
;
}
}
spin_lock_init
(
&
client
->
buffer_lock
);
client
->
evdev
=
evdev
;
client
->
evdev
=
evdev
;
list_add_tail
(
&
client
->
node
,
&
evdev
->
client_lis
t
);
evdev_attach_client
(
evdev
,
clien
t
);
if
(
!
evdev
->
open
++
&&
evdev
->
exist
)
{
error
=
evdev_open_device
(
evdev
);
error
=
input_open_device
(
&
evdev
->
handle
);
if
(
error
)
if
(
error
)
goto
err_free_client
;
goto
err_free_client
;
}
file
->
private_data
=
client
;
file
->
private_data
=
client
;
return
0
;
return
0
;
err_free_client:
err_free_client:
list_del
(
&
client
->
node
);
evdev_detach_client
(
evdev
,
client
);
kfree
(
client
);
kfree
(
client
);
err_put_evdev:
err_put_evdev:
put_device
(
&
evdev
->
dev
);
put_device
(
&
evdev
->
dev
);
...
@@ -197,12 +320,14 @@ static inline size_t evdev_event_size(void)
...
@@ -197,12 +320,14 @@ static inline size_t evdev_event_size(void)
sizeof
(
struct
input_event_compat
)
:
sizeof
(
struct
input_event
);
sizeof
(
struct
input_event_compat
)
:
sizeof
(
struct
input_event
);
}
}
static
int
evdev_event_from_user
(
const
char
__user
*
buffer
,
struct
input_event
*
event
)
static
int
evdev_event_from_user
(
const
char
__user
*
buffer
,
struct
input_event
*
event
)
{
{
if
(
COMPAT_TEST
)
{
if
(
COMPAT_TEST
)
{
struct
input_event_compat
compat_event
;
struct
input_event_compat
compat_event
;
if
(
copy_from_user
(
&
compat_event
,
buffer
,
sizeof
(
struct
input_event_compat
)))
if
(
copy_from_user
(
&
compat_event
,
buffer
,
sizeof
(
struct
input_event_compat
)))
return
-
EFAULT
;
return
-
EFAULT
;
event
->
time
.
tv_sec
=
compat_event
.
time
.
tv_sec
;
event
->
time
.
tv_sec
=
compat_event
.
time
.
tv_sec
;
...
@@ -219,7 +344,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
...
@@ -219,7 +344,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
return
0
;
return
0
;
}
}
static
int
evdev_event_to_user
(
char
__user
*
buffer
,
const
struct
input_event
*
event
)
static
int
evdev_event_to_user
(
char
__user
*
buffer
,
const
struct
input_event
*
event
)
{
{
if
(
COMPAT_TEST
)
{
if
(
COMPAT_TEST
)
{
struct
input_event_compat
compat_event
;
struct
input_event_compat
compat_event
;
...
@@ -230,7 +356,8 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
...
@@ -230,7 +356,8 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
compat_event
.
code
=
event
->
code
;
compat_event
.
code
=
event
->
code
;
compat_event
.
value
=
event
->
value
;
compat_event
.
value
=
event
->
value
;
if
(
copy_to_user
(
buffer
,
&
compat_event
,
sizeof
(
struct
input_event_compat
)))
if
(
copy_to_user
(
buffer
,
&
compat_event
,
sizeof
(
struct
input_event_compat
)))
return
-
EFAULT
;
return
-
EFAULT
;
}
else
{
}
else
{
...
@@ -248,7 +375,8 @@ static inline size_t evdev_event_size(void)
...
@@ -248,7 +375,8 @@ static inline size_t evdev_event_size(void)
return
sizeof
(
struct
input_event
);
return
sizeof
(
struct
input_event
);
}
}
static
int
evdev_event_from_user
(
const
char
__user
*
buffer
,
struct
input_event
*
event
)
static
int
evdev_event_from_user
(
const
char
__user
*
buffer
,
struct
input_event
*
event
)
{
{
if
(
copy_from_user
(
event
,
buffer
,
sizeof
(
struct
input_event
)))
if
(
copy_from_user
(
event
,
buffer
,
sizeof
(
struct
input_event
)))
return
-
EFAULT
;
return
-
EFAULT
;
...
@@ -256,7 +384,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
...
@@ -256,7 +384,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
return
0
;
return
0
;
}
}
static
int
evdev_event_to_user
(
char
__user
*
buffer
,
const
struct
input_event
*
event
)
static
int
evdev_event_to_user
(
char
__user
*
buffer
,
const
struct
input_event
*
event
)
{
{
if
(
copy_to_user
(
buffer
,
event
,
sizeof
(
struct
input_event
)))
if
(
copy_to_user
(
buffer
,
event
,
sizeof
(
struct
input_event
)))
return
-
EFAULT
;
return
-
EFAULT
;
...
@@ -266,37 +395,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
...
@@ -266,37 +395,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
#endif
/* CONFIG_COMPAT */
#endif
/* CONFIG_COMPAT */
static
ssize_t
evdev_write
(
struct
file
*
file
,
const
char
__user
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
static
ssize_t
evdev_write
(
struct
file
*
file
,
const
char
__user
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
{
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev
*
evdev
=
client
->
evdev
;
struct
evdev
*
evdev
=
client
->
evdev
;
struct
input_event
event
;
struct
input_event
event
;
int
retval
=
0
;
int
retval
;
if
(
!
evdev
->
exist
)
retval
=
mutex_lock_interruptible
(
&
evdev
->
mutex
);
return
-
ENODEV
;
if
(
retval
)
return
retval
;
if
(
!
evdev
->
exist
)
{
retval
=
-
ENODEV
;
goto
out
;
}
while
(
retval
<
count
)
{
while
(
retval
<
count
)
{
if
(
evdev_event_from_user
(
buffer
+
retval
,
&
event
))
if
(
evdev_event_from_user
(
buffer
+
retval
,
&
event
))
{
return
-
EFAULT
;
retval
=
-
EFAULT
;
input_inject_event
(
&
evdev
->
handle
,
event
.
type
,
event
.
code
,
event
.
value
);
goto
out
;
}
input_inject_event
(
&
evdev
->
handle
,
event
.
type
,
event
.
code
,
event
.
value
);
retval
+=
evdev_event_size
();
retval
+=
evdev_event_size
();
}
}
out:
mutex_unlock
(
&
evdev
->
mutex
);
return
retval
;
return
retval
;
}
}
static
ssize_t
evdev_read
(
struct
file
*
file
,
char
__user
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
static
int
evdev_fetch_next_event
(
struct
evdev_client
*
client
,
struct
input_event
*
event
)
{
int
have_event
;
spin_lock_irq
(
&
client
->
buffer_lock
);
have_event
=
client
->
head
!=
client
->
tail
;
if
(
have_event
)
{
*
event
=
client
->
buffer
[
client
->
tail
++
];
client
->
tail
&=
EVDEV_BUFFER_SIZE
-
1
;
}
spin_unlock_irq
(
&
client
->
buffer_lock
);
return
have_event
;
}
static
ssize_t
evdev_read
(
struct
file
*
file
,
char
__user
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
{
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev
*
evdev
=
client
->
evdev
;
struct
evdev
*
evdev
=
client
->
evdev
;
struct
input_event
event
;
int
retval
;
int
retval
;
if
(
count
<
evdev_event_size
())
if
(
count
<
evdev_event_size
())
return
-
EINVAL
;
return
-
EINVAL
;
if
(
client
->
head
==
client
->
tail
&&
evdev
->
exist
&&
(
file
->
f_flags
&
O_NONBLOCK
))
if
(
client
->
head
==
client
->
tail
&&
evdev
->
exist
&&
(
file
->
f_flags
&
O_NONBLOCK
))
return
-
EAGAIN
;
return
-
EAGAIN
;
retval
=
wait_event_interruptible
(
evdev
->
wait
,
retval
=
wait_event_interruptible
(
evdev
->
wait
,
...
@@ -307,14 +470,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count,
...
@@ -307,14 +470,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count,
if
(
!
evdev
->
exist
)
if
(
!
evdev
->
exist
)
return
-
ENODEV
;
return
-
ENODEV
;
while
(
client
->
head
!=
client
->
tail
&&
retval
+
evdev_event_size
()
<=
count
)
{
while
(
retval
+
evdev_event_size
()
<=
count
&&
evdev_fetch_next_event
(
client
,
&
event
))
{
struct
input_event
*
event
=
(
struct
input_event
*
)
client
->
buffer
+
client
->
tail
;
if
(
evdev_event_to_user
(
buffer
+
retval
,
event
))
if
(
evdev_event_to_user
(
buffer
+
retval
,
&
event
))
return
-
EFAULT
;
return
-
EFAULT
;
client
->
tail
=
(
client
->
tail
+
1
)
&
(
EVDEV_BUFFER_SIZE
-
1
);
retval
+=
evdev_event_size
();
retval
+=
evdev_event_size
();
}
}
...
@@ -409,7 +570,7 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
...
@@ -409,7 +570,7 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
return
copy_to_user
(
p
,
str
,
len
)
?
-
EFAULT
:
len
;
return
copy_to_user
(
p
,
str
,
len
)
?
-
EFAULT
:
len
;
}
}
static
long
evdev_
ioctl_handler
(
struct
file
*
file
,
unsigned
int
cmd
,
static
long
evdev_
do_ioctl
(
struct
file
*
file
,
unsigned
int
cmd
,
void
__user
*
p
,
int
compat_mode
)
void
__user
*
p
,
int
compat_mode
)
{
{
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev_client
*
client
=
file
->
private_data
;
...
@@ -421,9 +582,6 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
...
@@ -421,9 +582,6 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
int
i
,
t
,
u
,
v
;
int
i
,
t
,
u
,
v
;
int
error
;
int
error
;
if
(
!
evdev
->
exist
)
return
-
ENODEV
;
switch
(
cmd
)
{
switch
(
cmd
)
{
case
EVIOCGVERSION
:
case
EVIOCGVERSION
:
...
@@ -490,26 +648,17 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
...
@@ -490,26 +648,17 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
return
input_ff_erase
(
dev
,
(
int
)(
unsigned
long
)
p
,
file
);
return
input_ff_erase
(
dev
,
(
int
)(
unsigned
long
)
p
,
file
);
case
EVIOCGEFFECTS
:
case
EVIOCGEFFECTS
:
i
=
test_bit
(
EV_FF
,
dev
->
evbit
)
?
dev
->
ff
->
max_effects
:
0
;
i
=
test_bit
(
EV_FF
,
dev
->
evbit
)
?
dev
->
ff
->
max_effects
:
0
;
if
(
put_user
(
i
,
ip
))
if
(
put_user
(
i
,
ip
))
return
-
EFAULT
;
return
-
EFAULT
;
return
0
;
return
0
;
case
EVIOCGRAB
:
case
EVIOCGRAB
:
if
(
p
)
{
if
(
p
)
if
(
evdev
->
grab
)
return
evdev_grab
(
evdev
,
client
);
return
-
EBUSY
;
else
if
(
input_grab_device
(
&
evdev
->
handle
))
return
evdev_ungrab
(
evdev
,
client
);
return
-
EBUSY
;
evdev
->
grab
=
client
;
return
0
;
}
else
{
if
(
evdev
->
grab
!=
client
)
return
-
EINVAL
;
input_release_device
(
&
evdev
->
handle
);
evdev
->
grab
=
NULL
;
return
0
;
}
default:
default:
...
@@ -518,12 +667,13 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
...
@@ -518,12 +667,13 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
if
(
_IOC_DIR
(
cmd
)
==
_IOC_READ
)
{
if
(
_IOC_DIR
(
cmd
)
==
_IOC_READ
)
{
if
((
_IOC_NR
(
cmd
)
&
~
EV_MAX
)
==
_IOC_NR
(
EVIOCGBIT
(
0
,
0
)))
{
if
((
_IOC_NR
(
cmd
)
&
~
EV_MAX
)
==
_IOC_NR
(
EVIOCGBIT
(
0
,
0
)))
{
unsigned
long
*
bits
;
unsigned
long
*
bits
;
int
len
;
int
len
;
switch
(
_IOC_NR
(
cmd
)
&
EV_MAX
)
{
switch
(
_IOC_NR
(
cmd
)
&
EV_MAX
)
{
case
0
:
bits
=
dev
->
evbit
;
len
=
EV_MAX
;
break
;
case
0
:
bits
=
dev
->
evbit
;
len
=
EV_MAX
;
break
;
case
EV_KEY
:
bits
=
dev
->
keybit
;
len
=
KEY_MAX
;
break
;
case
EV_KEY
:
bits
=
dev
->
keybit
;
len
=
KEY_MAX
;
break
;
case
EV_REL
:
bits
=
dev
->
relbit
;
len
=
REL_MAX
;
break
;
case
EV_REL
:
bits
=
dev
->
relbit
;
len
=
REL_MAX
;
break
;
...
@@ -587,15 +737,25 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
...
@@ -587,15 +737,25 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
t
=
_IOC_NR
(
cmd
)
&
ABS_MAX
;
t
=
_IOC_NR
(
cmd
)
&
ABS_MAX
;
if
(
copy_from_user
(
&
abs
,
p
,
sizeof
(
struct
input_absinfo
)))
if
(
copy_from_user
(
&
abs
,
p
,
sizeof
(
struct
input_absinfo
)))
return
-
EFAULT
;
return
-
EFAULT
;
/*
* Take event lock to ensure that we are not
* changing device parameters in the middle
* of event.
*/
spin_lock_irq
(
&
dev
->
event_lock
);
dev
->
abs
[
t
]
=
abs
.
value
;
dev
->
abs
[
t
]
=
abs
.
value
;
dev
->
absmin
[
t
]
=
abs
.
minimum
;
dev
->
absmin
[
t
]
=
abs
.
minimum
;
dev
->
absmax
[
t
]
=
abs
.
maximum
;
dev
->
absmax
[
t
]
=
abs
.
maximum
;
dev
->
absfuzz
[
t
]
=
abs
.
fuzz
;
dev
->
absfuzz
[
t
]
=
abs
.
fuzz
;
dev
->
absflat
[
t
]
=
abs
.
flat
;
dev
->
absflat
[
t
]
=
abs
.
flat
;
spin_unlock_irq
(
&
dev
->
event_lock
);
return
0
;
return
0
;
}
}
}
}
...
@@ -603,13 +763,37 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
...
@@ -603,13 +763,37 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
return
-
EINVAL
;
return
-
EINVAL
;
}
}
static
long
evdev_ioctl_handler
(
struct
file
*
file
,
unsigned
int
cmd
,
void
__user
*
p
,
int
compat_mode
)
{
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev
*
evdev
=
client
->
evdev
;
int
retval
;
retval
=
mutex_lock_interruptible
(
&
evdev
->
mutex
);
if
(
retval
)
return
retval
;
if
(
!
evdev
->
exist
)
{
retval
=
-
ENODEV
;
goto
out
;
}
retval
=
evdev_do_ioctl
(
file
,
cmd
,
p
,
compat_mode
);
out:
mutex_unlock
(
&
evdev
->
mutex
);
return
retval
;
}
static
long
evdev_ioctl
(
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
static
long
evdev_ioctl
(
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
{
return
evdev_ioctl_handler
(
file
,
cmd
,
(
void
__user
*
)
arg
,
0
);
return
evdev_ioctl_handler
(
file
,
cmd
,
(
void
__user
*
)
arg
,
0
);
}
}
#ifdef CONFIG_COMPAT
#ifdef CONFIG_COMPAT
static
long
evdev_ioctl_compat
(
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
static
long
evdev_ioctl_compat
(
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
{
return
evdev_ioctl_handler
(
file
,
cmd
,
compat_ptr
(
arg
),
1
);
return
evdev_ioctl_handler
(
file
,
cmd
,
compat_ptr
(
arg
),
1
);
}
}
...
@@ -630,6 +814,57 @@ static const struct file_operations evdev_fops = {
...
@@ -630,6 +814,57 @@ static const struct file_operations evdev_fops = {
.
flush
=
evdev_flush
.
flush
=
evdev_flush
};
};
static
int
evdev_install_chrdev
(
struct
evdev
*
evdev
)
{
/*
* No need to do any locking here as calls to connect and
* disconnect are serialized by the input core
*/
evdev_table
[
evdev
->
minor
]
=
evdev
;
return
0
;
}
static
void
evdev_remove_chrdev
(
struct
evdev
*
evdev
)
{
/*
* Lock evdev table to prevent race with evdev_open()
*/
mutex_lock
(
&
evdev_table_mutex
);
evdev_table
[
evdev
->
minor
]
=
NULL
;
mutex_unlock
(
&
evdev_table_mutex
);
}
/*
* Mark device non-existent. This disables writes, ioctls and
* prevents new users from opening the device. Already posted
* blocking reads will stay, however new ones will fail.
*/
static
void
evdev_mark_dead
(
struct
evdev
*
evdev
)
{
mutex_lock
(
&
evdev
->
mutex
);
evdev
->
exist
=
0
;
mutex_unlock
(
&
evdev
->
mutex
);
}
static
void
evdev_cleanup
(
struct
evdev
*
evdev
)
{
struct
input_handle
*
handle
=
&
evdev
->
handle
;
evdev_mark_dead
(
evdev
);
evdev_hangup
(
evdev
);
evdev_remove_chrdev
(
evdev
);
/* evdev is marked dead so no one else accesses evdev->open */
if
(
evdev
->
open
)
{
input_flush_device
(
handle
,
NULL
);
input_close_device
(
handle
);
}
}
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect so we don't need to lock evdev_table here.
*/
static
int
evdev_connect
(
struct
input_handler
*
handler
,
struct
input_dev
*
dev
,
static
int
evdev_connect
(
struct
input_handler
*
handler
,
struct
input_dev
*
dev
,
const
struct
input_device_id
*
id
)
const
struct
input_device_id
*
id
)
{
{
...
@@ -637,7 +872,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
...
@@ -637,7 +872,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
int
minor
;
int
minor
;
int
error
;
int
error
;
for
(
minor
=
0
;
minor
<
EVDEV_MINORS
&&
evdev_table
[
minor
];
minor
++
);
for
(
minor
=
0
;
minor
<
EVDEV_MINORS
;
minor
++
)
if
(
!
evdev_table
[
minor
])
break
;
if
(
minor
==
EVDEV_MINORS
)
{
if
(
minor
==
EVDEV_MINORS
)
{
printk
(
KERN_ERR
"evdev: no more free evdev devices
\n
"
);
printk
(
KERN_ERR
"evdev: no more free evdev devices
\n
"
);
return
-
ENFILE
;
return
-
ENFILE
;
...
@@ -648,38 +886,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
...
@@ -648,38 +886,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
return
-
ENOMEM
;
return
-
ENOMEM
;
INIT_LIST_HEAD
(
&
evdev
->
client_list
);
INIT_LIST_HEAD
(
&
evdev
->
client_list
);
spin_lock_init
(
&
evdev
->
client_lock
);
mutex_init
(
&
evdev
->
mutex
);
init_waitqueue_head
(
&
evdev
->
wait
);
init_waitqueue_head
(
&
evdev
->
wait
);
snprintf
(
evdev
->
name
,
sizeof
(
evdev
->
name
),
"event%d"
,
minor
);
evdev
->
exist
=
1
;
evdev
->
exist
=
1
;
evdev
->
minor
=
minor
;
evdev
->
minor
=
minor
;
evdev
->
handle
.
dev
=
dev
;
evdev
->
handle
.
dev
=
dev
;
evdev
->
handle
.
name
=
evdev
->
name
;
evdev
->
handle
.
name
=
evdev
->
name
;
evdev
->
handle
.
handler
=
handler
;
evdev
->
handle
.
handler
=
handler
;
evdev
->
handle
.
private
=
evdev
;
evdev
->
handle
.
private
=
evdev
;
snprintf
(
evdev
->
name
,
sizeof
(
evdev
->
name
),
"event%d"
,
minor
);
s
nprintf
(
evdev
->
dev
.
bus_id
,
sizeof
(
evdev
->
dev
.
bus_id
),
s
trlcpy
(
evdev
->
dev
.
bus_id
,
evdev
->
name
,
sizeof
(
evdev
->
dev
.
bus_id
));
"event%d"
,
minor
);
evdev
->
dev
.
devt
=
MKDEV
(
INPUT_MAJOR
,
EVDEV_MINOR_BASE
+
minor
);
evdev
->
dev
.
class
=
&
input_class
;
evdev
->
dev
.
class
=
&
input_class
;
evdev
->
dev
.
parent
=
&
dev
->
dev
;
evdev
->
dev
.
parent
=
&
dev
->
dev
;
evdev
->
dev
.
devt
=
MKDEV
(
INPUT_MAJOR
,
EVDEV_MINOR_BASE
+
minor
);
evdev
->
dev
.
release
=
evdev_free
;
evdev
->
dev
.
release
=
evdev_free
;
device_initialize
(
&
evdev
->
dev
);
device_initialize
(
&
evdev
->
dev
);
evdev_table
[
minor
]
=
evdev
;
error
=
input_register_handle
(
&
evdev
->
handle
);
error
=
device_add
(
&
evdev
->
dev
);
if
(
error
)
if
(
error
)
goto
err_free_evdev
;
goto
err_free_evdev
;
error
=
input_register_handle
(
&
evdev
->
handle
);
error
=
evdev_install_chrdev
(
evdev
);
if
(
error
)
if
(
error
)
goto
err_delete_evdev
;
goto
err_unregister_handle
;
error
=
device_add
(
&
evdev
->
dev
);
if
(
error
)
goto
err_cleanup_evdev
;
return
0
;
return
0
;
err_delete_evdev:
err_cleanup_evdev:
device_del
(
&
evdev
->
dev
);
evdev_cleanup
(
evdev
);
err_unregister_handle:
input_unregister_handle
(
&
evdev
->
handle
);
err_free_evdev:
err_free_evdev:
put_device
(
&
evdev
->
dev
);
put_device
(
&
evdev
->
dev
);
return
error
;
return
error
;
...
@@ -688,21 +932,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
...
@@ -688,21 +932,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
static
void
evdev_disconnect
(
struct
input_handle
*
handle
)
static
void
evdev_disconnect
(
struct
input_handle
*
handle
)
{
{
struct
evdev
*
evdev
=
handle
->
private
;
struct
evdev
*
evdev
=
handle
->
private
;
struct
evdev_client
*
client
;
input_unregister_handle
(
handle
);
device_del
(
&
evdev
->
dev
);
device_del
(
&
evdev
->
dev
);
evdev_cleanup
(
evdev
);
evdev
->
exist
=
0
;
input_unregister_handle
(
handle
);
if
(
evdev
->
open
)
{
input_flush_device
(
handle
,
NULL
);
input_close_device
(
handle
);
list_for_each_entry
(
client
,
&
evdev
->
client_list
,
node
)
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_HUP
);
wake_up_interruptible
(
&
evdev
->
wait
);
}
put_device
(
&
evdev
->
dev
);
put_device
(
&
evdev
->
dev
);
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录