We need hv_kbd_pm_notify() to make sure the pm_wakeup_hard_event() call does not prevent the system from entering hibernation: the hibernation is a relatively long process, which can be aborted by the call pm_wakeup_hard_event(), which is invoked upon keyboard events. Signed-off-by: Dexuan Cui <decui@xxxxxxxxxxxxx> --- This patch is basically a pure Hyper-V specific change and it has a build dependency on the commit 271b2224d42f ("Drivers: hv: vmbus: Implement suspend/resume for VSC drivers for hibernation"), which is on Sasha Levin's Hyper-V tree's hyperv-next branch: https://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git/log/?h=hyperv-next I request this patch should go through Sasha's tree rather than the input subsystemi's tree. Hi Dmitry, can you please Ack? drivers/input/serio/hyperv-keyboard.c | 68 ++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index 88ae7c2..277dc4c 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -10,6 +10,7 @@ #include <linux/hyperv.h> #include <linux/serio.h> #include <linux/slab.h> +#include <linux/suspend.h> /* * Current version 1.0 @@ -95,6 +96,9 @@ struct hv_kbd_dev { struct completion wait_event; spinlock_t lock; /* protects 'started' field */ bool started; + + struct notifier_block pm_nb; + bool hibernation_in_progress; }; static void hv_kbd_on_receive(struct hv_device *hv_dev, @@ -168,7 +172,7 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, * "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)) + if (!(info & IS_BREAK) && !kbd_dev->hibernation_in_progress) pm_wakeup_hard_event(&hv_dev->device); break; @@ -179,10 +183,10 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, } } -static void hv_kbd_handle_received_packet(struct hv_device *hv_dev, - struct vmpacket_descriptor *desc, - u32 bytes_recvd, - u64 req_id) +static void +hv_kbd_handle_received_packet(struct hv_device *hv_dev, + const struct vmpacket_descriptor *desc, + u32 bytes_recvd, u64 req_id) { struct synth_kbd_msg *msg; u32 msg_sz; @@ -282,6 +286,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); @@ -332,6 +338,29 @@ static void hv_kbd_stop(struct serio *serio) spin_unlock_irqrestore(&kbd_dev->lock, flags); } +static int hv_kbd_pm_notify(struct notifier_block *nb, + unsigned long val, void *ign) +{ + struct hv_kbd_dev *kbd_dev; + + kbd_dev = container_of(nb, struct hv_kbd_dev, pm_nb); + + switch (val) { + case PM_HIBERNATION_PREPARE: + case PM_RESTORE_PREPARE: + kbd_dev->hibernation_in_progress = true; + return NOTIFY_OK; + + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + kbd_dev->hibernation_in_progress = false; + return NOTIFY_OK; + + default: + return NOTIFY_DONE; + } +} + static int hv_kbd_probe(struct hv_device *hv_dev, const struct hv_vmbus_device_id *dev_id) { @@ -380,6 +409,9 @@ static int hv_kbd_probe(struct hv_device *hv_dev, device_init_wakeup(&hv_dev->device, true); + kbd_dev->pm_nb.notifier_call = hv_kbd_pm_notify; + register_pm_notifier(&kbd_dev->pm_nb); + return 0; err_close_vmbus: @@ -394,6 +426,7 @@ static int hv_kbd_remove(struct hv_device *hv_dev) { struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + unregister_pm_notifier(&kbd_dev->pm_nb); serio_unregister_port(kbd_dev->hv_serio); vmbus_close(hv_dev->channel); kfree(kbd_dev); @@ -403,6 +436,29 @@ static int hv_kbd_remove(struct hv_device *hv_dev) return 0; } +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, }, @@ -416,6 +472,8 @@ static int hv_kbd_remove(struct hv_device *hv_dev) .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, }, -- 1.8.3.1