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
b126207c
Commit
b126207c
authored
Aug 30, 2007
by
Dmitry Torokhov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Input: joydev - implement proper locking
Signed-off-by:
Dmitry Torokhov
<
dtor@mail.ru
>
parent
464b2415
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
492 additions
and
251 deletions
+492
-251
drivers/input/joydev.c
drivers/input/joydev.c
+492
-251
No files found.
drivers/input/joydev.c
View file @
b126207c
...
...
@@ -43,6 +43,8 @@ struct joydev {
struct
input_handle
handle
;
wait_queue_head_t
wait
;
struct
list_head
client_list
;
spinlock_t
client_lock
;
/* protects client_list */
struct
mutex
mutex
;
struct
device
dev
;
struct
js_corr
corr
[
ABS_MAX
+
1
];
...
...
@@ -61,31 +63,61 @@ struct joydev_client {
int
head
;
int
tail
;
int
startup
;
spinlock_t
buffer_lock
;
/* protects access to buffer, head and tail */
struct
fasync_struct
*
fasync
;
struct
joydev
*
joydev
;
struct
list_head
node
;
};
static
struct
joydev
*
joydev_table
[
JOYDEV_MINORS
];
static
DEFINE_MUTEX
(
joydev_table_mutex
);
static
int
joydev_correct
(
int
value
,
struct
js_corr
*
corr
)
{
switch
(
corr
->
type
)
{
case
JS_CORR_NONE
:
break
;
case
JS_CORR_BROKEN
:
value
=
value
>
corr
->
coef
[
0
]
?
(
value
<
corr
->
coef
[
1
]
?
0
:
((
corr
->
coef
[
3
]
*
(
value
-
corr
->
coef
[
1
]))
>>
14
))
:
((
corr
->
coef
[
2
]
*
(
value
-
corr
->
coef
[
0
]))
>>
14
);
break
;
default:
return
0
;
case
JS_CORR_NONE
:
break
;
case
JS_CORR_BROKEN
:
value
=
value
>
corr
->
coef
[
0
]
?
(
value
<
corr
->
coef
[
1
]
?
0
:
((
corr
->
coef
[
3
]
*
(
value
-
corr
->
coef
[
1
]))
>>
14
))
:
((
corr
->
coef
[
2
]
*
(
value
-
corr
->
coef
[
0
]))
>>
14
);
break
;
default:
return
0
;
}
return
value
<
-
32767
?
-
32767
:
(
value
>
32767
?
32767
:
value
);
}
static
void
joydev_event
(
struct
input_handle
*
handle
,
unsigned
int
type
,
unsigned
int
code
,
int
value
)
static
void
joydev_pass_event
(
struct
joydev_client
*
client
,
struct
js_event
*
event
)
{
struct
joydev
*
joydev
=
client
->
joydev
;
/*
* IRQs already disabled, just acquire the lock
*/
spin_lock
(
&
client
->
buffer_lock
);
client
->
buffer
[
client
->
head
]
=
*
event
;
if
(
client
->
startup
==
joydev
->
nabs
+
joydev
->
nkey
)
{
client
->
head
++
;
client
->
head
&=
JOYDEV_BUFFER_SIZE
-
1
;
if
(
client
->
tail
==
client
->
head
)
client
->
startup
=
0
;
}
spin_unlock
(
&
client
->
buffer_lock
);
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_IN
);
}
static
void
joydev_event
(
struct
input_handle
*
handle
,
unsigned
int
type
,
unsigned
int
code
,
int
value
)
{
struct
joydev
*
joydev
=
handle
->
private
;
struct
joydev_client
*
client
;
...
...
@@ -93,39 +125,32 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
switch
(
type
)
{
case
EV_KEY
:
if
(
code
<
BTN_MISC
||
value
==
2
)
return
;
event
.
type
=
JS_EVENT_BUTTON
;
event
.
number
=
joydev
->
keymap
[
code
-
BTN_MISC
];
event
.
value
=
value
;
break
;
case
EV_ABS
:
event
.
type
=
JS_EVENT_AXIS
;
event
.
number
=
joydev
->
absmap
[
code
];
event
.
value
=
joydev_correct
(
value
,
joydev
->
corr
+
event
.
number
);
if
(
event
.
value
==
joydev
->
abs
[
event
.
number
])
return
;
joydev
->
abs
[
event
.
number
]
=
event
.
value
;
break
;
case
EV_KEY
:
if
(
code
<
BTN_MISC
||
value
==
2
)
return
;
event
.
type
=
JS_EVENT_BUTTON
;
event
.
number
=
joydev
->
keymap
[
code
-
BTN_MISC
];
event
.
value
=
value
;
break
;
default:
case
EV_ABS
:
event
.
type
=
JS_EVENT_AXIS
;
event
.
number
=
joydev
->
absmap
[
code
];
event
.
value
=
joydev_correct
(
value
,
&
joydev
->
corr
[
event
.
number
]);
if
(
event
.
value
==
joydev
->
abs
[
event
.
number
])
return
;
joydev
->
abs
[
event
.
number
]
=
event
.
value
;
break
;
default:
return
;
}
event
.
time
=
jiffies_to_msecs
(
jiffies
);
list_for_each_entry
(
client
,
&
joydev
->
client_list
,
node
)
{
memcpy
(
client
->
buffer
+
client
->
head
,
&
event
,
sizeof
(
struct
js_event
));
if
(
client
->
startup
==
joydev
->
nabs
+
joydev
->
nkey
)
if
(
client
->
tail
==
(
client
->
head
=
(
client
->
head
+
1
)
&
(
JOYDEV_BUFFER_SIZE
-
1
)))
client
->
startup
=
0
;
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_IN
);
}
list_for_each_entry_rcu
(
client
,
&
joydev
->
client_list
,
node
)
joydev_pass_event
(
client
,
&
event
);
wake_up_interruptible
(
&
joydev
->
wait
);
}
...
...
@@ -144,23 +169,85 @@ static void joydev_free(struct device *dev)
{
struct
joydev
*
joydev
=
container_of
(
dev
,
struct
joydev
,
dev
);
joydev_table
[
joydev
->
minor
]
=
NULL
;
kfree
(
joydev
);
}
static
void
joydev_attach_client
(
struct
joydev
*
joydev
,
struct
joydev_client
*
client
)
{
spin_lock
(
&
joydev
->
client_lock
);
list_add_tail_rcu
(
&
client
->
node
,
&
joydev
->
client_list
);
spin_unlock
(
&
joydev
->
client_lock
);
/*
* We don't use synchronize_rcu() here because read-side
* critical section is protected by a spinlock (dev->event_lock)
* instead of rcu_read_lock().
*/
synchronize_sched
();
}
static
void
joydev_detach_client
(
struct
joydev
*
joydev
,
struct
joydev_client
*
client
)
{
spin_lock
(
&
joydev
->
client_lock
);
list_del_rcu
(
&
client
->
node
);
spin_unlock
(
&
joydev
->
client_lock
);
synchronize_sched
();
}
static
int
joydev_open_device
(
struct
joydev
*
joydev
)
{
int
retval
;
retval
=
mutex_lock_interruptible
(
&
joydev
->
mutex
);
if
(
retval
)
return
retval
;
if
(
!
joydev
->
exist
)
retval
=
-
ENODEV
;
else
if
(
!
joydev
->
open
++
)
retval
=
input_open_device
(
&
joydev
->
handle
);
mutex_unlock
(
&
joydev
->
mutex
);
return
retval
;
}
static
void
joydev_close_device
(
struct
joydev
*
joydev
)
{
mutex_lock
(
&
joydev
->
mutex
);
if
(
joydev
->
exist
&&
!--
joydev
->
open
)
input_close_device
(
&
joydev
->
handle
);
mutex_unlock
(
&
joydev
->
mutex
);
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device.
*/
static
void
joydev_hangup
(
struct
joydev
*
joydev
)
{
struct
joydev_client
*
client
;
spin_lock
(
&
joydev
->
client_lock
);
list_for_each_entry
(
client
,
&
joydev
->
client_list
,
node
)
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_HUP
);
spin_unlock
(
&
joydev
->
client_lock
);
wake_up_interruptible
(
&
joydev
->
wait
);
}
static
int
joydev_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
joydev_client
*
client
=
file
->
private_data
;
struct
joydev
*
joydev
=
client
->
joydev
;
joydev_fasync
(
-
1
,
file
,
0
);
list_del
(
&
client
->
node
);
joydev_detach_client
(
joydev
,
client
);
kfree
(
client
);
if
(
!--
joydev
->
open
&&
joydev
->
exist
)
input_close_device
(
&
joydev
->
handle
);
joydev_close_device
(
joydev
);
put_device
(
&
joydev
->
dev
);
return
0
;
...
...
@@ -176,11 +263,16 @@ static int joydev_open(struct inode *inode, struct file *file)
if
(
i
>=
JOYDEV_MINORS
)
return
-
ENODEV
;
error
=
mutex_lock_interruptible
(
&
joydev_table_mutex
);
if
(
error
)
return
error
;
joydev
=
joydev_table
[
i
];
if
(
!
joydev
||
!
joydev
->
exist
)
return
-
ENODEV
;
if
(
joydev
)
get_device
(
&
joydev
->
dev
);
mutex_unlock
(
&
joydev_table_mutex
);
get_device
(
&
joydev
->
dev
);
if
(
!
joydev
)
return
-
ENODEV
;
client
=
kzalloc
(
sizeof
(
struct
joydev_client
),
GFP_KERNEL
);
if
(
!
client
)
{
...
...
@@ -188,37 +280,129 @@ static int joydev_open(struct inode *inode, struct file *file)
goto
err_put_joydev
;
}
spin_lock_init
(
&
client
->
buffer_lock
);
client
->
joydev
=
joydev
;
list_add_tail
(
&
client
->
node
,
&
joydev
->
client_lis
t
);
joydev_attach_client
(
joydev
,
clien
t
);
if
(
!
joydev
->
open
++
&&
joydev
->
exist
)
{
error
=
input_open_device
(
&
joydev
->
handle
);
if
(
error
)
goto
err_free_client
;
}
error
=
joydev_open_device
(
joydev
);
if
(
error
)
goto
err_free_client
;
file
->
private_data
=
client
;
return
0
;
err_free_client:
list_del
(
&
client
->
node
);
joydev_detach_client
(
joydev
,
client
);
kfree
(
client
);
err_put_joydev:
put_device
(
&
joydev
->
dev
);
return
error
;
}
static
ssize_t
joydev_write
(
struct
file
*
file
,
const
char
__user
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
static
int
joydev_generate_startup_event
(
struct
joydev_client
*
client
,
struct
input_dev
*
input
,
struct
js_event
*
event
)
{
return
-
EINVAL
;
struct
joydev
*
joydev
=
client
->
joydev
;
int
have_event
;
spin_lock_irq
(
&
client
->
buffer_lock
);
have_event
=
client
->
startup
<
joydev
->
nabs
+
joydev
->
nkey
;
if
(
have_event
)
{
event
->
time
=
jiffies_to_msecs
(
jiffies
);
if
(
client
->
startup
<
joydev
->
nkey
)
{
event
->
type
=
JS_EVENT_BUTTON
|
JS_EVENT_INIT
;
event
->
number
=
client
->
startup
;
event
->
value
=
!!
test_bit
(
joydev
->
keypam
[
event
->
number
],
input
->
key
);
}
else
{
event
->
type
=
JS_EVENT_AXIS
|
JS_EVENT_INIT
;
event
->
number
=
client
->
startup
-
joydev
->
nkey
;
event
->
value
=
joydev
->
abs
[
event
->
number
];
}
client
->
startup
++
;
}
spin_unlock_irq
(
&
client
->
buffer_lock
);
return
have_event
;
}
static
int
joydev_fetch_next_event
(
struct
joydev_client
*
client
,
struct
js_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
&=
JOYDEV_BUFFER_SIZE
-
1
;
}
spin_unlock_irq
(
&
client
->
buffer_lock
);
return
have_event
;
}
/*
* Old joystick interface
*/
static
ssize_t
joydev_0x_read
(
struct
joydev_client
*
client
,
struct
input_dev
*
input
,
char
__user
*
buf
)
{
struct
joydev
*
joydev
=
client
->
joydev
;
struct
JS_DATA_TYPE
data
;
int
i
;
spin_lock_irq
(
&
input
->
event_lock
);
/*
* Get device state
*/
for
(
data
.
buttons
=
i
=
0
;
i
<
32
&&
i
<
joydev
->
nkey
;
i
++
)
data
.
buttons
|=
test_bit
(
joydev
->
keypam
[
i
],
input
->
key
)
?
(
1
<<
i
)
:
0
;
data
.
x
=
(
joydev
->
abs
[
0
]
/
256
+
128
)
>>
joydev
->
glue
.
JS_CORR
.
x
;
data
.
y
=
(
joydev
->
abs
[
1
]
/
256
+
128
)
>>
joydev
->
glue
.
JS_CORR
.
y
;
/*
* Reset reader's event queue
*/
spin_lock
(
&
client
->
buffer_lock
);
client
->
startup
=
0
;
client
->
tail
=
client
->
head
;
spin_unlock
(
&
client
->
buffer_lock
);
spin_unlock_irq
(
&
input
->
event_lock
);
if
(
copy_to_user
(
buf
,
&
data
,
sizeof
(
struct
JS_DATA_TYPE
)))
return
-
EFAULT
;
return
sizeof
(
struct
JS_DATA_TYPE
);
}
static
ssize_t
joydev_read
(
struct
file
*
file
,
char
__user
*
buf
,
size_t
count
,
loff_t
*
ppos
)
static
inline
int
joydev_data_pending
(
struct
joydev_client
*
client
)
{
struct
joydev
*
joydev
=
client
->
joydev
;
return
client
->
startup
<
joydev
->
nabs
+
joydev
->
nkey
||
client
->
head
!=
client
->
tail
;
}
static
ssize_t
joydev_read
(
struct
file
*
file
,
char
__user
*
buf
,
size_t
count
,
loff_t
*
ppos
)
{
struct
joydev_client
*
client
=
file
->
private_data
;
struct
joydev
*
joydev
=
client
->
joydev
;
struct
input_dev
*
input
=
joydev
->
handle
.
dev
;
int
retval
=
0
;
struct
js_event
event
;
int
retval
;
if
(
!
joydev
->
exist
)
return
-
ENODEV
;
...
...
@@ -226,68 +410,35 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
if
(
count
<
sizeof
(
struct
js_event
))
return
-
EINVAL
;
if
(
count
==
sizeof
(
struct
JS_DATA_TYPE
))
{
struct
JS_DATA_TYPE
data
;
int
i
;
if
(
count
==
sizeof
(
struct
JS_DATA_TYPE
))
return
joydev_0x_read
(
client
,
input
,
buf
);
for
(
data
.
buttons
=
i
=
0
;
i
<
32
&&
i
<
joydev
->
nkey
;
i
++
)
data
.
buttons
|=
test_bit
(
joydev
->
keypam
[
i
],
input
->
key
)
?
(
1
<<
i
)
:
0
;
data
.
x
=
(
joydev
->
abs
[
0
]
/
256
+
128
)
>>
joydev
->
glue
.
JS_CORR
.
x
;
data
.
y
=
(
joydev
->
abs
[
1
]
/
256
+
128
)
>>
joydev
->
glue
.
JS_CORR
.
y
;
if
(
copy_to_user
(
buf
,
&
data
,
sizeof
(
struct
JS_DATA_TYPE
)))
return
-
EFAULT
;
client
->
startup
=
0
;
client
->
tail
=
client
->
head
;
return
sizeof
(
struct
JS_DATA_TYPE
);
}
if
(
client
->
startup
==
joydev
->
nabs
+
joydev
->
nkey
&&
client
->
head
==
client
->
tail
&&
(
file
->
f_flags
&
O_NONBLOCK
))
if
(
!
joydev_data_pending
(
client
)
&&
(
file
->
f_flags
&
O_NONBLOCK
))
return
-
EAGAIN
;
retval
=
wait_event_interruptible
(
joydev
->
wait
,
!
joydev
->
exist
||
client
->
startup
<
joydev
->
nabs
+
joydev
->
nkey
||
client
->
head
!=
client
->
tail
);
!
joydev
->
exist
||
joydev_data_pending
(
client
));
if
(
retval
)
return
retval
;
if
(
!
joydev
->
exist
)
return
-
ENODEV
;
while
(
client
->
startup
<
joydev
->
nabs
+
joydev
->
nkey
&&
retval
+
sizeof
(
struct
js_event
)
<=
count
)
{
struct
js_event
event
;
event
.
time
=
jiffies_to_msecs
(
jiffies
);
if
(
client
->
startup
<
joydev
->
nkey
)
{
event
.
type
=
JS_EVENT_BUTTON
|
JS_EVENT_INIT
;
event
.
number
=
client
->
startup
;
event
.
value
=
!!
test_bit
(
joydev
->
keypam
[
event
.
number
],
input
->
key
);
}
else
{
event
.
type
=
JS_EVENT_AXIS
|
JS_EVENT_INIT
;
event
.
number
=
client
->
startup
-
joydev
->
nkey
;
event
.
value
=
joydev
->
abs
[
event
.
number
];
}
while
(
retval
+
sizeof
(
struct
js_event
)
<=
count
&&
joydev_generate_startup_event
(
client
,
input
,
&
event
))
{
if
(
copy_to_user
(
buf
+
retval
,
&
event
,
sizeof
(
struct
js_event
)))
return
-
EFAULT
;
client
->
startup
++
;
retval
+=
sizeof
(
struct
js_event
);
}
while
(
client
->
head
!=
client
->
tail
&&
retval
+
sizeof
(
struct
js_event
)
<=
count
)
{
while
(
retval
+
sizeof
(
struct
js_event
)
<=
count
&&
joydev_fetch_next_event
(
client
,
&
event
))
{
if
(
copy_to_user
(
buf
+
retval
,
client
->
buffer
+
client
->
tail
,
sizeof
(
struct
js_event
)))
if
(
copy_to_user
(
buf
+
retval
,
&
event
,
sizeof
(
struct
js_event
)))
return
-
EFAULT
;
client
->
tail
=
(
client
->
tail
+
1
)
&
(
JOYDEV_BUFFER_SIZE
-
1
);
retval
+=
sizeof
(
struct
js_event
);
}
...
...
@@ -301,126 +452,144 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait)
struct
joydev
*
joydev
=
client
->
joydev
;
poll_wait
(
file
,
&
joydev
->
wait
,
wait
);
return
(
(
client
->
head
!=
client
->
tail
||
client
->
startup
<
joydev
->
nabs
+
joydev
->
nkey
)
?
(
POLLIN
|
POLLRDNORM
)
:
0
)
|
(
joydev
->
exist
?
0
:
(
POLLHUP
|
POLLERR
));
return
(
joydev_data_pending
(
client
)
?
(
POLLIN
|
POLLRDNORM
)
:
0
)
|
(
joydev
->
exist
?
0
:
(
POLLHUP
|
POLLERR
));
}
static
int
joydev_ioctl_common
(
struct
joydev
*
joydev
,
unsigned
int
cmd
,
void
__user
*
argp
)
static
int
joydev_ioctl_common
(
struct
joydev
*
joydev
,
unsigned
int
cmd
,
void
__user
*
argp
)
{
struct
input_dev
*
dev
=
joydev
->
handle
.
dev
;
int
i
,
j
;
switch
(
cmd
)
{
case
JS_SET_CAL
:
return
copy_from_user
(
&
joydev
->
glue
.
JS_CORR
,
argp
,
case
JS_SET_CAL
:
return
copy_from_user
(
&
joydev
->
glue
.
JS_CORR
,
argp
,
sizeof
(
joydev
->
glue
.
JS_CORR
))
?
-
EFAULT
:
0
;
case
JS_GET_CAL
:
return
copy_to_user
(
argp
,
&
joydev
->
glue
.
JS_CORR
,
case
JS_GET_CAL
:
return
copy_to_user
(
argp
,
&
joydev
->
glue
.
JS_CORR
,
sizeof
(
joydev
->
glue
.
JS_CORR
))
?
-
EFAULT
:
0
;
case
JS_SET_TIMEOUT
:
return
get_user
(
joydev
->
glue
.
JS_TIMEOUT
,
(
s32
__user
*
)
argp
);
case
JS_SET_TIMEOUT
:
return
get_user
(
joydev
->
glue
.
JS_TIMEOUT
,
(
s32
__user
*
)
argp
);
case
JS_GET_TIMEOUT
:
return
put_user
(
joydev
->
glue
.
JS_TIMEOUT
,
(
s32
__user
*
)
argp
);
case
JS_GET_TIMEOUT
:
return
put_user
(
joydev
->
glue
.
JS_TIMEOUT
,
(
s32
__user
*
)
argp
);
case
JSIOCGVERSION
:
return
put_user
(
JS_VERSION
,
(
__u32
__user
*
)
argp
);
case
JSIOCGVERSION
:
return
put_user
(
JS_VERSION
,
(
__u32
__user
*
)
argp
);
case
JSIOCGAXES
:
return
put_user
(
joydev
->
nabs
,
(
__u8
__user
*
)
argp
);
case
JSIOCGAXES
:
return
put_user
(
joydev
->
nabs
,
(
__u8
__user
*
)
argp
);
case
JSIOCGBUTTONS
:
return
put_user
(
joydev
->
nkey
,
(
__u8
__user
*
)
argp
);
case
JSIOCGBUTTONS
:
return
put_user
(
joydev
->
nkey
,
(
__u8
__user
*
)
argp
);
case
JSIOCSCORR
:
if
(
copy_from_user
(
joydev
->
corr
,
argp
,
sizeof
(
joydev
->
corr
[
0
])
*
joydev
->
nabs
))
return
-
EFAULT
;
for
(
i
=
0
;
i
<
joydev
->
nabs
;
i
++
)
{
j
=
joydev
->
abspam
[
i
];
joydev
->
abs
[
i
]
=
joydev_correct
(
dev
->
abs
[
j
],
joydev
->
corr
+
i
);
}
return
0
;
case
JSIOCSCORR
:
if
(
copy_from_user
(
joydev
->
corr
,
argp
,
sizeof
(
joydev
->
corr
[
0
])
*
joydev
->
nabs
))
return
-
EFAULT
;
case
JSIOCGCORR
:
return
copy_to_user
(
argp
,
joydev
->
corr
,
sizeof
(
joydev
->
corr
[
0
])
*
joydev
->
nabs
)
?
-
EFAULT
:
0
;
for
(
i
=
0
;
i
<
joydev
->
nabs
;
i
++
)
{
j
=
joydev
->
abspam
[
i
];
joydev
->
abs
[
i
]
=
joydev_correct
(
dev
->
abs
[
j
],
&
joydev
->
corr
[
i
]);
}
return
0
;
case
JSIOCSAXMAP
:
if
(
copy_from_user
(
joydev
->
abspam
,
argp
,
sizeof
(
__u8
)
*
(
ABS_MAX
+
1
)))
return
-
EFAULT
;
for
(
i
=
0
;
i
<
joydev
->
nabs
;
i
++
)
{
if
(
joydev
->
abspam
[
i
]
>
ABS_MAX
)
return
-
EINVAL
;
joydev
->
absmap
[
joydev
->
abspam
[
i
]]
=
i
;
}
return
0
;
case
JSIOCGAXMAP
:
return
copy_to_user
(
argp
,
joydev
->
abspam
,
sizeof
(
__u8
)
*
(
ABS_MAX
+
1
))
?
-
EFAULT
:
0
;
case
JSIOCSBTNMAP
:
if
(
copy_from_user
(
joydev
->
keypam
,
argp
,
sizeof
(
__u16
)
*
(
KEY_MAX
-
BTN_MISC
+
1
)))
case
JSIOCGCORR
:
return
copy_to_user
(
argp
,
joydev
->
corr
,
sizeof
(
joydev
->
corr
[
0
])
*
joydev
->
nabs
)
?
-
EFAULT
:
0
;
case
JSIOCSAXMAP
:
if
(
copy_from_user
(
joydev
->
abspam
,
argp
,
sizeof
(
__u8
)
*
(
ABS_MAX
+
1
)))
return
-
EFAULT
;
for
(
i
=
0
;
i
<
joydev
->
nabs
;
i
++
)
{
if
(
joydev
->
abspam
[
i
]
>
ABS_MAX
)
return
-
EINVAL
;
joydev
->
absmap
[
joydev
->
abspam
[
i
]]
=
i
;
}
return
0
;
case
JSIOCGAXMAP
:
return
copy_to_user
(
argp
,
joydev
->
abspam
,
sizeof
(
__u8
)
*
(
ABS_MAX
+
1
))
?
-
EFAULT
:
0
;
case
JSIOCSBTNMAP
:
if
(
copy_from_user
(
joydev
->
keypam
,
argp
,
sizeof
(
__u16
)
*
(
KEY_MAX
-
BTN_MISC
+
1
)))
return
-
EFAULT
;
for
(
i
=
0
;
i
<
joydev
->
nkey
;
i
++
)
{
if
(
joydev
->
keypam
[
i
]
>
KEY_MAX
||
joydev
->
keypam
[
i
]
<
BTN_MISC
)
return
-
EINVAL
;
joydev
->
keymap
[
joydev
->
keypam
[
i
]
-
BTN_MISC
]
=
i
;
}
return
0
;
case
JSIOCGBTNMAP
:
return
copy_to_user
(
argp
,
joydev
->
keypam
,
sizeof
(
__u16
)
*
(
KEY_MAX
-
BTN_MISC
+
1
))
?
-
EFAULT
:
0
;
default:
if
((
cmd
&
~
IOCSIZE_MASK
)
==
JSIOCGNAME
(
0
))
{
int
len
;
if
(
!
dev
->
name
)
return
0
;
len
=
strlen
(
dev
->
name
)
+
1
;
if
(
len
>
_IOC_SIZE
(
cmd
))
len
=
_IOC_SIZE
(
cmd
);
if
(
copy_to_user
(
argp
,
dev
->
name
,
len
))
return
-
EFAULT
;
for
(
i
=
0
;
i
<
joydev
->
nkey
;
i
++
)
{
if
(
joydev
->
keypam
[
i
]
>
KEY_MAX
||
joydev
->
keypam
[
i
]
<
BTN_MISC
)
return
-
EINVAL
;
joydev
->
keymap
[
joydev
->
keypam
[
i
]
-
BTN_MISC
]
=
i
;
}
return
0
;
case
JSIOCGBTNMAP
:
return
copy_to_user
(
argp
,
joydev
->
keypam
,
sizeof
(
__u16
)
*
(
KEY_MAX
-
BTN_MISC
+
1
))
?
-
EFAULT
:
0
;
default:
if
((
cmd
&
~
(
_IOC_SIZEMASK
<<
_IOC_SIZESHIFT
))
==
JSIOCGNAME
(
0
))
{
int
len
;
if
(
!
dev
->
name
)
return
0
;
len
=
strlen
(
dev
->
name
)
+
1
;
if
(
len
>
_IOC_SIZE
(
cmd
))
len
=
_IOC_SIZE
(
cmd
);
if
(
copy_to_user
(
argp
,
dev
->
name
,
len
))
return
-
EFAULT
;
return
len
;
}
return
len
;
}
}
return
-
EINVAL
;
}
#ifdef CONFIG_COMPAT
static
long
joydev_compat_ioctl
(
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
static
long
joydev_compat_ioctl
(
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
joydev_client
*
client
=
file
->
private_data
;
struct
joydev
*
joydev
=
client
->
joydev
;
void
__user
*
argp
=
(
void
__user
*
)
arg
;
s32
tmp32
;
struct
JS_DATA_SAVE_TYPE_32
ds32
;
int
err
;
int
retval
;
if
(
!
joydev
->
exist
)
return
-
ENODEV
;
retval
=
mutex_lock_interruptible
(
&
joydev
->
mutex
);
if
(
retval
)
return
retval
;
if
(
!
joydev
->
exist
)
{
retval
=
-
ENODEV
;
goto
out
;
}
switch
(
cmd
)
{
switch
(
cmd
)
{
case
JS_SET_TIMELIMIT
:
err
=
get_user
(
tmp32
,
(
s32
__user
*
)
arg
);
if
(
err
==
0
)
retval
=
get_user
(
tmp32
,
(
s32
__user
*
)
arg
);
if
(
retval
==
0
)
joydev
->
glue
.
JS_TIMELIMIT
=
tmp32
;
break
;
case
JS_GET_TIMELIMIT
:
tmp32
=
joydev
->
glue
.
JS_TIMELIMIT
;
err
=
put_user
(
tmp32
,
(
s32
__user
*
)
arg
);
retval
=
put_user
(
tmp32
,
(
s32
__user
*
)
arg
);
break
;
case
JS_SET_ALL
:
err
=
copy_from_user
(
&
ds32
,
argp
,
sizeof
(
ds32
))
?
-
EFAULT
:
0
;
if
(
err
==
0
)
{
retval
=
copy_from_user
(
&
ds32
,
argp
,
sizeof
(
ds32
))
?
-
EFAULT
:
0
;
if
(
retval
==
0
)
{
joydev
->
glue
.
JS_TIMEOUT
=
ds32
.
JS_TIMEOUT
;
joydev
->
glue
.
BUSY
=
ds32
.
BUSY
;
joydev
->
glue
.
JS_EXPIRETIME
=
ds32
.
JS_EXPIRETIME
;
...
...
@@ -438,55 +607,119 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
ds32
.
JS_SAVE
=
joydev
->
glue
.
JS_SAVE
;
ds32
.
JS_CORR
=
joydev
->
glue
.
JS_CORR
;
err
=
copy_to_user
(
argp
,
&
ds32
,
sizeof
(
ds32
))
?
-
EFAULT
:
0
;
retval
=
copy_to_user
(
argp
,
&
ds32
,
sizeof
(
ds32
))
?
-
EFAULT
:
0
;
break
;
default:
err
=
joydev_ioctl_common
(
joydev
,
cmd
,
argp
);
retval
=
joydev_ioctl_common
(
joydev
,
cmd
,
argp
);
break
;
}
return
err
;
out:
mutex_unlock
(
&
joydev
->
mutex
);
return
retval
;
}
#endif
/* CONFIG_COMPAT */
static
int
joydev_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
static
long
joydev_ioctl
(
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
joydev_client
*
client
=
file
->
private_data
;
struct
joydev
*
joydev
=
client
->
joydev
;
void
__user
*
argp
=
(
void
__user
*
)
arg
;
int
retval
;
if
(
!
joydev
->
exist
)
return
-
ENODEV
;
retval
=
mutex_lock_interruptible
(
&
joydev
->
mutex
);
if
(
retval
)
return
retval
;
switch
(
cmd
)
{
case
JS_SET_TIMELIMIT
:
return
get_user
(
joydev
->
glue
.
JS_TIMELIMIT
,
(
long
__user
*
)
arg
);
case
JS_GET_TIMELIMIT
:
return
put_user
(
joydev
->
glue
.
JS_TIMELIMIT
,
(
long
__user
*
)
arg
);
case
JS_SET_ALL
:
return
copy_from_user
(
&
joydev
->
glue
,
argp
,
sizeof
(
joydev
->
glue
))
?
-
EFAULT
:
0
;
case
JS_GET_ALL
:
return
copy_to_user
(
argp
,
&
joydev
->
glue
,
sizeof
(
joydev
->
glue
))
?
-
EFAULT
:
0
;
default:
return
joydev_ioctl_common
(
joydev
,
cmd
,
argp
);
if
(
!
joydev
->
exist
)
{
retval
=
-
ENODEV
;
goto
out
;
}
switch
(
cmd
)
{
case
JS_SET_TIMELIMIT
:
retval
=
get_user
(
joydev
->
glue
.
JS_TIMELIMIT
,
(
long
__user
*
)
arg
);
break
;
case
JS_GET_TIMELIMIT
:
retval
=
put_user
(
joydev
->
glue
.
JS_TIMELIMIT
,
(
long
__user
*
)
arg
);
break
;
case
JS_SET_ALL
:
retval
=
copy_from_user
(
&
joydev
->
glue
,
argp
,
sizeof
(
joydev
->
glue
))
?
-
EFAULT
:
0
;
break
;
case
JS_GET_ALL
:
retval
=
copy_to_user
(
argp
,
&
joydev
->
glue
,
sizeof
(
joydev
->
glue
))
?
-
EFAULT
:
0
;
break
;
default:
retval
=
joydev_ioctl_common
(
joydev
,
cmd
,
argp
);
break
;
}
out:
mutex_unlock
(
&
joydev
->
mutex
);
return
retval
;
}
static
const
struct
file_operations
joydev_fops
=
{
.
owner
=
THIS_MODULE
,
.
read
=
joydev_read
,
.
write
=
joydev_write
,
.
poll
=
joydev_poll
,
.
open
=
joydev_open
,
.
release
=
joydev_release
,
.
ioctl
=
joydev_ioctl
,
.
owner
=
THIS_MODULE
,
.
read
=
joydev_read
,
.
poll
=
joydev_poll
,
.
open
=
joydev_open
,
.
release
=
joydev_release
,
.
unlocked_ioctl
=
joydev_ioctl
,
#ifdef CONFIG_COMPAT
.
compat_ioctl
=
joydev_compat_ioctl
,
.
compat_ioctl
=
joydev_compat_ioctl
,
#endif
.
fasync
=
joydev_fasync
,
.
fasync
=
joydev_fasync
,
};
static
int
joydev_install_chrdev
(
struct
joydev
*
joydev
)
{
joydev_table
[
joydev
->
minor
]
=
joydev
;
return
0
;
}
static
void
joydev_remove_chrdev
(
struct
joydev
*
joydev
)
{
mutex_lock
(
&
joydev_table_mutex
);
joydev_table
[
joydev
->
minor
]
=
NULL
;
mutex_unlock
(
&
joydev_table_mutex
);
}
/*
* Mark device non-existant. 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
joydev_mark_dead
(
struct
joydev
*
joydev
)
{
mutex_lock
(
&
joydev
->
mutex
);
joydev
->
exist
=
0
;
mutex_unlock
(
&
joydev
->
mutex
);
}
static
void
joydev_cleanup
(
struct
joydev
*
joydev
)
{
struct
input_handle
*
handle
=
&
joydev
->
handle
;
joydev_mark_dead
(
joydev
);
joydev_hangup
(
joydev
);
joydev_remove_chrdev
(
joydev
);
/* joydev is marked dead so noone else accesses joydev->open */
if
(
joydev
->
open
)
input_close_device
(
handle
);
}
static
int
joydev_connect
(
struct
input_handler
*
handler
,
struct
input_dev
*
dev
,
const
struct
input_device_id
*
id
)
{
...
...
@@ -494,7 +727,10 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
int
i
,
j
,
t
,
minor
;
int
error
;
for
(
minor
=
0
;
minor
<
JOYDEV_MINORS
&&
joydev_table
[
minor
];
minor
++
);
for
(
minor
=
0
;
minor
<
JOYDEV_MINORS
;
minor
++
)
if
(
!
joydev_table
[
minor
])
break
;
if
(
minor
==
JOYDEV_MINORS
)
{
printk
(
KERN_ERR
"joydev: no more free joydev devices
\n
"
);
return
-
ENFILE
;
...
...
@@ -505,15 +741,19 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
return
-
ENOMEM
;
INIT_LIST_HEAD
(
&
joydev
->
client_list
);
spin_lock_init
(
&
joydev
->
client_lock
);
mutex_init
(
&
joydev
->
mutex
);
init_waitqueue_head
(
&
joydev
->
wait
);
snprintf
(
joydev
->
name
,
sizeof
(
joydev
->
name
),
"js%d"
,
minor
);
joydev
->
exist
=
1
;
joydev
->
minor
=
minor
;
joydev
->
exist
=
1
;
joydev
->
handle
.
dev
=
dev
;
joydev
->
handle
.
name
=
joydev
->
name
;
joydev
->
handle
.
handler
=
handler
;
joydev
->
handle
.
private
=
joydev
;
snprintf
(
joydev
->
name
,
sizeof
(
joydev
->
name
),
"js%d"
,
minor
);
for
(
i
=
0
;
i
<
ABS_MAX
+
1
;
i
++
)
if
(
test_bit
(
i
,
dev
->
absbit
))
{
...
...
@@ -545,67 +785,65 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
}
joydev
->
corr
[
i
].
type
=
JS_CORR_BROKEN
;
joydev
->
corr
[
i
].
prec
=
dev
->
absfuzz
[
j
];
joydev
->
corr
[
i
].
coef
[
0
]
=
(
dev
->
absmax
[
j
]
+
dev
->
absmin
[
j
])
/
2
-
dev
->
absflat
[
j
];
joydev
->
corr
[
i
].
coef
[
1
]
=
(
dev
->
absmax
[
j
]
+
dev
->
absmin
[
j
])
/
2
+
dev
->
absflat
[
j
];
if
(
!
(
t
=
((
dev
->
absmax
[
j
]
-
dev
->
absmin
[
j
])
/
2
-
2
*
dev
->
absflat
[
j
])))
continue
;
joydev
->
corr
[
i
].
coef
[
2
]
=
(
1
<<
29
)
/
t
;
joydev
->
corr
[
i
].
coef
[
3
]
=
(
1
<<
29
)
/
t
;
joydev
->
abs
[
i
]
=
joydev_correct
(
dev
->
abs
[
j
],
joydev
->
corr
+
i
);
joydev
->
corr
[
i
].
coef
[
0
]
=
(
dev
->
absmax
[
j
]
+
dev
->
absmin
[
j
])
/
2
-
dev
->
absflat
[
j
];
joydev
->
corr
[
i
].
coef
[
1
]
=
(
dev
->
absmax
[
j
]
+
dev
->
absmin
[
j
])
/
2
+
dev
->
absflat
[
j
];
t
=
(
dev
->
absmax
[
j
]
-
dev
->
absmin
[
j
])
/
2
-
2
*
dev
->
absflat
[
j
];
if
(
t
)
{
joydev
->
corr
[
i
].
coef
[
2
]
=
(
1
<<
29
)
/
t
;
joydev
->
corr
[
i
].
coef
[
3
]
=
(
1
<<
29
)
/
t
;
joydev
->
abs
[
i
]
=
joydev_correct
(
dev
->
abs
[
j
],
joydev
->
corr
+
i
);
}
}
s
nprintf
(
joydev
->
dev
.
bus_id
,
sizeof
(
joydev
->
dev
.
bus_id
),
"js%d"
,
minor
);
s
trlcpy
(
joydev
->
dev
.
bus_id
,
joydev
->
name
,
sizeof
(
joydev
->
dev
.
bus_id
));
joydev
->
dev
.
devt
=
MKDEV
(
INPUT_MAJOR
,
JOYDEV_MINOR_BASE
+
minor
);
joydev
->
dev
.
class
=
&
input_class
;
joydev
->
dev
.
parent
=
&
dev
->
dev
;
joydev
->
dev
.
devt
=
MKDEV
(
INPUT_MAJOR
,
JOYDEV_MINOR_BASE
+
minor
);
joydev
->
dev
.
release
=
joydev_free
;
device_initialize
(
&
joydev
->
dev
);
joydev_table
[
minor
]
=
joydev
;
error
=
device_add
(
&
joydev
->
dev
);
error
=
input_register_handle
(
&
joydev
->
handle
);
if
(
error
)
goto
err_free_joydev
;
error
=
input_register_handle
(
&
joydev
->
handle
);
error
=
joydev_install_chrdev
(
joydev
);
if
(
error
)
goto
err_delete_joydev
;
goto
err_unregister_handle
;
error
=
device_add
(
&
joydev
->
dev
);
if
(
error
)
goto
err_cleanup_joydev
;
return
0
;
err_delete_joydev:
device_del
(
&
joydev
->
dev
);
err_cleanup_joydev:
joydev_cleanup
(
joydev
);
err_unregister_handle:
input_unregister_handle
(
&
joydev
->
handle
);
err_free_joydev:
put_device
(
&
joydev
->
dev
);
return
error
;
}
static
void
joydev_disconnect
(
struct
input_handle
*
handle
)
{
struct
joydev
*
joydev
=
handle
->
private
;
struct
joydev_client
*
client
;
input_unregister_handle
(
handle
);
device_del
(
&
joydev
->
dev
);
joydev
->
exist
=
0
;
if
(
joydev
->
open
)
{
input_close_device
(
handle
);
list_for_each_entry
(
client
,
&
joydev
->
client_list
,
node
)
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_HUP
);
wake_up_interruptible
(
&
joydev
->
wait
);
}
joydev_cleanup
(
joydev
);
input_unregister_handle
(
handle
);
put_device
(
&
joydev
->
dev
);
}
static
const
struct
input_device_id
joydev_blacklist
[]
=
{
{
.
flags
=
INPUT_DEVICE_ID_MATCH_EVBIT
|
INPUT_DEVICE_ID_MATCH_KEYBIT
,
.
flags
=
INPUT_DEVICE_ID_MATCH_EVBIT
|
INPUT_DEVICE_ID_MATCH_KEYBIT
,
.
evbit
=
{
BIT
(
EV_KEY
)
},
.
keybit
=
{
[
LONG
(
BTN_TOUCH
)]
=
BIT
(
BTN_TOUCH
)
},
},
/* Avoid itouchpads, touchscreens and tablets */
...
...
@@ -614,17 +852,20 @@ static const struct input_device_id joydev_blacklist[] = {
static
const
struct
input_device_id
joydev_ids
[]
=
{
{
.
flags
=
INPUT_DEVICE_ID_MATCH_EVBIT
|
INPUT_DEVICE_ID_MATCH_ABSBIT
,
.
flags
=
INPUT_DEVICE_ID_MATCH_EVBIT
|
INPUT_DEVICE_ID_MATCH_ABSBIT
,
.
evbit
=
{
BIT
(
EV_ABS
)
},
.
absbit
=
{
BIT
(
ABS_X
)
},
},
{
.
flags
=
INPUT_DEVICE_ID_MATCH_EVBIT
|
INPUT_DEVICE_ID_MATCH_ABSBIT
,
.
flags
=
INPUT_DEVICE_ID_MATCH_EVBIT
|
INPUT_DEVICE_ID_MATCH_ABSBIT
,
.
evbit
=
{
BIT
(
EV_ABS
)
},
.
absbit
=
{
BIT
(
ABS_WHEEL
)
},
},
{
.
flags
=
INPUT_DEVICE_ID_MATCH_EVBIT
|
INPUT_DEVICE_ID_MATCH_ABSBIT
,
.
flags
=
INPUT_DEVICE_ID_MATCH_EVBIT
|
INPUT_DEVICE_ID_MATCH_ABSBIT
,
.
evbit
=
{
BIT
(
EV_ABS
)
},
.
absbit
=
{
BIT
(
ABS_THROTTLE
)
},
},
...
...
@@ -634,14 +875,14 @@ static const struct input_device_id joydev_ids[] = {
MODULE_DEVICE_TABLE
(
input
,
joydev_ids
);
static
struct
input_handler
joydev_handler
=
{
.
event
=
joydev_event
,
.
connect
=
joydev_connect
,
.
disconnect
=
joydev_disconnect
,
.
fops
=
&
joydev_fops
,
.
minor
=
JOYDEV_MINOR_BASE
,
.
name
=
"joydev"
,
.
id_table
=
joydev_ids
,
.
blacklist
=
joydev_blacklist
,
.
event
=
joydev_event
,
.
connect
=
joydev_connect
,
.
disconnect
=
joydev_disconnect
,
.
fops
=
&
joydev_fops
,
.
minor
=
JOYDEV_MINOR_BASE
,
.
name
=
"joydev"
,
.
id_table
=
joydev_ids
,
.
blacklist
=
joydev_blacklist
,
};
static
int
__init
joydev_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