2005-04-17 02:20:36 +04:00
/*
* User level driver support for input subsystem
*
* Heavily based on evdev . c by Vojtech Pavlik
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Author : Aristeu Sergio Rozanski Filho < aris @ cathedrallabs . org >
*
* Changes / Revisions :
* 0.2 16 / 10 / 2004 ( Micah Dowty < micah @ navi . cx > )
* - added force feedback support
* - added UI_SET_PHYS
* 0.1 20 / 06 / 2002
* - first public version
*/
# include <linux/poll.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/smp_lock.h>
# include <linux/fs.h>
# include <linux/miscdevice.h>
# include <linux/uinput.h>
static int uinput_dev_event ( struct input_dev * dev , unsigned int type , unsigned int code , int value )
{
struct uinput_device * udev ;
udev = dev - > private ;
udev - > buff [ udev - > head ] . type = type ;
udev - > buff [ udev - > head ] . code = code ;
udev - > buff [ udev - > head ] . value = value ;
do_gettimeofday ( & udev - > buff [ udev - > head ] . time ) ;
udev - > head = ( udev - > head + 1 ) % UINPUT_BUFFER_SIZE ;
wake_up_interruptible ( & udev - > waitq ) ;
return 0 ;
}
2005-06-30 09:48:14 +04:00
static int uinput_request_alloc_id ( struct uinput_device * udev , struct uinput_request * request )
2005-04-17 02:20:36 +04:00
{
/* Atomically allocate an ID for the given request. Returns 0 on success. */
int id ;
2005-06-30 09:47:50 +04:00
int err = - 1 ;
2005-04-17 02:20:36 +04:00
2005-06-30 09:48:14 +04:00
spin_lock ( & udev - > requests_lock ) ;
2005-06-30 09:47:50 +04:00
for ( id = 0 ; id < UINPUT_NUM_REQUESTS ; id + + )
2005-04-17 02:20:36 +04:00
if ( ! udev - > requests [ id ] ) {
request - > id = id ;
2005-06-30 09:48:14 +04:00
udev - > requests [ id ] = request ;
2005-06-30 09:47:50 +04:00
err = 0 ;
break ;
2005-04-17 02:20:36 +04:00
}
2005-06-30 09:47:50 +04:00
2005-06-30 09:48:14 +04:00
spin_unlock ( & udev - > requests_lock ) ;
2005-06-30 09:47:50 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static struct uinput_request * uinput_request_find ( struct uinput_device * udev , int id )
{
/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
if ( id > = UINPUT_NUM_REQUESTS | | id < 0 )
return NULL ;
return udev - > requests [ id ] ;
}
2005-06-30 09:48:14 +04:00
static inline int uinput_request_reserve_slot ( struct uinput_device * udev , struct uinput_request * request )
2005-04-17 02:20:36 +04:00
{
2005-06-30 09:48:14 +04:00
/* Allocate slot. If none are available right away, wait. */
return wait_event_interruptible ( udev - > requests_waitq ,
! uinput_request_alloc_id ( udev , request ) ) ;
}
2005-04-17 02:20:36 +04:00
2005-06-30 09:48:14 +04:00
static void uinput_request_done ( struct uinput_device * udev , struct uinput_request * request )
{
/* Mark slot as available */
udev - > requests [ request - > id ] = NULL ;
wake_up_interruptible ( & udev - > requests_waitq ) ;
2005-10-18 03:43:32 +04:00
complete ( & request - > done ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-30 09:48:14 +04:00
static int uinput_request_submit ( struct input_dev * dev , struct uinput_request * request )
2005-04-17 02:20:36 +04:00
{
int retval ;
/* Tell our userspace app about this new request by queueing an input event */
uinput_dev_event ( dev , EV_UINPUT , request - > code , request - > id ) ;
/* Wait for the request to complete */
2005-06-30 09:48:14 +04:00
retval = wait_for_completion_interruptible ( & request - > done ) ;
if ( ! retval )
retval = request - > retval ;
2005-04-17 02:20:36 +04:00
2005-06-30 09:48:14 +04:00
return retval ;
2005-04-17 02:20:36 +04:00
}
static int uinput_dev_upload_effect ( struct input_dev * dev , struct ff_effect * effect )
{
struct uinput_request request ;
2005-06-30 09:48:14 +04:00
int retval ;
2005-04-17 02:20:36 +04:00
if ( ! test_bit ( EV_FF , dev - > evbit ) )
return - ENOSYS ;
2005-06-30 09:48:14 +04:00
request . id = - 1 ;
init_completion ( & request . done ) ;
request . code = UI_FF_UPLOAD ;
2005-04-17 02:20:36 +04:00
request . u . effect = effect ;
2005-06-30 09:48:14 +04:00
retval = uinput_request_reserve_slot ( dev - > private , & request ) ;
if ( ! retval )
retval = uinput_request_submit ( dev , & request ) ;
return retval ;
2005-04-17 02:20:36 +04:00
}
static int uinput_dev_erase_effect ( struct input_dev * dev , int effect_id )
{
struct uinput_request request ;
2005-06-30 09:48:14 +04:00
int retval ;
2005-04-17 02:20:36 +04:00
if ( ! test_bit ( EV_FF , dev - > evbit ) )
return - ENOSYS ;
2005-06-30 09:48:14 +04:00
request . id = - 1 ;
init_completion ( & request . done ) ;
request . code = UI_FF_ERASE ;
2005-04-17 02:20:36 +04:00
request . u . effect_id = effect_id ;
2005-06-30 09:48:14 +04:00
retval = uinput_request_reserve_slot ( dev - > private , & request ) ;
if ( ! retval )
retval = uinput_request_submit ( dev , & request ) ;
return retval ;
2005-04-17 02:20:36 +04:00
}
2005-11-20 08:51:22 +03:00
static void uinput_destroy_device ( struct uinput_device * udev )
2005-04-17 02:20:36 +04:00
{
2005-11-20 08:51:22 +03:00
const char * name , * phys ;
if ( udev - > dev ) {
name = udev - > dev - > name ;
phys = udev - > dev - > phys ;
if ( udev - > state = = UIST_CREATED )
input_unregister_device ( udev - > dev ) ;
else
input_free_device ( udev - > dev ) ;
kfree ( name ) ;
kfree ( phys ) ;
udev - > dev = NULL ;
2005-04-17 02:20:36 +04:00
}
2005-11-20 08:51:22 +03:00
udev - > state = UIST_NEW_DEVICE ;
2005-04-17 02:20:36 +04:00
}
2005-11-20 08:51:22 +03:00
static int uinput_create_device ( struct uinput_device * udev )
2005-04-17 02:20:36 +04:00
{
2005-11-20 08:51:22 +03:00
int error ;
if ( udev - > state ! = UIST_SETUP_COMPLETE ) {
printk ( KERN_DEBUG " %s: write device info first \n " , UINPUT_NAME ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2005-11-20 08:51:22 +03:00
error = input_register_device ( udev - > dev ) ;
if ( error ) {
uinput_destroy_device ( udev ) ;
return error ;
}
2005-04-17 02:20:36 +04:00
2005-11-20 08:51:22 +03:00
udev - > state = UIST_CREATED ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int uinput_open ( struct inode * inode , struct file * file )
{
2005-11-20 08:51:22 +03:00
struct uinput_device * newdev ;
2005-04-17 02:20:36 +04:00
2005-11-20 08:51:22 +03:00
newdev = kzalloc ( sizeof ( struct uinput_device ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! newdev )
2005-11-20 08:51:22 +03:00
return - ENOMEM ;
init_MUTEX ( & newdev - > sem ) ;
2005-06-30 09:48:14 +04:00
spin_lock_init ( & newdev - > requests_lock ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & newdev - > requests_waitq ) ;
2005-11-20 08:51:22 +03:00
init_waitqueue_head ( & newdev - > waitq ) ;
newdev - > state = UIST_NEW_DEVICE ;
2005-04-17 02:20:36 +04:00
file - > private_data = newdev ;
return 0 ;
}
static int uinput_validate_absbits ( struct input_dev * dev )
{
unsigned int cnt ;
int retval = 0 ;
for ( cnt = 0 ; cnt < ABS_MAX + 1 ; cnt + + ) {
if ( ! test_bit ( cnt , dev - > absbit ) )
continue ;
if ( ( dev - > absmax [ cnt ] < = dev - > absmin [ cnt ] ) ) {
printk ( KERN_DEBUG
" %s: invalid abs[%02x] min:%d max:%d \n " ,
UINPUT_NAME , cnt ,
dev - > absmin [ cnt ] , dev - > absmax [ cnt ] ) ;
retval = - EINVAL ;
break ;
}
if ( dev - > absflat [ cnt ] > ( dev - > absmax [ cnt ] - dev - > absmin [ cnt ] ) ) {
printk ( KERN_DEBUG
" %s: absflat[%02x] out of range: %d "
" (min:%d/max:%d) \n " ,
UINPUT_NAME , cnt , dev - > absflat [ cnt ] ,
dev - > absmin [ cnt ] , dev - > absmax [ cnt ] ) ;
retval = - EINVAL ;
break ;
}
}
return retval ;
}
2005-11-20 08:51:22 +03:00
static int uinput_allocate_device ( struct uinput_device * udev )
{
udev - > dev = input_allocate_device ( ) ;
if ( ! udev - > dev )
return - ENOMEM ;
udev - > dev - > event = uinput_dev_event ;
udev - > dev - > upload_effect = uinput_dev_upload_effect ;
udev - > dev - > erase_effect = uinput_dev_erase_effect ;
udev - > dev - > private = udev ;
return 0 ;
}
static int uinput_setup_device ( struct uinput_device * udev , const char __user * buffer , size_t count )
2005-04-17 02:20:36 +04:00
{
struct uinput_user_dev * user_dev ;
struct input_dev * dev ;
2005-06-30 09:50:38 +04:00
char * name ;
2005-06-30 09:47:50 +04:00
int size ;
int retval ;
2005-04-17 02:20:36 +04:00
2005-11-20 08:51:22 +03:00
if ( count ! = sizeof ( struct uinput_user_dev ) )
return - EINVAL ;
if ( ! udev - > dev ) {
retval = uinput_allocate_device ( udev ) ;
if ( retval )
return retval ;
}
2005-04-17 02:20:36 +04:00
dev = udev - > dev ;
2005-06-30 09:47:50 +04:00
user_dev = kmalloc ( sizeof ( struct uinput_user_dev ) , GFP_KERNEL ) ;
2005-11-20 08:51:22 +03:00
if ( ! user_dev )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( user_dev , buffer , sizeof ( struct uinput_user_dev ) ) ) {
retval = - EFAULT ;
goto exit ;
}
size = strnlen ( user_dev - > name , UINPUT_MAX_NAME_SIZE ) + 1 ;
2005-11-20 08:51:22 +03:00
if ( ! size ) {
retval = - EINVAL ;
goto exit ;
}
kfree ( dev - > name ) ;
2005-06-30 09:50:38 +04:00
dev - > name = name = kmalloc ( size , GFP_KERNEL ) ;
if ( ! name ) {
2005-04-17 02:20:36 +04:00
retval = - ENOMEM ;
goto exit ;
}
2005-06-30 09:50:38 +04:00
strlcpy ( name , user_dev - > name , size ) ;
2005-04-17 02:20:36 +04:00
dev - > id . bustype = user_dev - > id . bustype ;
dev - > id . vendor = user_dev - > id . vendor ;
dev - > id . product = user_dev - > id . product ;
dev - > id . version = user_dev - > id . version ;
dev - > ff_effects_max = user_dev - > ff_effects_max ;
size = sizeof ( int ) * ( ABS_MAX + 1 ) ;
memcpy ( dev - > absmax , user_dev - > absmax , size ) ;
memcpy ( dev - > absmin , user_dev - > absmin , size ) ;
memcpy ( dev - > absfuzz , user_dev - > absfuzz , size ) ;
memcpy ( dev - > absflat , user_dev - > absflat , size ) ;
/* check if absmin/absmax/absfuzz/absflat are filled as
* told in Documentation / input / input - programming . txt */
if ( test_bit ( EV_ABS , dev - > evbit ) ) {
2005-11-20 08:51:22 +03:00
retval = uinput_validate_absbits ( dev ) ;
if ( retval < 0 )
goto exit ;
2005-04-17 02:20:36 +04:00
}
2005-11-20 08:51:22 +03:00
udev - > state = UIST_SETUP_COMPLETE ;
retval = count ;
exit :
2005-04-17 02:20:36 +04:00
kfree ( user_dev ) ;
return retval ;
}
2005-11-20 08:51:22 +03:00
static inline ssize_t uinput_inject_event ( struct uinput_device * udev , const char __user * buffer , size_t count )
{
struct input_event ev ;
if ( count ! = sizeof ( struct input_event ) )
return - EINVAL ;
if ( copy_from_user ( & ev , buffer , sizeof ( struct input_event ) ) )
return - EFAULT ;
input_event ( udev - > dev , ev . type , ev . code , ev . value ) ;
return sizeof ( struct input_event ) ;
}
2005-04-17 02:20:36 +04:00
static ssize_t uinput_write ( struct file * file , const char __user * buffer , size_t count , loff_t * ppos )
{
struct uinput_device * udev = file - > private_data ;
2005-11-20 08:51:22 +03:00
int retval ;
2005-04-17 02:20:36 +04:00
2005-11-20 08:51:22 +03:00
retval = down_interruptible ( & udev - > sem ) ;
if ( retval )
return retval ;
retval = udev - > state = = UIST_CREATED ?
uinput_inject_event ( udev , buffer , count ) :
uinput_setup_device ( udev , buffer , count ) ;
2005-04-17 02:20:36 +04:00
2005-11-20 08:51:22 +03:00
up ( & udev - > sem ) ;
2005-04-17 02:20:36 +04:00
2005-11-20 08:51:22 +03:00
return retval ;
2005-04-17 02:20:36 +04:00
}
static ssize_t uinput_read ( struct file * file , char __user * buffer , size_t count , loff_t * ppos )
{
struct uinput_device * udev = file - > private_data ;
int retval = 0 ;
2005-11-20 08:51:22 +03:00
if ( udev - > state ! = UIST_CREATED )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-06-30 09:47:50 +04:00
if ( udev - > head = = udev - > tail & & ( file - > f_flags & O_NONBLOCK ) )
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
retval = wait_event_interruptible ( udev - > waitq ,
2005-11-20 08:51:22 +03:00
udev - > head ! = udev - > tail | | udev - > state ! = UIST_CREATED ) ;
2005-04-17 02:20:36 +04:00
if ( retval )
return retval ;
2005-11-20 08:51:22 +03:00
retval = down_interruptible ( & udev - > sem ) ;
if ( retval )
return retval ;
if ( udev - > state ! = UIST_CREATED ) {
retval = - ENODEV ;
goto out ;
}
2005-04-17 02:20:36 +04:00
2005-11-20 08:51:22 +03:00
while ( udev - > head ! = udev - > tail & & retval + sizeof ( struct input_event ) < = count ) {
if ( copy_to_user ( buffer + retval , & udev - > buff [ udev - > tail ] , sizeof ( struct input_event ) ) ) {
retval = - EFAULT ;
goto out ;
}
2005-04-17 02:20:36 +04:00
udev - > tail = ( udev - > tail + 1 ) % UINPUT_BUFFER_SIZE ;
retval + = sizeof ( struct input_event ) ;
}
2005-11-20 08:51:22 +03:00
out :
up ( & udev - > sem ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
static unsigned int uinput_poll ( struct file * file , poll_table * wait )
{
struct uinput_device * udev = file - > private_data ;
poll_wait ( file , & udev - > waitq , wait ) ;
if ( udev - > head ! = udev - > tail )
return POLLIN | POLLRDNORM ;
return 0 ;
}
2005-11-20 08:51:22 +03:00
static int uinput_release ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-11-20 08:51:22 +03:00
struct uinput_device * udev = file - > private_data ;
2005-04-17 02:20:36 +04:00
2005-11-20 08:51:22 +03:00
uinput_destroy_device ( udev ) ;
2005-04-17 02:20:36 +04:00
kfree ( udev ) ;
return 0 ;
}
2005-11-20 08:51:22 +03:00
# define uinput_set_bit(_arg, _bit, _max) \
( { \
int __ret = 0 ; \
if ( udev - > state = = UIST_CREATED ) \
__ret = - EINVAL ; \
else if ( ( _arg ) > ( _max ) ) \
__ret = - EINVAL ; \
else set_bit ( ( _arg ) , udev - > dev - > _bit ) ; \
__ret ; \
} )
static long uinput_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2005-11-20 08:51:22 +03:00
int retval ;
2005-04-17 02:20:36 +04:00
struct uinput_device * udev ;
void __user * p = ( void __user * ) arg ;
struct uinput_ff_upload ff_up ;
struct uinput_ff_erase ff_erase ;
struct uinput_request * req ;
int length ;
2005-06-30 09:50:38 +04:00
char * phys ;
2005-04-17 02:20:36 +04:00
udev = file - > private_data ;
2005-11-20 08:51:22 +03:00
retval = down_interruptible ( & udev - > sem ) ;
if ( retval )
return retval ;
if ( ! udev - > dev ) {
retval = uinput_allocate_device ( udev ) ;
if ( retval )
goto out ;
2005-04-17 02:20:36 +04:00
}
switch ( cmd ) {
case UI_DEV_CREATE :
retval = uinput_create_device ( udev ) ;
break ;
case UI_DEV_DESTROY :
2005-11-20 08:51:22 +03:00
uinput_destroy_device ( udev ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_EVBIT :
2005-11-20 08:51:22 +03:00
retval = uinput_set_bit ( arg , evbit , EV_MAX ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_KEYBIT :
2005-11-20 08:51:22 +03:00
retval = uinput_set_bit ( arg , keybit , KEY_MAX ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_RELBIT :
2005-11-20 08:51:22 +03:00
retval = uinput_set_bit ( arg , relbit , REL_MAX ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_ABSBIT :
2005-11-20 08:51:22 +03:00
retval = uinput_set_bit ( arg , absbit , ABS_MAX ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_MSCBIT :
2005-11-20 08:51:22 +03:00
retval = uinput_set_bit ( arg , mscbit , MSC_MAX ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_LEDBIT :
2005-11-20 08:51:22 +03:00
retval = uinput_set_bit ( arg , ledbit , LED_MAX ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_SNDBIT :
2005-11-20 08:51:22 +03:00
retval = uinput_set_bit ( arg , sndbit , SND_MAX ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_FFBIT :
2005-11-20 08:51:22 +03:00
retval = uinput_set_bit ( arg , ffbit , FF_MAX ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_SET_PHYS :
2005-11-20 08:51:22 +03:00
if ( udev - > state = = UIST_CREATED ) {
retval = - EINVAL ;
goto out ;
}
2005-04-17 02:20:36 +04:00
length = strnlen_user ( p , 1024 ) ;
if ( length < = 0 ) {
retval = - EFAULT ;
break ;
}
2005-06-30 09:50:38 +04:00
kfree ( udev - > dev - > phys ) ;
udev - > dev - > phys = phys = kmalloc ( length , GFP_KERNEL ) ;
if ( ! phys ) {
2005-04-17 02:20:36 +04:00
retval = - ENOMEM ;
break ;
}
2005-06-30 09:50:38 +04:00
if ( copy_from_user ( phys , p , length ) ) {
2005-04-17 02:20:36 +04:00
udev - > dev - > phys = NULL ;
2005-06-30 09:50:38 +04:00
kfree ( phys ) ;
retval = - EFAULT ;
2005-04-17 02:20:36 +04:00
break ;
}
2005-06-30 09:50:38 +04:00
phys [ length - 1 ] = ' \0 ' ;
2005-04-17 02:20:36 +04:00
break ;
case UI_BEGIN_FF_UPLOAD :
if ( copy_from_user ( & ff_up , p , sizeof ( ff_up ) ) ) {
retval = - EFAULT ;
break ;
}
req = uinput_request_find ( udev , ff_up . request_id ) ;
2005-06-30 09:47:50 +04:00
if ( ! ( req & & req - > code = = UI_FF_UPLOAD & & req - > u . effect ) ) {
2005-04-17 02:20:36 +04:00
retval = - EINVAL ;
break ;
}
ff_up . retval = 0 ;
memcpy ( & ff_up . effect , req - > u . effect , sizeof ( struct ff_effect ) ) ;
if ( copy_to_user ( p , & ff_up , sizeof ( ff_up ) ) ) {
retval = - EFAULT ;
break ;
}
break ;
case UI_BEGIN_FF_ERASE :
if ( copy_from_user ( & ff_erase , p , sizeof ( ff_erase ) ) ) {
retval = - EFAULT ;
break ;
}
req = uinput_request_find ( udev , ff_erase . request_id ) ;
2005-06-30 09:47:50 +04:00
if ( ! ( req & & req - > code = = UI_FF_ERASE ) ) {
2005-04-17 02:20:36 +04:00
retval = - EINVAL ;
break ;
}
ff_erase . retval = 0 ;
ff_erase . effect_id = req - > u . effect_id ;
if ( copy_to_user ( p , & ff_erase , sizeof ( ff_erase ) ) ) {
retval = - EFAULT ;
break ;
}
break ;
case UI_END_FF_UPLOAD :
if ( copy_from_user ( & ff_up , p , sizeof ( ff_up ) ) ) {
retval = - EFAULT ;
break ;
}
req = uinput_request_find ( udev , ff_up . request_id ) ;
2005-06-30 09:47:50 +04:00
if ( ! ( req & & req - > code = = UI_FF_UPLOAD & & req - > u . effect ) ) {
2005-04-17 02:20:36 +04:00
retval = - EINVAL ;
break ;
}
req - > retval = ff_up . retval ;
memcpy ( req - > u . effect , & ff_up . effect , sizeof ( struct ff_effect ) ) ;
2005-06-30 09:48:14 +04:00
uinput_request_done ( udev , req ) ;
2005-04-17 02:20:36 +04:00
break ;
case UI_END_FF_ERASE :
if ( copy_from_user ( & ff_erase , p , sizeof ( ff_erase ) ) ) {
retval = - EFAULT ;
break ;
}
req = uinput_request_find ( udev , ff_erase . request_id ) ;
2005-06-30 09:47:50 +04:00
if ( ! ( req & & req - > code = = UI_FF_ERASE ) ) {
2005-04-17 02:20:36 +04:00
retval = - EINVAL ;
break ;
}
req - > retval = ff_erase . retval ;
2005-06-30 09:48:14 +04:00
uinput_request_done ( udev , req ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
retval = - EINVAL ;
}
2005-11-20 08:51:22 +03:00
out :
up ( & udev - > sem ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
static struct file_operations uinput_fops = {
2005-11-20 08:51:22 +03:00
. owner = THIS_MODULE ,
. open = uinput_open ,
. release = uinput_release ,
. read = uinput_read ,
. write = uinput_write ,
. poll = uinput_poll ,
. unlocked_ioctl = uinput_ioctl ,
2005-04-17 02:20:36 +04:00
} ;
static struct miscdevice uinput_misc = {
2005-11-20 08:51:22 +03:00
. fops = & uinput_fops ,
. minor = UINPUT_MINOR ,
. name = UINPUT_NAME ,
2005-04-17 02:20:36 +04:00
} ;
static int __init uinput_init ( void )
{
return misc_register ( & uinput_misc ) ;
}
static void __exit uinput_exit ( void )
{
misc_deregister ( & uinput_misc ) ;
}
MODULE_AUTHOR ( " Aristeu Sergio Rozanski Filho " ) ;
MODULE_DESCRIPTION ( " User level driver support for input subsystem " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( uinput_init ) ;
module_exit ( uinput_exit ) ;