@ -17,6 +17,7 @@
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/input.h>
# include <linux/input/mt.h>
# include <linux/slab.h>
# include <asm/xen/hypervisor.h>
@ -34,11 +35,14 @@
struct xenkbd_info {
struct input_dev * kbd ;
struct input_dev * ptr ;
struct input_dev * mtouch ;
struct xenkbd_page * page ;
int gref ;
int irq ;
struct xenbus_device * xbdev ;
char phys [ 32 ] ;
/* current MT slot/contact ID we are injecting events in */
int mtouch_cur_contact_id ;
} ;
enum { KPARAM_X , KPARAM_Y , KPARAM_CNT } ;
@ -56,6 +60,112 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *);
* to do that .
*/
static void xenkbd_handle_motion_event ( struct xenkbd_info * info ,
struct xenkbd_motion * motion )
{
input_report_rel ( info - > ptr , REL_X , motion - > rel_x ) ;
input_report_rel ( info - > ptr , REL_Y , motion - > rel_y ) ;
if ( motion - > rel_z )
input_report_rel ( info - > ptr , REL_WHEEL , - motion - > rel_z ) ;
input_sync ( info - > ptr ) ;
}
static void xenkbd_handle_position_event ( struct xenkbd_info * info ,
struct xenkbd_position * pos )
{
input_report_abs ( info - > ptr , ABS_X , pos - > abs_x ) ;
input_report_abs ( info - > ptr , ABS_Y , pos - > abs_y ) ;
if ( pos - > rel_z )
input_report_rel ( info - > ptr , REL_WHEEL , - pos - > rel_z ) ;
input_sync ( info - > ptr ) ;
}
static void xenkbd_handle_key_event ( struct xenkbd_info * info ,
struct xenkbd_key * key )
{
struct input_dev * dev ;
if ( test_bit ( key - > keycode , info - > ptr - > keybit ) ) {
dev = info - > ptr ;
} else if ( test_bit ( key - > keycode , info - > kbd - > keybit ) ) {
dev = info - > kbd ;
} else {
pr_warn ( " unhandled keycode 0x%x \n " , key - > keycode ) ;
return ;
}
input_report_key ( dev , key - > keycode , key - > pressed ) ;
input_sync ( dev ) ;
}
static void xenkbd_handle_mt_event ( struct xenkbd_info * info ,
struct xenkbd_mtouch * mtouch )
{
if ( unlikely ( ! info - > mtouch ) )
return ;
if ( mtouch - > contact_id ! = info - > mtouch_cur_contact_id ) {
info - > mtouch_cur_contact_id = mtouch - > contact_id ;
input_mt_slot ( info - > mtouch , mtouch - > contact_id ) ;
}
switch ( mtouch - > event_type ) {
case XENKBD_MT_EV_DOWN :
input_mt_report_slot_state ( info - > mtouch , MT_TOOL_FINGER , true ) ;
/* fall through */
case XENKBD_MT_EV_MOTION :
input_report_abs ( info - > mtouch , ABS_MT_POSITION_X ,
mtouch - > u . pos . abs_x ) ;
input_report_abs ( info - > mtouch , ABS_MT_POSITION_Y ,
mtouch - > u . pos . abs_y ) ;
break ;
case XENKBD_MT_EV_SHAPE :
input_report_abs ( info - > mtouch , ABS_MT_TOUCH_MAJOR ,
mtouch - > u . shape . major ) ;
input_report_abs ( info - > mtouch , ABS_MT_TOUCH_MINOR ,
mtouch - > u . shape . minor ) ;
break ;
case XENKBD_MT_EV_ORIENT :
input_report_abs ( info - > mtouch , ABS_MT_ORIENTATION ,
mtouch - > u . orientation ) ;
break ;
case XENKBD_MT_EV_UP :
input_mt_report_slot_state ( info - > mtouch , MT_TOOL_FINGER , false ) ;
break ;
case XENKBD_MT_EV_SYN :
input_mt_sync_frame ( info - > mtouch ) ;
input_sync ( info - > mtouch ) ;
break ;
}
}
static void xenkbd_handle_event ( struct xenkbd_info * info ,
union xenkbd_in_event * event )
{
switch ( event - > type ) {
case XENKBD_TYPE_MOTION :
xenkbd_handle_motion_event ( info , & event - > motion ) ;
break ;
case XENKBD_TYPE_KEY :
xenkbd_handle_key_event ( info , & event - > key ) ;
break ;
case XENKBD_TYPE_POS :
xenkbd_handle_position_event ( info , & event - > pos ) ;
break ;
case XENKBD_TYPE_MTOUCH :
xenkbd_handle_mt_event ( info , & event - > mtouch ) ;
break ;
}
}
static irqreturn_t input_handler ( int rq , void * dev_id )
{
struct xenkbd_info * info = dev_id ;
@ -66,44 +176,8 @@ static irqreturn_t input_handler(int rq, void *dev_id)
if ( prod = = page - > in_cons )
return IRQ_HANDLED ;
rmb ( ) ; /* ensure we see ring contents up to prod */
for ( cons = page - > in_cons ; cons ! = prod ; cons + + ) {
union xenkbd_in_event * event ;
struct input_dev * dev ;
event = & XENKBD_IN_RING_REF ( page , cons ) ;
dev = info - > ptr ;
switch ( event - > type ) {
case XENKBD_TYPE_MOTION :
input_report_rel ( dev , REL_X , event - > motion . rel_x ) ;
input_report_rel ( dev , REL_Y , event - > motion . rel_y ) ;
if ( event - > motion . rel_z )
input_report_rel ( dev , REL_WHEEL ,
- event - > motion . rel_z ) ;
break ;
case XENKBD_TYPE_KEY :
dev = NULL ;
if ( test_bit ( event - > key . keycode , info - > kbd - > keybit ) )
dev = info - > kbd ;
if ( test_bit ( event - > key . keycode , info - > ptr - > keybit ) )
dev = info - > ptr ;
if ( dev )
input_report_key ( dev , event - > key . keycode ,
event - > key . pressed ) ;
else
pr_warn ( " unhandled keycode 0x%x \n " ,
event - > key . keycode ) ;
break ;
case XENKBD_TYPE_POS :
input_report_abs ( dev , ABS_X , event - > pos . abs_x ) ;
input_report_abs ( dev , ABS_Y , event - > pos . abs_y ) ;
if ( event - > pos . rel_z )
input_report_rel ( dev , REL_WHEEL ,
- event - > pos . rel_z ) ;
break ;
}
if ( dev )
input_sync ( dev ) ;
}
for ( cons = page - > in_cons ; cons ! = prod ; cons + + )
xenkbd_handle_event ( info , & XENKBD_IN_RING_REF ( page , cons ) ) ;
mb ( ) ; /* ensure we got ring contents */
page - > in_cons = cons ;
notify_remote_via_irq ( info - > irq ) ;
@ -115,9 +189,9 @@ static int xenkbd_probe(struct xenbus_device *dev,
const struct xenbus_device_id * id )
{
int ret , i ;
unsigned int abs ;
unsigned int abs , touch ;
struct xenkbd_info * info ;
struct input_dev * kbd , * ptr ;
struct input_dev * kbd , * ptr , * mtouch ;
info = kzalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info ) {
@ -152,6 +226,17 @@ static int xenkbd_probe(struct xenbus_device *dev,
}
}
touch = xenbus_read_unsigned ( dev - > nodename ,
XENKBD_FIELD_FEAT_MTOUCH , 0 ) ;
if ( touch ) {
ret = xenbus_write ( XBT_NIL , dev - > nodename ,
XENKBD_FIELD_REQ_MTOUCH , " 1 " ) ;
if ( ret ) {
pr_warn ( " xenkbd: can't request multi-touch " ) ;
touch = 0 ;
}
}
/* keyboard */
kbd = input_allocate_device ( ) ;
if ( ! kbd )
@ -208,6 +293,58 @@ static int xenkbd_probe(struct xenbus_device *dev,
}
info - > ptr = ptr ;
/* multi-touch device */
if ( touch ) {
int num_cont , width , height ;
mtouch = input_allocate_device ( ) ;
if ( ! mtouch )
goto error_nomem ;
num_cont = xenbus_read_unsigned ( info - > xbdev - > nodename ,
XENKBD_FIELD_MT_NUM_CONTACTS ,
1 ) ;
width = xenbus_read_unsigned ( info - > xbdev - > nodename ,
XENKBD_FIELD_MT_WIDTH ,
XENFB_WIDTH ) ;
height = xenbus_read_unsigned ( info - > xbdev - > nodename ,
XENKBD_FIELD_MT_HEIGHT ,
XENFB_HEIGHT ) ;
mtouch - > name = " Xen Virtual Multi-touch " ;
mtouch - > phys = info - > phys ;
mtouch - > id . bustype = BUS_PCI ;
mtouch - > id . vendor = 0x5853 ;
mtouch - > id . product = 0xfffd ;
input_set_abs_params ( mtouch , ABS_MT_TOUCH_MAJOR ,
0 , 255 , 0 , 0 ) ;
input_set_abs_params ( mtouch , ABS_MT_POSITION_X ,
0 , width , 0 , 0 ) ;
input_set_abs_params ( mtouch , ABS_MT_POSITION_Y ,
0 , height , 0 , 0 ) ;
input_set_abs_params ( mtouch , ABS_MT_PRESSURE ,
0 , 255 , 0 , 0 ) ;
ret = input_mt_init_slots ( mtouch , num_cont , INPUT_MT_DIRECT ) ;
if ( ret ) {
input_free_device ( mtouch ) ;
xenbus_dev_fatal ( info - > xbdev , ret ,
" input_mt_init_slots " ) ;
goto error ;
}
ret = input_register_device ( mtouch ) ;
if ( ret ) {
input_free_device ( mtouch ) ;
xenbus_dev_fatal ( info - > xbdev , ret ,
" input_register_device(mtouch) " ) ;
goto error ;
}
info - > mtouch_cur_contact_id = - 1 ;
info - > mtouch = mtouch ;
}
ret = xenkbd_connect_backend ( dev , info ) ;
if ( ret < 0 )
goto error ;
@ -240,6 +377,8 @@ static int xenkbd_remove(struct xenbus_device *dev)
input_unregister_device ( info - > kbd ) ;
if ( info - > ptr )
input_unregister_device ( info - > ptr ) ;
if ( info - > mtouch )
input_unregister_device ( info - > mtouch ) ;
free_page ( ( unsigned long ) info - > page ) ;
kfree ( info ) ;
return 0 ;