Input: hyperv-keyboard - implement Type Clipboard Text
Subsystems (3)
Dexuan Cui
maintainerPATCH 1/1 Input: hyperv-keyboard - implement Type Clipboard Text
In the menu of the Hyper-V's Virtual Machine Connection, there is a feature called "Clipboard | Type clipboard text", which can be used to copy a string in the host's clipboard into the guest's current input focus(text console or a GUI window).
Currently the feature doesn't work for Linux VM because the driver hyperv-keyboard hasn't been enhanced to support it -- this patch is made to do it.
For each char in the string, the host sends 2 events (key down/up with the char's UNICODE value) to the guest.
The patch finds each char's scan codes of key down/up, and injects the scan codes to the serio keyboard module.
Known issues:
1) Only printable ASCII chars are supported, and unsupported chars are ignored. It seems unlikely to support generic UNICODE chars because there is not a generic API to inject a UNICODE char to text mode console, KDE, gnome, etc.
2) When we use the feature, make sure the CapsLock state of the VM's (virtual) keyboard is OFF because this patch assumes it -- we'll try to fix this later, probably by tracking the state of virtual CapsLock, because it looks the keyboard module doesn't supply an API for us to query the state of the keyboard.
--- drivers/input/serio/hyperv-keyboard.c | 213 ++++++++++++++++++++++++++++++++-- 1 file changed, 201 insertions(+), 12 deletions(-)
drivers/input/serio/hyperv-keyboard.c +201 -12
@@ -18,6 +18,8 @@#include <linux/hyperv.h> #include <linux/serio.h> #include <linux/slab.h> +#include <linux/kfifo.h> +#include <linux/delay.h> /* * Current version 1.0
@@ -84,7 +86,15 @@ struct synth_kbd_keystroke {#define HK_MAXIMUM_MESSAGE_SIZE 256#define KBD_VSC_SEND_RING_BUFFER_SIZE (10 * PAGE_SIZE) -#define KBD_VSC_RECV_RING_BUFFER_SIZE (10 * PAGE_SIZE) + +/* +* For the Type Clipboard Text feature, the host can inject a string of +* up to 2047 chars, and each char means 2 events(key down/up) and every +* event means a VMBUS packet of 32 Bytes: so the max recv buf's size +* should be about 2K * 2 * 32 = 32 pages. +* Let's add 8 extra pages for safety. +*/ +#define KBD_VSC_RECV_RING_BUFFER_SIZE (40 * PAGE_SIZE)#define XTKBD_EMUL0 0xe0#define XTKBD_EMUL1 0xe1
@@ -103,8 +113,173 @@ struct hv_kbd_dev {struct completion wait_event;spinlock_t lock;/* protects 'started' field */ bool started; + +DECLARE_KFIFO_PTR (fifo, u8); +struct work_struct work; +}; + +static const u8 ascii_to_scan_code[128 ] = { + ['a' ] =0x1e , ['A' ] =0x1e , + ['b' ] =0x30 , ['B' ] =0x30 , + ['c' ] =0x2e , ['C' ] =0x2e , + ['d' ] =0x20 , ['D' ] =0x20 , + ['e' ] =0x12 , ['E' ] =0x12 , + ['f' ] =0x21 , ['F' ] =0x21 , + ['g' ] =0x22 , ['G' ] =0x22 , + ['h' ] =0x23 , ['H' ] =0x23 , + ['i' ] =0x17 , ['I' ] =0x17 , + ['j' ] =0x24 , ['J' ] =0x24 , + ['k' ] =0x25 , ['K' ] =0x25 , + ['l' ] =0x26 , ['L' ] =0x26 , + ['m' ] =0x32 , ['M' ] =0x32 , + ['n' ] =0x31 , ['N' ] =0x31 , + ['o' ] =0x18 , ['O' ] =0x18 , + ['p' ] =0x19 , ['P' ] =0x19 , + ['q' ] =0x10 , ['Q' ] =0x10 , + ['r' ] =0x13 , ['R' ] =0x13 , + ['s' ] =0x1f , ['S' ] =0x1f , + ['t' ] =0x14 , ['T' ] =0x14 , + ['u' ] =0x16 , ['U' ] =0x16 , + ['v' ] =0x2f , ['V' ] =0x2f , + ['w' ] =0x11 , ['W' ] =0x11 , + ['x' ] =0x2d , ['X' ] =0x2d , + ['y' ] =0x15 , ['Y' ] =0x15 , + ['z' ] =0x2c , ['Z' ] =0x2c , + ['`' ] =0x29 , ['~' ] =0x29 , + ['1' ] =0x02 , ['!' ] =0x02 , + ['2' ] =0x03 , ['@' ] =0x03 , + ['3' ] =0x04 , ['#' ] =0x04 , + ['4' ] =0x05 , ['$' ] =0x05 , + ['5' ] =0x06 , ['%' ] =0x06 , + ['6' ] =0x07 , ['^' ] =0x07 , + ['7' ] =0x08 , ['&' ] =0x08 , + ['8' ] =0x09 , ['*' ] =0x09 , + ['9' ] =0x0a , ['(' ] =0x0a , + ['0' ] =0x0b , [')' ] =0x0b , + ['-' ] =0x0c , ['_' ] =0x0c , + ['=' ] =0x0d , ['+' ] =0x0d , + ['\t' ] =0x0f , + ['[' ] =0x1a , ['{' ] =0x1a , + [']' ] =0x1b , ['}' ] =0x1b , + ['\\' ] =0x2b , ['|' ] =0x2b , + [';' ] =0x27 , [':' ] =0x27 , + ['\'' ] =0x28 , ['\"' ] =0x28 , + ['\r' ] =0x1c , + ['\n' ] =0x1c , + [',' ] =0x33 , ['<' ] =0x33 , + ['.' ] =0x34 , ['>' ] =0x34 , + ['/' ] =0x35 , ['?' ] =0x35 , + [' ' ] =0x39 , }; +static const bool ascii_needs_shift[128 ] = { + ['A' ] =1 , + ['B' ] =1 , + ['C' ] =1 , + ['D' ] =1 , + ['E' ] =1 , + ['F' ] =1 , + ['G' ] =1 , + ['H' ] =1 , + ['I' ] =1 , + ['J' ] =1 , + ['K' ] =1 , + ['L' ] =1 , + ['M' ] =1 , + ['N' ] =1 , + ['O' ] =1 , + ['P' ] =1 , + ['Q' ] =1 , + ['R' ] =1 , + ['S' ] =1 , + ['T' ] =1 , + ['U' ] =1 , + ['V' ] =1 , + ['W' ] =1 , + ['X' ] =1 , + ['Y' ] =1 , + ['Z' ] =1 , + ['~' ] =1 , + ['!' ] =1 , + ['@' ] =1 , + ['#' ] =1 , + ['$' ] =1 , + ['%' ] =1 , + ['^' ] =1 , + ['&' ] =1 , + ['*' ] =1 , + ['(' ] =1 , + [')' ] =1 , + ['_' ] =1 , + ['+' ] =1 , + ['{' ] =1 , + ['}' ] =1 , + ['|' ] =1 , + [':' ] =1 , + ['\"' ] =1 , + ['<' ] =1 , + ['>' ] =1 , + ['?' ] =1 , +}; +static void handle_scancode (struct work_struct *work) +{ +unsigned long flags; +struct hv_kbd_dev *kbd_dev = + container_of(work, struct hv_kbd_dev, work); + +u8 scan_code =-1 ; +unsigned int ret; + +/* +* Inject the information through the serio interrupt. +*/ +do { +spin_lock_irqsave (&kbd_dev->lock, flags); + + ret =kfifo_get (&kbd_dev->fifo, &scan_code); +if (ret !=0 ) +serio_interrupt (kbd_dev->hv_serio, scan_code,0 ); + +spin_unlock_irqrestore (&kbd_dev->lock, flags); + +/* +* We can't inject the scan codes too fast, otherwise, some +* of the keys can be found lost in a X11 window. Tests show +* 10ms is a safe empirical value. +*/ +if (ret !=0 ) +msleep (10 ); + }while (ret); +} + +static void handle_unicode (struct hv_kbd_dev *kbd_dev,u32 c,u32 info) +{ +u8 scan_code; +bool break_code = false; + +/* We don't support generic UNICODE chars. */ +if (c >=0x80 ) +return ; + +/* We only support printable ASCII chars */ + scan_code = ascii_to_scan_code[c]; +if (!scan_code) +return ; + +if (info &IS_BREAK ) { + scan_code |=XTKBD_RELEASE ; + break_code = true; + } + +if (ascii_needs_shift[c] && !break_code) +kfifo_put (&kbd_dev->fifo,0x2a );/* L-Shift Down */ + +kfifo_put (&kbd_dev->fifo, scan_code); + +if (ascii_needs_shift[c] && break_code) +kfifo_put (&kbd_dev->fifo,0x2a |XTKBD_RELEASE );/* L-Shift Up */ +} +static void hv_kbd_on_receive (struct hv_device *hv_dev,struct synth_kbd_msg *msg,u32 msg_length) {
@@ -152,22 +327,28 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, ks_msg = (struct synth_kbd_keystroke *)msg; info =__le32_to_cpu (ks_msg->info); -/* -* Inject the information through the serio interrupt. -*/ +/* Put the scan codes into a fifo and handle it in a work */ spin_lock_irqsave (&kbd_dev->lock, flags);if (kbd_dev->started) {if (info &IS_E0 ) -serio_interrupt (kbd_dev->hv_serio, -XTKBD_EMUL0 ,0 ); +kfifo_put (&kbd_dev->fifo,XTKBD_EMUL0 );if (info &IS_E1 ) -serio_interrupt (kbd_dev->hv_serio, -XTKBD_EMUL1 ,0 ); +kfifo_put (&kbd_dev->fifo,XTKBD_EMUL1 ); + 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 ); +if (!(info &IS_UNICODE )) { +if (info &IS_BREAK ) + scan_code |=XTKBD_RELEASE ; + +kfifo_put (&kbd_dev->fifo, scan_code); + }else { +/* the scan_code is actually a UNICODE char */ +handle_unicode (kbd_dev, scan_code, info); + } + +schedule_work (&kbd_dev->work); + }spin_unlock_irqrestore (&kbd_dev->lock, flags);
@@ -354,6 +535,11 @@ static int hv_kbd_probe(struct hv_device *hv_dev, goto err_free_mem; } + error =kfifo_alloc (&kbd_dev->fifo,PAGE_SIZE *4 ,GFP_KERNEL ); +if (error) + goto err_free_mem; +INIT_WORK (&kbd_dev->work, handle_scancode); + kbd_dev->hv_dev = hv_dev; kbd_dev->hv_serio = hv_serio;spin_lock_init (&kbd_dev->lock);
@@ -378,7 +564,7 @@ static int hv_kbd_probe(struct hv_device *hv_dev, hv_kbd_on_channel_callback, hv_dev);if (error) - goto err_free_mem; + goto err_free_fifo; error =hv_kbd_connect_to_vsp (hv_dev);if (error)
@@ -392,6 +578,8 @@ static int hv_kbd_probe(struct hv_device *hv_dev, err_close_vmbus:vmbus_close (hv_dev->channel); +err_free_fifo: +kfifo_free (&kbd_dev->fifo); err_free_mem:kfree (hv_serio);kfree (kbd_dev);
@@ -405,6 +593,7 @@ static int hv_kbd_remove(struct hv_device *hv_dev)device_init_wakeup (&hv_dev->device, false);serio_unregister_port (kbd_dev->hv_serio);vmbus_close (hv_dev->channel); +kfifo_free (&kbd_dev->fifo);kfree (kbd_dev);hv_set_drvdata (hv_dev,NULL );
Signature · 1 line
1.9.1
Dmitry Torokhov
maintainerHi Dexuan,
Quoted 20 lines · Dexuan Cui
In the menu of the Hyper-V's Virtual Machine Connection, there is a feature called "Clipboard | Type clipboard text", which can be used to copy a string in the host's clipboard into the guest's current input focus(text console or a GUI window).
Currently the feature doesn't work for Linux VM because the driver hyperv-keyboard hasn't been enhanced to support it -- this patch is made to do it.
For each char in the string, the host sends 2 events (key down/up with the char's UNICODE value) to the guest.
The patch finds each char's scan codes of key down/up, and injects the scan codes to the serio keyboard module.Known issues:
1) Only printable ASCII chars are supported, and unsupported chars are ignored. It seems unlikely to support generic UNICODE chars because there is not a generic API to inject a UNICODE char to text mode console, KDE, gnome, etc.
Quoted 7 lines
2) When we use the feature, make sure the CapsLock state of the VM's (virtual) keyboard is OFF because this patch assumes it -- we'll try to fix this later, probably by tracking the state of virtual CapsLock, because it looks the keyboard module doesn't supply an API for us to query the state of the keyboard.
Signed-off-by Dexuan Cui <[email protected]>
No way. If you want to do this this way, do it in hypervisor code and keep feeding AT scan codes to hyperv-keyboard, although I am pretty sure users of French, Czech and other keyboard layouts with numbers in upper register and symbols in lower will have a few choice words for you.
If you want real cut-and-paste support in various DEs I'd recommend working with VMware on open-vm-tools package to see what can be shared/reused there.
Consider this NACked with prejudice.
Thanks.
Signature · 1 line
Dmitry
Dexuan Cui
maintainerQuoted 19 lines
-----Original Message-----
From: Dmitry Torokhov
Sent: Saturday, August 16, 2014 0:58 AM
To: Dexuan CuiQuoted 11 lines
For each char in the string, the host sends 2 events (key down/up with the char's UNICODE value) to the guest.
The patch finds each char's scan codes of key down/up, and injects the scan codes to the serio keyboard module.Known issues:
1) Only printable ASCII chars are supported, and unsupported chars are ignored. It seems unlikely to support generic UNICODE chars because there is not a generic API to inject a UNICODE char to text mode console, KDE, gnome, etc.Quoted 4 lines
2) When we use the feature, make sure the CapsLock state of the VM's (virtual) keyboard is OFF because this patch assumes it -- we'll try to fix this later, probably by tracking the state of virtual CapsLock, because it looks the keyboard module doesn't supply an API for us to query the
Quoted 5 lines
state
Quoted 2 lines
of the keyboard.
No way. If you want to do this this way, do it in hypervisor code and keep feeding AT scan codes to hyperv-keyboard, although I am pretty sure users
Hi Dmitry,
Yeah, I had the same wish, but later I found this seems unlikely because IMO the feature was firstly invented for Windows VM + generic UNICODE chars, and we know there is no "scan code" for generic UNICODE chars... :-(
Quoted 4 lines
of
French, Czech and other keyboard layouts with numbers in upper register and
symbols in lower will have a few choice words for you.
Sorry, I can't understand what these are.
Can you please give more details or a link to further info?
Quoted 6 lines
If you want real cut-and-paste support in various DEs I'd recommend working
with VMware on open-vm-tools package to see what can be shared/reused there.
Consider this NACked with prejudice.
Dmitry
Thanks for the suggestion!
Let me study open-vm-tools and report back.
Thanks,
-- Dexuan
Dmitry Torokhov
maintainerQuoted 20 lines · Dexuan Cui
Quoted 20 lines
-----Original Message-----
From: Dmitry Torokhov
Sent: Saturday, August 16, 2014 0:58 AM
To: Dexuan CuiQuoted 11 lines
For each char in the string, the host sends 2 events (key down/up with the char's UNICODE value) to the guest.
The patch finds each char's scan codes of key down/up, and injects the scan codes to the serio keyboard module.Known issues:
1) Only printable ASCII chars are supported, and unsupported chars are ignored. It seems unlikely to support generic UNICODE chars because there is not a generic API to inject a UNICODE char to text mode console, KDE, gnome, etc.Quoted 4 lines
2) When we use the feature, make sure the CapsLock state of the VM's (virtual) keyboard is OFF because this patch assumes it -- we'll try to fix this later, probably by tracking the state of virtual CapsLock, because it looks the keyboard module doesn't supply an API for us to query the
state
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
of the keyboard.
No way. If you want to do this this way, do it in hypervisor code and keep feeding AT scan codes to hyperv-keyboard, although I am pretty sure users
Hi Dmitry,
Yeah, I had the same wish, but later I found this seems unlikely because IMO the feature was firstly invented for Windows VM + generic UNICODE chars, and we know there is no "scan code" for generic UNICODE chars... :-(
Do you know what guest is running? You could potentially gate on that.
Quoted 7 lines
Quoted 5 lines
of
French, Czech and other keyboard layouts with numbers in upper register and
symbols in lower will have a few choice words for you.Sorry, I can't understand what these are.
Can you please give more details or a link to further info?
On French and many other European layouts to produce a number one needs to use Shift key, instead of simply using lower register, as on US or UK layouts. I.e. if you send scancode for KEY_3 through your proposed implementation it will produce '#' and KEY_SHIFT + KEY_3 will result in '3'.
There are also issues with QWERTY/AZERTY and other layouts...
Thanks.
Signature · 1 line
Dmitry
Dexuan Cui
maintainerQuoted 17 lines
-----Original Message-----
From: Dmitry Torokhov
Sent: Tuesday, August 19, 2014 2:52 AM
To: Dexuan CuiQuoted 5 lines · Dexuan Cui
Quoted 5 lines
-----Original Message-----
From: Dmitry Torokhov
Sent: Saturday, August 16, 2014 0:58 AM
To: Dexuan CuiQuoted 1 line
For each char in the string, the host sends 2 events (key down/up with
the
Quoted 6 lines
Quoted 6 lines
Quoted 4 lines
char's UNICODE value) to the guest.
The patch finds each char's scan codes of key down/up, and injects the scan codes to the serio keyboard module.No way. If you want to do this this way, do it in hypervisor code and keep feeding AT scan codes to hyperv-keyboard, although I am pretty sure
Quoted 8 lines
users
Quoted 2 lines
Hi Dmitry,
Yeah, I had the same wish, but later I found this seems unlikely becauseIMO
Quoted 2 lines
the feature was firstly invented for Windows VM + generic UNICODE chars, and we know there is no "scan code" for generic UNICODE chars... :-(
Do you know what guest is running? You could potentially gate on that.
I'm not sure if the host knows this or not.
But even if the host knows this, IMO it seems bad for the host to discriminate between Windows guest and Linux guest. And generally there is no scan code for UNICODE chars.
And, here the patch tries to use a light-weight and straightforward method to achieve the basic functionality of "Type (host) clipboard text (to guest)" -- the patch doesn't use the guest's X11 clipboard and it works for both text mode tty and X11 window's input (for English keyboard layout. Please see my below reply).
I studied open-vm-tools.sourceforge.net and github.com/vmware/open-vm-tools.
My impression is: it's a pretty big collection of utilities/daemons and its code seems bound to vmware-specific features, maybe that's why it's called "open-vm-tools" rather than "generic-vm-tools" :-)
1) 2517/2528 of the commits of the git repo are from vmware;
2) In Ubuntu 14.04, the utilities of open-vm-tools are just named as vmware-XXX (I checked this by "dpkg-query -L open-vm-tools");
3) about the text copy&paste functionality between host/guest in either direction, the implementation of open-vm-tools uses host/guest X11 clipboards and hence doesn't work in real text mode tty terminal, and, vmware-specific implementation of backdoor() is extensively used -- I don't think there is such an equivalent in other hypervisors' environment, AFAIK.
So IMHO it's impractical for me to re-use open-vm-tools here?
Quoted 7 lines
Quoted 6 lines
Quoted 4 lines
of
French, Czech and other keyboard layouts with numbers in upper register and
symbols in lower will have a few choice words for you.Sorry, I can't understand what these are.
Can you please give more details or a link to further info?
Quoted 8 lines
On French and many other European layouts to produce a number one needs to use
Shift key, instead of simply using lower register, as on US or UK layouts. I.e. if you send scancode forKEY_3through your proposed implementation it will produce '#' andKEY_SHIFT+KEY_3will result in '3'.There are also issues with QWERTY/AZERTY and other layouts... Dmitry
Hi Dmitry,
Thanks for pointing this out!
This is a real issue -- my patch only works for English layout. :-(
I wrongly assumed there is a 1:1 mapping between an ASCII char and a scan code -- this is not true considering different keyboard layouts(keymaps).
So looks it's impossible for me to implement the functionality through the path of the keyboard input subsystem... :-(
A feasible method would be: I can write a lightweight userland daemon, just like open-vm-tools, which receives the string from the host and passes it to the X11 windowing system directly using X11 APIs(this doesn't work in text mode however).
Looking forward to your comment.
Thanks,
-- Dexuan