Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
6addb1d6
Commit
6addb1d6
authored
Aug 30, 2007
by
Dmitry Torokhov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Input: evdev - implement proper locking
Signed-off-by:
Dmitry Torokhov
<
dtor@mail.ru
>
parent
8006479c
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
473 additions
and
240 deletions
+473
-240
drivers/input/evdev.c
drivers/input/evdev.c
+473
-240
No files found.
drivers/input/evdev.c
View file @
6addb1d6
...
...
@@ -30,6 +30,8 @@ struct evdev {
wait_queue_head_t
wait
;
struct
evdev_client
*
grab
;
struct
list_head
client_list
;
spinlock_t
client_lock
;
/* protects client_list */
struct
mutex
mutex
;
struct
device
dev
;
};
...
...
@@ -37,39 +39,53 @@ struct evdev_client {
struct
input_event
buffer
[
EVDEV_BUFFER_SIZE
];
int
head
;
int
tail
;
spinlock_t
buffer_lock
;
/* protects access to buffer, head and tail */
struct
fasync_struct
*
fasync
;
struct
evdev
*
evdev
;
struct
list_head
node
;
};
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
)
{
/*
* Interrupts are disabled, just acquire the lock
*/
spin_lock
(
&
client
->
buffer_lock
);
client
->
buffer
[
client
->
head
++
]
=
*
event
;
client
->
head
&=
EVDEV_BUFFER_SIZE
-
1
;
spin_unlock
(
&
client
->
buffer_lock
);
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_IN
);
}
/*
* Pass incoming event to all connected clients. Note that we are
* caleld under a spinlock with interrupts off so we don't need
* to use rcu_read_lock() here. Writers will be using syncronize_sched()
* 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
;
if
(
evdev
->
grab
)
{
client
=
evdev
->
grab
;
do_gettimeofday
(
&
client
->
buffer
[
client
->
head
].
time
);
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
);
}
else
list_for_each_entry
(
client
,
&
evdev
->
client_list
,
node
)
{
do_gettimeofday
(
&
client
->
buffer
[
client
->
head
].
time
);
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
);
do_gettimeofday
(
&
event
.
time
);
event
.
type
=
type
;
event
.
code
=
code
;
event
.
value
=
value
;
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_IN
);
}
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
);
}
...
...
@@ -88,38 +104,142 @@ static int evdev_flush(struct file *file, fl_owner_t id)
{
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
)
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
)
{
struct
evdev
*
evdev
=
container_of
(
dev
,
struct
evdev
,
dev
);
evdev_table
[
evdev
->
minor
]
=
NULL
;
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
)
{
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev
*
evdev
=
client
->
evdev
;
if
(
evdev
->
grab
==
client
)
{
input_release_device
(
&
evdev
->
handle
);
evdev
->
grab
=
NULL
;
}
mutex_lock
(
&
evdev
->
mutex
);
if
(
evdev
->
grab
==
client
)
evdev
_ungrab
(
evdev
,
client
)
;
mutex_unlock
(
&
evdev
->
mutex
);
evdev_fasync
(
-
1
,
file
,
0
);
list_del
(
&
client
->
node
);
evdev_detach_client
(
evdev
,
client
);
kfree
(
client
);
if
(
!--
evdev
->
open
&&
evdev
->
exist
)
input_close_device
(
&
evdev
->
handle
);
evdev_close_device
(
evdev
);
put_device
(
&
evdev
->
dev
);
return
0
;
...
...
@@ -127,41 +247,44 @@ static int evdev_release(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_client
*
client
;
int
i
=
iminor
(
inode
)
-
EVDEV_MINOR_BASE
;
int
error
;
if
(
i
>=
EVDEV_MINORS
)
return
-
ENODEV
;
error
=
mutex_lock_interruptible
(
&
evdev_table_mutex
);
if
(
error
)
return
error
;
evdev
=
evdev_table
[
i
];
if
(
evdev
)
get_device
(
&
evdev
->
dev
);
mutex_unlock
(
&
evdev_table_mutex
);
if
(
!
evdev
||
!
evdev
->
exist
)
if
(
!
evdev
)
return
-
ENODEV
;
get_device
(
&
evdev
->
dev
);
client
=
kzalloc
(
sizeof
(
struct
evdev_client
),
GFP_KERNEL
);
if
(
!
client
)
{
error
=
-
ENOMEM
;
goto
err_put_evdev
;
}
spin_lock_init
(
&
client
->
buffer_lock
);
client
->
evdev
=
evdev
;
list_add_tail
(
&
client
->
node
,
&
evdev
->
client_lis
t
);
evdev_attach_client
(
evdev
,
clien
t
);
if
(
!
evdev
->
open
++
&&
evdev
->
exist
)
{
error
=
input_open_device
(
&
evdev
->
handle
);
if
(
error
)
goto
err_free_client
;
}
error
=
evdev_open_device
(
evdev
);
if
(
error
)
goto
err_free_client
;
file
->
private_data
=
client
;
return
0
;
err_free_client:
list_del
(
&
client
->
node
);
evdev_detach_client
(
evdev
,
client
);
kfree
(
client
);
err_put_evdev:
put_device
(
&
evdev
->
dev
);
...
...
@@ -197,12 +320,14 @@ static inline size_t evdev_event_size(void)
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
)
{
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
;
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 *
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
)
{
struct
input_event_compat
compat_event
;
...
...
@@ -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
.
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
;
}
else
{
...
...
@@ -248,7 +375,8 @@ static inline size_t evdev_event_size(void)
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
)))
return
-
EFAULT
;
...
...
@@ -256,7 +384,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
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
)))
return
-
EFAULT
;
...
...
@@ -266,37 +395,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
#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
*
evdev
=
client
->
evdev
;
struct
input_event
event
;
int
retval
=
0
;
int
retval
;
if
(
!
evdev
->
exist
)
return
-
ENODEV
;
retval
=
mutex_lock_interruptible
(
&
evdev
->
mutex
);
if
(
retval
)
return
retval
;
if
(
!
evdev
->
exist
)
{
retval
=
-
ENODEV
;
goto
out
;
}
while
(
retval
<
count
)
{
if
(
evdev_event_from_user
(
buffer
+
retval
,
&
event
))
return
-
EFAULT
;
input_inject_event
(
&
evdev
->
handle
,
event
.
type
,
event
.
code
,
event
.
value
);
if
(
evdev_event_from_user
(
buffer
+
retval
,
&
event
))
{
retval
=
-
EFAULT
;
goto
out
;
}
input_inject_event
(
&
evdev
->
handle
,
event
.
type
,
event
.
code
,
event
.
value
);
retval
+=
evdev_event_size
();
}
out:
mutex_unlock
(
&
evdev
->
mutex
);
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
*
evdev
=
client
->
evdev
;
struct
input_event
event
;
int
retval
;
if
(
count
<
evdev_event_size
())
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
;
retval
=
wait_event_interruptible
(
evdev
->
wait
,
...
...
@@ -307,14 +470,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count,
if
(
!
evdev
->
exist
)
return
-
ENODEV
;
while
(
client
->
head
!=
client
->
tail
&&
retval
+
evdev_event_size
()
<=
count
)
{
struct
input_event
*
event
=
(
struct
input_event
*
)
client
->
buffer
+
client
->
tail
;
while
(
retval
+
evdev_event_size
()
<=
count
&&
evdev_fetch_next_event
(
client
,
&
event
))
{
if
(
evdev_event_to_user
(
buffer
+
retval
,
event
))
if
(
evdev_event_to_user
(
buffer
+
retval
,
&
event
))
return
-
EFAULT
;
client
->
tail
=
(
client
->
tail
+
1
)
&
(
EVDEV_BUFFER_SIZE
-
1
);
retval
+=
evdev_event_size
();
}
...
...
@@ -409,8 +570,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
return
copy_to_user
(
p
,
str
,
len
)
?
-
EFAULT
:
len
;
}
static
long
evdev_
ioctl_handler
(
struct
file
*
file
,
unsigned
int
cmd
,
void
__user
*
p
,
int
compat_mode
)
static
long
evdev_
do_ioctl
(
struct
file
*
file
,
unsigned
int
cmd
,
void
__user
*
p
,
int
compat_mode
)
{
struct
evdev_client
*
client
=
file
->
private_data
;
struct
evdev
*
evdev
=
client
->
evdev
;
...
...
@@ -421,215 +582,289 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
int
i
,
t
,
u
,
v
;
int
error
;
if
(
!
evdev
->
exist
)
return
-
ENODEV
;
switch
(
cmd
)
{
case
EVIOCGVERSION
:
return
put_user
(
EV_VERSION
,
ip
);
case
EVIOCGVERSION
:
return
put_user
(
EV_VERSION
,
ip
);
case
EVIOCGID
:
if
(
copy_to_user
(
p
,
&
dev
->
id
,
sizeof
(
struct
input_id
)))
return
-
EFAULT
;
return
0
;
case
EVIOCGID
:
if
(
copy_to_user
(
p
,
&
dev
->
id
,
sizeof
(
struct
input_id
)))
return
-
EFAULT
;
return
0
;
case
EVIOCGREP
:
if
(
!
test_bit
(
EV_REP
,
dev
->
evbit
))
return
-
ENOSYS
;
if
(
put_user
(
dev
->
rep
[
REP_DELAY
],
ip
))
return
-
EFAULT
;
if
(
put_user
(
dev
->
rep
[
REP_PERIOD
],
ip
+
1
))
return
-
EFAULT
;
return
0
;
case
EVIOCGREP
:
if
(
!
test_bit
(
EV_REP
,
dev
->
evbit
))
return
-
ENOSYS
;
if
(
put_user
(
dev
->
rep
[
REP_DELAY
],
ip
))
return
-
EFAULT
;
if
(
put_user
(
dev
->
rep
[
REP_PERIOD
],
ip
+
1
))
return
-
EFAULT
;
return
0
;
case
EVIOCSREP
:
if
(
!
test_bit
(
EV_REP
,
dev
->
evbit
))
return
-
ENOSYS
;
if
(
get_user
(
u
,
ip
))
return
-
EFAULT
;
if
(
get_user
(
v
,
ip
+
1
))
return
-
EFAULT
;
case
EVIOCSREP
:
if
(
!
test_bit
(
EV_REP
,
dev
->
evbit
))
return
-
ENOSYS
;
if
(
get_user
(
u
,
ip
))
return
-
EFAULT
;
if
(
get_user
(
v
,
ip
+
1
))
return
-
EFAULT
;
input_inject_event
(
&
evdev
->
handle
,
EV_REP
,
REP_DELAY
,
u
);
input_inject_event
(
&
evdev
->
handle
,
EV_REP
,
REP_PERIOD
,
v
);
input_inject_event
(
&
evdev
->
handle
,
EV_REP
,
REP_DELAY
,
u
);
input_inject_event
(
&
evdev
->
handle
,
EV_REP
,
REP_PERIOD
,
v
);
return
0
;
return
0
;
case
EVIOCGKEYCODE
:
if
(
get_user
(
t
,
ip
))
return
-
EFAULT
;
case
EVIOCGKEYCODE
:
if
(
get_user
(
t
,
ip
))
return
-
EFAULT
;
error
=
dev
->
getkeycode
(
dev
,
t
,
&
v
);
if
(
error
)
return
error
;
error
=
dev
->
getkeycode
(
dev
,
t
,
&
v
);
if
(
error
)
return
error
;
if
(
put_user
(
v
,
ip
+
1
))
return
-
EFAULT
;
if
(
put_user
(
v
,
ip
+
1
))
return
-
EFAULT
;
return
0
;
return
0
;
case
EVIOCSKEYCODE
:
if
(
get_user
(
t
,
ip
)
||
get_user
(
v
,
ip
+
1
))
return
-
EFAULT
;
case
EVIOCSKEYCODE
:
if
(
get_user
(
t
,
ip
)
||
get_user
(
v
,
ip
+
1
))
return
-
EFAULT
;
return
dev
->
setkeycode
(
dev
,
t
,
v
);
return
dev
->
setkeycode
(
dev
,
t
,
v
);
case
EVIOCSFF
:
if
(
copy_from_user
(
&
effect
,
p
,
sizeof
(
effect
)))
return
-
EFAULT
;
case
EVIOCSFF
:
if
(
copy_from_user
(
&
effect
,
p
,
sizeof
(
effect
)))
return
-
EFAULT
;
error
=
input_ff_upload
(
dev
,
&
effect
,
file
);
error
=
input_ff_upload
(
dev
,
&
effect
,
file
);
if
(
put_user
(
effect
.
id
,
&
(((
struct
ff_effect
__user
*
)
p
)
->
id
)))
return
-
EFAULT
;
if
(
put_user
(
effect
.
id
,
&
(((
struct
ff_effect
__user
*
)
p
)
->
id
)))
return
-
EFAULT
;
return
error
;
return
error
;
case
EVIOCRMFF
:
return
input_ff_erase
(
dev
,
(
int
)(
unsigned
long
)
p
,
file
);
case
EVIOCRMFF
:
return
input_ff_erase
(
dev
,
(
int
)(
unsigned
long
)
p
,
file
);
case
EVIOCGEFFECTS
:
i
=
test_bit
(
EV_FF
,
dev
->
evbit
)
?
dev
->
ff
->
max_effects
:
0
;
if
(
put_user
(
i
,
ip
))
return
-
EFAULT
;
return
0
;
case
EVIOCGRAB
:
if
(
p
)
{
if
(
evdev
->
grab
)
return
-
EBUSY
;
if
(
input_grab_device
(
&
evdev
->
handle
))
return
-
EBUSY
;
evdev
->
grab
=
client
;
return
0
;
}
else
{
if
(
evdev
->
grab
!=
client
)
return
-
EINVAL
;
input_release_device
(
&
evdev
->
handle
);
evdev
->
grab
=
NULL
;
return
0
;
}
case
EVIOCGEFFECTS
:
i
=
test_bit
(
EV_FF
,
dev
->
evbit
)
?
dev
->
ff
->
max_effects
:
0
;
if
(
put_user
(
i
,
ip
))
return
-
EFAULT
;
return
0
;
case
EVIOCGRAB
:
if
(
p
)
return
evdev_grab
(
evdev
,
client
);
else
return
evdev_ungrab
(
evdev
,
client
);
default:
default:
if
(
_IOC_TYPE
(
cmd
)
!=
'E'
)
return
-
EINVAL
;
if
(
_IOC_TYPE
(
cmd
)
!=
'E'
)
return
-
EINVAL
;
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
;
int
len
;
unsigned
long
*
bits
;
int
len
;
switch
(
_IOC_NR
(
cmd
)
&
EV_MAX
)
{
case
0
:
bits
=
dev
->
evbit
;
len
=
EV_MAX
;
break
;
case
EV_KEY
:
bits
=
dev
->
keybit
;
len
=
KEY_MAX
;
break
;
case
EV_REL
:
bits
=
dev
->
relbit
;
len
=
REL_MAX
;
break
;
case
EV_ABS
:
bits
=
dev
->
absbit
;
len
=
ABS_MAX
;
break
;
case
EV_MSC
:
bits
=
dev
->
mscbit
;
len
=
MSC_MAX
;
break
;
case
EV_LED
:
bits
=
dev
->
ledbit
;
len
=
LED_MAX
;
break
;
case
EV_SND
:
bits
=
dev
->
sndbit
;
len
=
SND_MAX
;
break
;
case
EV_FF
:
bits
=
dev
->
ffbit
;
len
=
FF_MAX
;
break
;
case
EV_SW
:
bits
=
dev
->
swbit
;
len
=
SW_MAX
;
break
;
default:
return
-
EINVAL
;
}
return
bits_to_user
(
bits
,
len
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
}
switch
(
_IOC_NR
(
cmd
)
&
EV_MAX
)
{
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGKEY
(
0
)))
return
bits_to_user
(
dev
->
key
,
KEY_MAX
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
case
0
:
bits
=
dev
->
evbit
;
len
=
EV_MAX
;
break
;
case
EV_KEY
:
bits
=
dev
->
keybit
;
len
=
KEY_MAX
;
break
;
case
EV_REL
:
bits
=
dev
->
relbit
;
len
=
REL_MAX
;
break
;
case
EV_ABS
:
bits
=
dev
->
absbit
;
len
=
ABS_MAX
;
break
;
case
EV_MSC
:
bits
=
dev
->
mscbit
;
len
=
MSC_MAX
;
break
;
case
EV_LED
:
bits
=
dev
->
ledbit
;
len
=
LED_MAX
;
break
;
case
EV_SND
:
bits
=
dev
->
sndbit
;
len
=
SND_MAX
;
break
;
case
EV_FF
:
bits
=
dev
->
ffbit
;
len
=
FF_MAX
;
break
;
case
EV_SW
:
bits
=
dev
->
swbit
;
len
=
SW_MAX
;
break
;
default:
return
-
EINVAL
;
}
return
bits_to_user
(
bits
,
len
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
}
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGLED
(
0
)))
return
bits_to_user
(
dev
->
led
,
LED
_MAX
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGKEY
(
0
)))
return
bits_to_user
(
dev
->
key
,
KEY
_MAX
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGSN
D
(
0
)))
return
bits_to_user
(
dev
->
snd
,
SN
D_MAX
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGLE
D
(
0
)))
return
bits_to_user
(
dev
->
led
,
LE
D_MAX
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGSW
(
0
)))
return
bits_to_user
(
dev
->
sw
,
SW
_MAX
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGSND
(
0
)))
return
bits_to_user
(
dev
->
snd
,
SND
_MAX
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGNAME
(
0
)))
return
str_to_user
(
dev
->
name
,
_IOC_SIZE
(
cmd
),
p
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGSW
(
0
)))
return
bits_to_user
(
dev
->
sw
,
SW_MAX
,
_IOC_SIZE
(
cmd
),
p
,
compat_mode
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGPHYS
(
0
)))
return
str_to_user
(
dev
->
phys
,
_IOC_SIZE
(
cmd
),
p
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGNAME
(
0
)))
return
str_to_user
(
dev
->
name
,
_IOC_SIZE
(
cmd
),
p
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGUNIQ
(
0
)))
return
str_to_user
(
dev
->
uniq
,
_IOC_SIZE
(
cmd
),
p
);
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGPHYS
(
0
)))
return
str_to_user
(
dev
->
phys
,
_IOC_SIZE
(
cmd
),
p
);
if
((
_IOC_NR
(
cmd
)
&
~
ABS_MAX
)
==
_IOC_NR
(
EVIOCGABS
(
0
)))
{
if
(
_IOC_NR
(
cmd
)
==
_IOC_NR
(
EVIOCGUNIQ
(
0
)))
return
str_to_user
(
dev
->
uniq
,
_IOC_SIZE
(
cmd
),
p
);
t
=
_IOC_NR
(
cmd
)
&
ABS_MAX
;
if
((
_IOC_NR
(
cmd
)
&
~
ABS_MAX
)
==
_IOC_NR
(
EVIOCGABS
(
0
)))
{
abs
.
value
=
dev
->
abs
[
t
];
abs
.
minimum
=
dev
->
absmin
[
t
];
abs
.
maximum
=
dev
->
absmax
[
t
];
abs
.
fuzz
=
dev
->
absfuzz
[
t
];
abs
.
flat
=
dev
->
absflat
[
t
];
t
=
_IOC_NR
(
cmd
)
&
ABS_MAX
;
if
(
copy_to_user
(
p
,
&
abs
,
sizeof
(
struct
input_absinfo
)))
return
-
EFAULT
;
abs
.
value
=
dev
->
abs
[
t
];
abs
.
minimum
=
dev
->
absmin
[
t
];
abs
.
maximum
=
dev
->
absmax
[
t
];
abs
.
fuzz
=
dev
->
absfuzz
[
t
];
abs
.
flat
=
dev
->
absflat
[
t
];
return
0
;
}
if
(
copy_to_user
(
p
,
&
abs
,
sizeof
(
struct
input_absinfo
)))
return
-
EFAULT
;
return
0
;
}
if
(
_IOC_DIR
(
cmd
)
==
_IOC_WRITE
)
{
}
if
(
_IOC_DIR
(
cmd
)
==
_IOC_WRITE
)
{
if
((
_IOC_NR
(
cmd
)
&
~
ABS_MAX
)
==
_IOC_NR
(
EVIOCSABS
(
0
)))
{
if
((
_IOC_NR
(
cmd
)
&
~
ABS_MAX
)
==
_IOC_NR
(
EVIOCSABS
(
0
)))
{
t
=
_IOC_NR
(
cmd
)
&
ABS_MAX
;
t
=
_IOC_NR
(
cmd
)
&
ABS_MAX
;
if
(
copy_from_user
(
&
abs
,
p
,
sizeof
(
struct
input_absinfo
)))
return
-
EFAULT
;
if
(
copy_from_user
(
&
abs
,
p
,
sizeof
(
struct
input_absinfo
)))
return
-
EFAULT
;
dev
->
abs
[
t
]
=
abs
.
value
;
dev
->
absmin
[
t
]
=
abs
.
minimum
;
dev
->
absmax
[
t
]
=
abs
.
maximum
;
dev
->
absfuzz
[
t
]
=
abs
.
fuzz
;
dev
->
absflat
[
t
]
=
abs
.
flat
;
/*
* Take event lock to ensure that we are not
* changing device parameters in the middle
* of event.
*/
spin_lock_irq
(
&
dev
->
event_lock
);
return
0
;
}
dev
->
abs
[
t
]
=
abs
.
value
;
dev
->
absmin
[
t
]
=
abs
.
minimum
;
dev
->
absmax
[
t
]
=
abs
.
maximum
;
dev
->
absfuzz
[
t
]
=
abs
.
fuzz
;
dev
->
absflat
[
t
]
=
abs
.
flat
;
spin_unlock_irq
(
&
dev
->
event_lock
);
return
0
;
}
}
}
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
)
{
return
evdev_ioctl_handler
(
file
,
cmd
,
(
void
__user
*
)
arg
,
0
);
}
#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
);
}
#endif
static
const
struct
file_operations
evdev_fops
=
{
.
owner
=
THIS_MODULE
,
.
read
=
evdev_read
,
.
write
=
evdev_write
,
.
poll
=
evdev_poll
,
.
open
=
evdev_open
,
.
release
=
evdev_release
,
.
unlocked_ioctl
=
evdev_ioctl
,
.
owner
=
THIS_MODULE
,
.
read
=
evdev_read
,
.
write
=
evdev_write
,
.
poll
=
evdev_poll
,
.
open
=
evdev_open
,
.
release
=
evdev_release
,
.
unlocked_ioctl
=
evdev_ioctl
,
#ifdef CONFIG_COMPAT
.
compat_ioctl
=
evdev_ioctl_compat
,
.
compat_ioctl
=
evdev_ioctl_compat
,
#endif
.
fasync
=
evdev_fasync
,
.
flush
=
evdev_flush
.
fasync
=
evdev_fasync
,
.
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
,
const
struct
input_device_id
*
id
)
{
...
...
@@ -637,7 +872,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
int
minor
;
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
)
{
printk
(
KERN_ERR
"evdev: no more free evdev devices
\n
"
);
return
-
ENFILE
;
...
...
@@ -648,38 +886,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
return
-
ENOMEM
;
INIT_LIST_HEAD
(
&
evdev
->
client_list
);
spin_lock_init
(
&
evdev
->
client_lock
);
mutex_init
(
&
evdev
->
mutex
);
init_waitqueue_head
(
&
evdev
->
wait
);
snprintf
(
evdev
->
name
,
sizeof
(
evdev
->
name
),
"event%d"
,
minor
);
evdev
->
exist
=
1
;
evdev
->
minor
=
minor
;
evdev
->
handle
.
dev
=
dev
;
evdev
->
handle
.
name
=
evdev
->
name
;
evdev
->
handle
.
handler
=
handler
;
evdev
->
handle
.
private
=
evdev
;
snprintf
(
evdev
->
name
,
sizeof
(
evdev
->
name
),
"event%d"
,
minor
);
s
nprintf
(
evdev
->
dev
.
bus_id
,
sizeof
(
evdev
->
dev
.
bus_id
),
"event%d"
,
minor
);
s
trlcpy
(
evdev
->
dev
.
bus_id
,
evdev
->
name
,
sizeof
(
evdev
->
dev
.
bus_id
));
evdev
->
dev
.
devt
=
MKDEV
(
INPUT_MAJOR
,
EVDEV_MINOR_BASE
+
minor
);
evdev
->
dev
.
class
=
&
input_class
;
evdev
->
dev
.
parent
=
&
dev
->
dev
;
evdev
->
dev
.
devt
=
MKDEV
(
INPUT_MAJOR
,
EVDEV_MINOR_BASE
+
minor
);
evdev
->
dev
.
release
=
evdev_free
;
device_initialize
(
&
evdev
->
dev
);
evdev_table
[
minor
]
=
evdev
;
error
=
device_add
(
&
evdev
->
dev
);
error
=
input_register_handle
(
&
evdev
->
handle
);
if
(
error
)
goto
err_free_evdev
;
error
=
input_register_handle
(
&
evdev
->
handle
);
error
=
evdev_install_chrdev
(
evdev
);
if
(
error
)
goto
err_unregister_handle
;
error
=
device_add
(
&
evdev
->
dev
);
if
(
error
)
goto
err_
delete
_evdev
;
goto
err_
cleanup
_evdev
;
return
0
;
err_delete_evdev:
device_del
(
&
evdev
->
dev
);
err_cleanup_evdev:
evdev_cleanup
(
evdev
);
err_unregister_handle:
input_unregister_handle
(
&
evdev
->
handle
);
err_free_evdev:
put_device
(
&
evdev
->
dev
);
return
error
;
...
...
@@ -688,21 +932,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
static
void
evdev_disconnect
(
struct
input_handle
*
handle
)
{
struct
evdev
*
evdev
=
handle
->
private
;
struct
evdev_client
*
client
;
input_unregister_handle
(
handle
);
device_del
(
&
evdev
->
dev
);
evdev
->
exist
=
0
;
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
);
}
evdev_cleanup
(
evdev
);
input_unregister_handle
(
handle
);
put_device
(
&
evdev
->
dev
);
}
...
...
@@ -714,13 +947,13 @@ static const struct input_device_id evdev_ids[] = {
MODULE_DEVICE_TABLE
(
input
,
evdev_ids
);
static
struct
input_handler
evdev_handler
=
{
.
event
=
evdev_event
,
.
connect
=
evdev_connect
,
.
disconnect
=
evdev_disconnect
,
.
fops
=
&
evdev_fops
,
.
minor
=
EVDEV_MINOR_BASE
,
.
name
=
"evdev"
,
.
id_table
=
evdev_ids
,
.
event
=
evdev_event
,
.
connect
=
evdev_connect
,
.
disconnect
=
evdev_disconnect
,
.
fops
=
&
evdev_fops
,
.
minor
=
EVDEV_MINOR_BASE
,
.
name
=
"evdev"
,
.
id_table
=
evdev_ids
,
};
static
int
__init
evdev_init
(
void
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment