diff options
Diffstat (limited to 'drivers/input/serio/hyperv-keyboard.c')
| -rw-r--r-- | drivers/input/serio/hyperv-keyboard.c | 129 |
1 files changed, 63 insertions, 66 deletions
diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index 25151d9214e0..0ee7505427ac 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. */ #include <linux/init.h> @@ -83,8 +75,8 @@ 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) +#define KBD_VSC_SEND_RING_BUFFER_SIZE VMBUS_RING_SIZE(36 * 1024) +#define KBD_VSC_RECV_RING_BUFFER_SIZE VMBUS_RING_SIZE(36 * 1024) #define XTKBD_EMUL0 0xe0 #define XTKBD_EMUL1 0xe1 @@ -110,7 +102,6 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, { 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; @@ -155,21 +146,22 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, /* * 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); - if (info & IS_E1) - serio_interrupt(kbd_dev->hv_serio, - XTKBD_EMUL1, 0); - scan_code = __le16_to_cpu(ks_msg->make_code); - if (info & IS_BREAK) - scan_code |= XTKBD_RELEASE; + scoped_guard(spinlock_irqsave, &kbd_dev->lock) { + if (kbd_dev->started) { + if (info & IS_E0) + serio_interrupt(kbd_dev->hv_serio, + XTKBD_EMUL0, 0); + if (info & IS_E1) + serio_interrupt(kbd_dev->hv_serio, + XTKBD_EMUL1, 0); + 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); + serio_interrupt(kbd_dev->hv_serio, + scan_code, 0); + } } - spin_unlock_irqrestore(&kbd_dev->lock, flags); /* * Only trigger a wakeup on key down, otherwise @@ -177,7 +169,7 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, * state because the Enter-UP can trigger a wakeup at once. */ if (!(info & IS_BREAK)) - pm_wakeup_event(&hv_dev->device, 0); + pm_wakeup_hard_event(&hv_dev->device); break; @@ -245,40 +237,17 @@ static void hv_kbd_handle_received_packet(struct hv_device *hv_dev, static void hv_kbd_on_channel_callback(void *context) { + struct vmpacket_descriptor *desc; struct hv_device *hv_dev = context; - void *buffer; - int bufferlen = 0x100; /* Start with sensible size */ u32 bytes_recvd; u64 req_id; - int error; - - buffer = kmalloc(bufferlen, GFP_ATOMIC); - if (!buffer) - return; - - while (1) { - error = vmbus_recvpacket_raw(hv_dev->channel, buffer, bufferlen, - &bytes_recvd, &req_id); - switch (error) { - case 0: - if (bytes_recvd == 0) { - kfree(buffer); - return; - } - hv_kbd_handle_received_packet(hv_dev, buffer, - bytes_recvd, req_id); - break; + foreach_vmbus_pkt(desc, hv_dev->channel) { + bytes_recvd = desc->len8 * 8; + req_id = desc->trans_id; - case -ENOBUFS: - kfree(buffer); - /* Handle large packet */ - bufferlen = bytes_recvd; - buffer = kmalloc(bytes_recvd, GFP_ATOMIC); - if (!buffer) - return; - break; - } + hv_kbd_handle_received_packet(hv_dev, desc, bytes_recvd, + req_id); } } @@ -290,6 +259,8 @@ static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev) u32 proto_status; int error; + reinit_completion(&kbd_dev->wait_event); + request = &kbd_dev->protocol_req; memset(request, 0, sizeof(struct synth_kbd_protocol_request)); request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST); @@ -321,11 +292,10 @@ static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev) 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); + guard(spinlock_irqsave)(&kbd_dev->lock); + kbd_dev->started = true; - spin_unlock_irqrestore(&kbd_dev->lock, flags); return 0; } @@ -333,11 +303,10 @@ static int hv_kbd_start(struct serio *serio) 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); + guard(spinlock_irqsave)(&kbd_dev->lock); + kbd_dev->started = false; - spin_unlock_irqrestore(&kbd_dev->lock, flags); } static int hv_kbd_probe(struct hv_device *hv_dev, @@ -347,8 +316,8 @@ static int hv_kbd_probe(struct hv_device *hv_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); + kbd_dev = kzalloc(sizeof(*kbd_dev), GFP_KERNEL); + hv_serio = kzalloc(sizeof(*hv_serio), GFP_KERNEL); if (!kbd_dev || !hv_serio) { error = -ENOMEM; goto err_free_mem; @@ -363,9 +332,9 @@ static int hv_kbd_probe(struct hv_device *hv_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), + strscpy(hv_serio->name, dev_name(&hv_dev->device), sizeof(hv_serio->name)); - strlcpy(hv_serio->phys, dev_name(&hv_dev->device), + strscpy(hv_serio->phys, dev_name(&hv_dev->device), sizeof(hv_serio->phys)); hv_serio->start = hv_kbd_start; @@ -398,7 +367,7 @@ err_free_mem: return error; } -static int hv_kbd_remove(struct hv_device *hv_dev) +static void hv_kbd_remove(struct hv_device *hv_dev) { struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); @@ -407,10 +376,31 @@ static int hv_kbd_remove(struct hv_device *hv_dev) kfree(kbd_dev); hv_set_drvdata(hv_dev, NULL); +} + +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; +} + static const struct hv_vmbus_device_id id_table[] = { /* Keyboard guid */ { HV_KBD_GUID, }, @@ -424,6 +414,11 @@ static struct hv_driver hv_kbd_drv = { .id_table = id_table, .probe = hv_kbd_probe, .remove = hv_kbd_remove, + .suspend = hv_kbd_suspend, + .resume = hv_kbd_resume, + .driver = { + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, }; static int __init hv_kbd_init(void) @@ -437,5 +432,7 @@ static void __exit hv_kbd_exit(void) } MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Keyboard Driver"); + module_init(hv_kbd_init); module_exit(hv_kbd_exit); |
