2019-05-29 07:18:02 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2013-09-18 12:50:42 -07:00
/*
* Copyright ( c ) 2013 , Microsoft Corporation .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/completion.h>
# include <linux/hyperv.h>
# include <linux/serio.h>
# include <linux/slab.h>
/*
* Current version 1.0
*
*/
# define SYNTH_KBD_VERSION_MAJOR 1
# define SYNTH_KBD_VERSION_MINOR 0
# define SYNTH_KBD_VERSION (SYNTH_KBD_VERSION_MINOR | \
( SYNTH_KBD_VERSION_MAJOR < < 16 ) )
/*
* Message types in the synthetic input protocol
*/
enum synth_kbd_msg_type {
SYNTH_KBD_PROTOCOL_REQUEST = 1 ,
SYNTH_KBD_PROTOCOL_RESPONSE = 2 ,
SYNTH_KBD_EVENT = 3 ,
SYNTH_KBD_LED_INDICATORS = 4 ,
} ;
/*
* Basic message structures .
*/
struct synth_kbd_msg_hdr {
__le32 type ;
} ;
struct synth_kbd_msg {
struct synth_kbd_msg_hdr header ;
char data [ ] ; /* Enclosed message */
} ;
union synth_kbd_version {
__le32 version ;
} ;
/*
* Protocol messages
*/
struct synth_kbd_protocol_request {
struct synth_kbd_msg_hdr header ;
union synth_kbd_version version_requested ;
} ;
# define PROTOCOL_ACCEPTED BIT(0)
struct synth_kbd_protocol_response {
struct synth_kbd_msg_hdr header ;
__le32 proto_status ;
} ;
# define IS_UNICODE BIT(0)
# define IS_BREAK BIT(1)
# define IS_E0 BIT(2)
# define IS_E1 BIT(3)
struct synth_kbd_keystroke {
struct synth_kbd_msg_hdr header ;
__le16 make_code ;
__le16 reserved0 ;
__le32 info ; /* Additional information */
} ;
# define HK_MAXIMUM_MESSAGE_SIZE 256
2019-07-14 16:27:18 -07:00
# define KBD_VSC_SEND_RING_BUFFER_SIZE (40 * 1024)
# define KBD_VSC_RECV_RING_BUFFER_SIZE (40 * 1024)
2013-09-18 12:50:42 -07:00
# define XTKBD_EMUL0 0xe0
# define XTKBD_EMUL1 0xe1
# define XTKBD_RELEASE 0x80
/*
* Represents a keyboard device
*/
struct hv_kbd_dev {
struct hv_device * hv_dev ;
struct serio * hv_serio ;
struct synth_kbd_protocol_request protocol_req ;
struct synth_kbd_protocol_response protocol_resp ;
/* Synchronize the request/response if needed */
struct completion wait_event ;
spinlock_t lock ; /* protects 'started' field */
bool started ;
} ;
static void hv_kbd_on_receive ( struct hv_device * hv_dev ,
struct synth_kbd_msg * msg , u32 msg_length )
{
struct hv_kbd_dev * kbd_dev = hv_get_drvdata ( hv_dev ) ;
struct synth_kbd_keystroke * ks_msg ;
unsigned long flags ;
u32 msg_type = __le32_to_cpu ( msg - > header . type ) ;
u32 info ;
u16 scan_code ;
switch ( msg_type ) {
case SYNTH_KBD_PROTOCOL_RESPONSE :
/*
* Validate the information provided by the host .
* If the host is giving us a bogus packet ,
* drop the packet ( hoping the problem
* goes away ) .
*/
if ( msg_length < sizeof ( struct synth_kbd_protocol_response ) ) {
dev_err ( & hv_dev - > device ,
" Illegal protocol response packet (len: %d) \n " ,
msg_length ) ;
break ;
}
memcpy ( & kbd_dev - > protocol_resp , msg ,
sizeof ( struct synth_kbd_protocol_response ) ) ;
complete ( & kbd_dev - > wait_event ) ;
break ;
case SYNTH_KBD_EVENT :
/*
* Validate the information provided by the host .
* If the host is giving us a bogus packet ,
* drop the packet ( hoping the problem
* goes away ) .
*/
if ( msg_length < sizeof ( struct synth_kbd_keystroke ) ) {
dev_err ( & hv_dev - > device ,
" Illegal keyboard event packet (len: %d) \n " ,
msg_length ) ;
break ;
}
ks_msg = ( struct synth_kbd_keystroke * ) msg ;
info = __le32_to_cpu ( ks_msg - > info ) ;
/*
* Inject the information through the serio interrupt .
*/
spin_lock_irqsave ( & kbd_dev - > lock , flags ) ;
if ( kbd_dev - > started ) {
if ( info & IS_E0 )
serio_interrupt ( kbd_dev - > hv_serio ,
XTKBD_EMUL0 , 0 ) ;
2014-01-12 11:09:14 -08:00
if ( info & IS_E1 )
serio_interrupt ( kbd_dev - > hv_serio ,
XTKBD_EMUL1 , 0 ) ;
2013-09-18 12:50:42 -07:00
scan_code = __le16_to_cpu ( ks_msg - > make_code ) ;
if ( info & IS_BREAK )
scan_code | = XTKBD_RELEASE ;
serio_interrupt ( kbd_dev - > hv_serio , scan_code , 0 ) ;
}
spin_unlock_irqrestore ( & kbd_dev - > lock , flags ) ;
2014-08-06 13:33:54 -07:00
/*
* Only trigger a wakeup on key down , otherwise
* " echo freeze > /sys/power/state " can ' t really enter the
* state because the Enter - UP can trigger a wakeup at once .
*/
if ( ! ( info & IS_BREAK ) )
2018-11-15 11:05:10 -08:00
pm_wakeup_hard_event ( & hv_dev - > device ) ;
2014-08-06 13:33:54 -07:00
2013-09-18 12:50:42 -07:00
break ;
default :
dev_err ( & hv_dev - > device ,
" unhandled message type %d \n " , msg_type ) ;
}
}
static void hv_kbd_handle_received_packet ( struct hv_device * hv_dev ,
struct vmpacket_descriptor * desc ,
u32 bytes_recvd ,
u64 req_id )
{
struct synth_kbd_msg * msg ;
u32 msg_sz ;
switch ( desc - > type ) {
case VM_PKT_COMP :
break ;
case VM_PKT_DATA_INBAND :
/*
* We have a packet that has " inband " data . The API used
* for retrieving the packet guarantees that the complete
* packet is read . So , minimally , we should be able to
* parse the payload header safely ( assuming that the host
* can be trusted . Trusting the host seems to be a
* reasonable assumption because in a virtualized
* environment there is not whole lot you can do if you
* don ' t trust the host .
*
* Nonetheless , let us validate if the host can be trusted
* ( in a trivial way ) . The interesting aspect of this
* validation is how do you recover if we discover that the
* host is not to be trusted ? Simply dropping the packet , I
* don ' t think is an appropriate recovery . In the interest
* of failing fast , it may be better to crash the guest .
* For now , I will just drop the packet !
*/
msg_sz = bytes_recvd - ( desc - > offset8 < < 3 ) ;
if ( msg_sz < = sizeof ( struct synth_kbd_msg_hdr ) ) {
/*
* Drop the packet and hope
* the problem magically goes away .
*/
dev_err ( & hv_dev - > device ,
" Illegal packet (type: %d, tid: %llx, size: %d) \n " ,
desc - > type , req_id , msg_sz ) ;
break ;
}
msg = ( void * ) desc + ( desc - > offset8 < < 3 ) ;
hv_kbd_on_receive ( hv_dev , msg , msg_sz ) ;
break ;
default :
dev_err ( & hv_dev - > device ,
" unhandled packet type %d, tid %llx len %d \n " ,
desc - > type , req_id , bytes_recvd ) ;
break ;
}
}
static void hv_kbd_on_channel_callback ( void * context )
{
2019-08-20 03:01:23 +00:00
struct vmpacket_descriptor * desc ;
2013-09-18 12:50:42 -07:00
struct hv_device * hv_dev = context ;
u32 bytes_recvd ;
u64 req_id ;
2019-08-20 03:01:23 +00:00
foreach_vmbus_pkt ( desc , hv_dev - > channel ) {
bytes_recvd = desc - > len8 * 8 ;
req_id = desc - > trans_id ;
2013-09-18 12:50:42 -07:00
2019-08-20 03:01:23 +00:00
hv_kbd_handle_received_packet ( hv_dev , desc , bytes_recvd ,
req_id ) ;
2013-09-18 12:50:42 -07:00
}
}
static int hv_kbd_connect_to_vsp ( struct hv_device * hv_dev )
{
struct hv_kbd_dev * kbd_dev = hv_get_drvdata ( hv_dev ) ;
struct synth_kbd_protocol_request * request ;
struct synth_kbd_protocol_response * response ;
u32 proto_status ;
int error ;
2020-01-06 14:40:44 -08:00
reinit_completion ( & kbd_dev - > wait_event ) ;
2013-09-18 12:50:42 -07:00
request = & kbd_dev - > protocol_req ;
memset ( request , 0 , sizeof ( struct synth_kbd_protocol_request ) ) ;
request - > header . type = __cpu_to_le32 ( SYNTH_KBD_PROTOCOL_REQUEST ) ;
request - > version_requested . version = __cpu_to_le32 ( SYNTH_KBD_VERSION ) ;
error = vmbus_sendpacket ( hv_dev - > channel , request ,
sizeof ( struct synth_kbd_protocol_request ) ,
( unsigned long ) request ,
VM_PKT_DATA_INBAND ,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED ) ;
if ( error )
return error ;
if ( ! wait_for_completion_timeout ( & kbd_dev - > wait_event , 10 * HZ ) )
return - ETIMEDOUT ;
response = & kbd_dev - > protocol_resp ;
proto_status = __le32_to_cpu ( response - > proto_status ) ;
if ( ! ( proto_status & PROTOCOL_ACCEPTED ) ) {
dev_err ( & hv_dev - > device ,
" synth_kbd protocol request failed (version %d) \n " ,
SYNTH_KBD_VERSION ) ;
return - ENODEV ;
}
return 0 ;
}
static int hv_kbd_start ( struct serio * serio )
{
struct hv_kbd_dev * kbd_dev = serio - > port_data ;
unsigned long flags ;
spin_lock_irqsave ( & kbd_dev - > lock , flags ) ;
kbd_dev - > started = true ;
spin_unlock_irqrestore ( & kbd_dev - > lock , flags ) ;
return 0 ;
}
static void hv_kbd_stop ( struct serio * serio )
{
struct hv_kbd_dev * kbd_dev = serio - > port_data ;
unsigned long flags ;
spin_lock_irqsave ( & kbd_dev - > lock , flags ) ;
kbd_dev - > started = false ;
spin_unlock_irqrestore ( & kbd_dev - > lock , flags ) ;
}
static int hv_kbd_probe ( struct hv_device * hv_dev ,
const struct hv_vmbus_device_id * dev_id )
{
struct hv_kbd_dev * kbd_dev ;
struct serio * hv_serio ;
int error ;
kbd_dev = kzalloc ( sizeof ( struct hv_kbd_dev ) , GFP_KERNEL ) ;
hv_serio = kzalloc ( sizeof ( struct serio ) , GFP_KERNEL ) ;
if ( ! kbd_dev | | ! hv_serio ) {
error = - ENOMEM ;
goto err_free_mem ;
}
kbd_dev - > hv_dev = hv_dev ;
kbd_dev - > hv_serio = hv_serio ;
spin_lock_init ( & kbd_dev - > lock ) ;
init_completion ( & kbd_dev - > wait_event ) ;
hv_set_drvdata ( hv_dev , kbd_dev ) ;
hv_serio - > dev . parent = & hv_dev - > device ;
hv_serio - > id . type = SERIO_8042_XL ;
hv_serio - > port_data = kbd_dev ;
strlcpy ( hv_serio - > name , dev_name ( & hv_dev - > device ) ,
sizeof ( hv_serio - > name ) ) ;
strlcpy ( hv_serio - > phys , dev_name ( & hv_dev - > device ) ,
sizeof ( hv_serio - > phys ) ) ;
hv_serio - > start = hv_kbd_start ;
hv_serio - > stop = hv_kbd_stop ;
error = vmbus_open ( hv_dev - > channel ,
KBD_VSC_SEND_RING_BUFFER_SIZE ,
KBD_VSC_RECV_RING_BUFFER_SIZE ,
NULL , 0 ,
hv_kbd_on_channel_callback ,
hv_dev ) ;
if ( error )
goto err_free_mem ;
error = hv_kbd_connect_to_vsp ( hv_dev ) ;
if ( error )
goto err_close_vmbus ;
serio_register_port ( kbd_dev - > hv_serio ) ;
2014-08-06 13:33:54 -07:00
device_init_wakeup ( & hv_dev - > device , true ) ;
2013-09-18 12:50:42 -07:00
return 0 ;
err_close_vmbus :
vmbus_close ( hv_dev - > channel ) ;
err_free_mem :
kfree ( hv_serio ) ;
kfree ( kbd_dev ) ;
return error ;
}
static int hv_kbd_remove ( struct hv_device * hv_dev )
{
struct hv_kbd_dev * kbd_dev = hv_get_drvdata ( hv_dev ) ;
serio_unregister_port ( kbd_dev - > hv_serio ) ;
vmbus_close ( hv_dev - > channel ) ;
kfree ( kbd_dev ) ;
hv_set_drvdata ( hv_dev , NULL ) ;
return 0 ;
}
2020-01-06 14:40:44 -08:00
static int hv_kbd_suspend ( struct hv_device * hv_dev )
{
vmbus_close ( hv_dev - > channel ) ;
return 0 ;
}
static int hv_kbd_resume ( struct hv_device * hv_dev )
{
int ret ;
ret = vmbus_open ( hv_dev - > channel ,
KBD_VSC_SEND_RING_BUFFER_SIZE ,
KBD_VSC_RECV_RING_BUFFER_SIZE ,
NULL , 0 ,
hv_kbd_on_channel_callback ,
hv_dev ) ;
if ( ret = = 0 )
ret = hv_kbd_connect_to_vsp ( hv_dev ) ;
return ret ;
}
2013-09-18 12:50:42 -07:00
static const struct hv_vmbus_device_id id_table [ ] = {
/* Keyboard guid */
{ HV_KBD_GUID , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( vmbus , id_table ) ;
static struct hv_driver hv_kbd_drv = {
. name = KBUILD_MODNAME ,
. id_table = id_table ,
. probe = hv_kbd_probe ,
. remove = hv_kbd_remove ,
2020-01-06 14:40:44 -08:00
. suspend = hv_kbd_suspend ,
. resume = hv_kbd_resume ,
2018-06-05 13:37:49 -07:00
. driver = {
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
} ,
2013-09-18 12:50:42 -07:00
} ;
static int __init hv_kbd_init ( void )
{
return vmbus_driver_register ( & hv_kbd_drv ) ;
}
static void __exit hv_kbd_exit ( void )
{
vmbus_driver_unregister ( & hv_kbd_drv ) ;
}
MODULE_LICENSE ( " GPL " ) ;
2019-04-23 03:37:07 +00:00
MODULE_DESCRIPTION ( " Microsoft Hyper-V Synthetic Keyboard Driver " ) ;
2013-09-18 12:50:42 -07:00
module_init ( hv_kbd_init ) ;
module_exit ( hv_kbd_exit ) ;