162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2013, Microsoft Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/completion.h> 1062306a36Sopenharmony_ci#include <linux/hyperv.h> 1162306a36Sopenharmony_ci#include <linux/serio.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Current version 1.0 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci#define SYNTH_KBD_VERSION_MAJOR 1 1962306a36Sopenharmony_ci#define SYNTH_KBD_VERSION_MINOR 0 2062306a36Sopenharmony_ci#define SYNTH_KBD_VERSION (SYNTH_KBD_VERSION_MINOR | \ 2162306a36Sopenharmony_ci (SYNTH_KBD_VERSION_MAJOR << 16)) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Message types in the synthetic input protocol 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_cienum synth_kbd_msg_type { 2862306a36Sopenharmony_ci SYNTH_KBD_PROTOCOL_REQUEST = 1, 2962306a36Sopenharmony_ci SYNTH_KBD_PROTOCOL_RESPONSE = 2, 3062306a36Sopenharmony_ci SYNTH_KBD_EVENT = 3, 3162306a36Sopenharmony_ci SYNTH_KBD_LED_INDICATORS = 4, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Basic message structures. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistruct synth_kbd_msg_hdr { 3862306a36Sopenharmony_ci __le32 type; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct synth_kbd_msg { 4262306a36Sopenharmony_ci struct synth_kbd_msg_hdr header; 4362306a36Sopenharmony_ci char data[]; /* Enclosed message */ 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciunion synth_kbd_version { 4762306a36Sopenharmony_ci __le32 version; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * Protocol messages 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistruct synth_kbd_protocol_request { 5462306a36Sopenharmony_ci struct synth_kbd_msg_hdr header; 5562306a36Sopenharmony_ci union synth_kbd_version version_requested; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define PROTOCOL_ACCEPTED BIT(0) 5962306a36Sopenharmony_cistruct synth_kbd_protocol_response { 6062306a36Sopenharmony_ci struct synth_kbd_msg_hdr header; 6162306a36Sopenharmony_ci __le32 proto_status; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define IS_UNICODE BIT(0) 6562306a36Sopenharmony_ci#define IS_BREAK BIT(1) 6662306a36Sopenharmony_ci#define IS_E0 BIT(2) 6762306a36Sopenharmony_ci#define IS_E1 BIT(3) 6862306a36Sopenharmony_cistruct synth_kbd_keystroke { 6962306a36Sopenharmony_ci struct synth_kbd_msg_hdr header; 7062306a36Sopenharmony_ci __le16 make_code; 7162306a36Sopenharmony_ci __le16 reserved0; 7262306a36Sopenharmony_ci __le32 info; /* Additional information */ 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define HK_MAXIMUM_MESSAGE_SIZE 256 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define KBD_VSC_SEND_RING_BUFFER_SIZE VMBUS_RING_SIZE(36 * 1024) 7962306a36Sopenharmony_ci#define KBD_VSC_RECV_RING_BUFFER_SIZE VMBUS_RING_SIZE(36 * 1024) 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define XTKBD_EMUL0 0xe0 8262306a36Sopenharmony_ci#define XTKBD_EMUL1 0xe1 8362306a36Sopenharmony_ci#define XTKBD_RELEASE 0x80 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * Represents a keyboard device 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistruct hv_kbd_dev { 9062306a36Sopenharmony_ci struct hv_device *hv_dev; 9162306a36Sopenharmony_ci struct serio *hv_serio; 9262306a36Sopenharmony_ci struct synth_kbd_protocol_request protocol_req; 9362306a36Sopenharmony_ci struct synth_kbd_protocol_response protocol_resp; 9462306a36Sopenharmony_ci /* Synchronize the request/response if needed */ 9562306a36Sopenharmony_ci struct completion wait_event; 9662306a36Sopenharmony_ci spinlock_t lock; /* protects 'started' field */ 9762306a36Sopenharmony_ci bool started; 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void hv_kbd_on_receive(struct hv_device *hv_dev, 10162306a36Sopenharmony_ci struct synth_kbd_msg *msg, u32 msg_length) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); 10462306a36Sopenharmony_ci struct synth_kbd_keystroke *ks_msg; 10562306a36Sopenharmony_ci unsigned long flags; 10662306a36Sopenharmony_ci u32 msg_type = __le32_to_cpu(msg->header.type); 10762306a36Sopenharmony_ci u32 info; 10862306a36Sopenharmony_ci u16 scan_code; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci switch (msg_type) { 11162306a36Sopenharmony_ci case SYNTH_KBD_PROTOCOL_RESPONSE: 11262306a36Sopenharmony_ci /* 11362306a36Sopenharmony_ci * Validate the information provided by the host. 11462306a36Sopenharmony_ci * If the host is giving us a bogus packet, 11562306a36Sopenharmony_ci * drop the packet (hoping the problem 11662306a36Sopenharmony_ci * goes away). 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci if (msg_length < sizeof(struct synth_kbd_protocol_response)) { 11962306a36Sopenharmony_ci dev_err(&hv_dev->device, 12062306a36Sopenharmony_ci "Illegal protocol response packet (len: %d)\n", 12162306a36Sopenharmony_ci msg_length); 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci memcpy(&kbd_dev->protocol_resp, msg, 12662306a36Sopenharmony_ci sizeof(struct synth_kbd_protocol_response)); 12762306a36Sopenharmony_ci complete(&kbd_dev->wait_event); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci case SYNTH_KBD_EVENT: 13162306a36Sopenharmony_ci /* 13262306a36Sopenharmony_ci * Validate the information provided by the host. 13362306a36Sopenharmony_ci * If the host is giving us a bogus packet, 13462306a36Sopenharmony_ci * drop the packet (hoping the problem 13562306a36Sopenharmony_ci * goes away). 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci if (msg_length < sizeof(struct synth_kbd_keystroke)) { 13862306a36Sopenharmony_ci dev_err(&hv_dev->device, 13962306a36Sopenharmony_ci "Illegal keyboard event packet (len: %d)\n", 14062306a36Sopenharmony_ci msg_length); 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ks_msg = (struct synth_kbd_keystroke *)msg; 14562306a36Sopenharmony_ci info = __le32_to_cpu(ks_msg->info); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* 14862306a36Sopenharmony_ci * Inject the information through the serio interrupt. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci spin_lock_irqsave(&kbd_dev->lock, flags); 15162306a36Sopenharmony_ci if (kbd_dev->started) { 15262306a36Sopenharmony_ci if (info & IS_E0) 15362306a36Sopenharmony_ci serio_interrupt(kbd_dev->hv_serio, 15462306a36Sopenharmony_ci XTKBD_EMUL0, 0); 15562306a36Sopenharmony_ci if (info & IS_E1) 15662306a36Sopenharmony_ci serio_interrupt(kbd_dev->hv_serio, 15762306a36Sopenharmony_ci XTKBD_EMUL1, 0); 15862306a36Sopenharmony_ci scan_code = __le16_to_cpu(ks_msg->make_code); 15962306a36Sopenharmony_ci if (info & IS_BREAK) 16062306a36Sopenharmony_ci scan_code |= XTKBD_RELEASE; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci serio_interrupt(kbd_dev->hv_serio, scan_code, 0); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci spin_unlock_irqrestore(&kbd_dev->lock, flags); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * Only trigger a wakeup on key down, otherwise 16862306a36Sopenharmony_ci * "echo freeze > /sys/power/state" can't really enter the 16962306a36Sopenharmony_ci * state because the Enter-UP can trigger a wakeup at once. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci if (!(info & IS_BREAK)) 17262306a36Sopenharmony_ci pm_wakeup_hard_event(&hv_dev->device); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci default: 17762306a36Sopenharmony_ci dev_err(&hv_dev->device, 17862306a36Sopenharmony_ci "unhandled message type %d\n", msg_type); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void hv_kbd_handle_received_packet(struct hv_device *hv_dev, 18362306a36Sopenharmony_ci struct vmpacket_descriptor *desc, 18462306a36Sopenharmony_ci u32 bytes_recvd, 18562306a36Sopenharmony_ci u64 req_id) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct synth_kbd_msg *msg; 18862306a36Sopenharmony_ci u32 msg_sz; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci switch (desc->type) { 19162306a36Sopenharmony_ci case VM_PKT_COMP: 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci case VM_PKT_DATA_INBAND: 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * We have a packet that has "inband" data. The API used 19762306a36Sopenharmony_ci * for retrieving the packet guarantees that the complete 19862306a36Sopenharmony_ci * packet is read. So, minimally, we should be able to 19962306a36Sopenharmony_ci * parse the payload header safely (assuming that the host 20062306a36Sopenharmony_ci * can be trusted. Trusting the host seems to be a 20162306a36Sopenharmony_ci * reasonable assumption because in a virtualized 20262306a36Sopenharmony_ci * environment there is not whole lot you can do if you 20362306a36Sopenharmony_ci * don't trust the host. 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * Nonetheless, let us validate if the host can be trusted 20662306a36Sopenharmony_ci * (in a trivial way). The interesting aspect of this 20762306a36Sopenharmony_ci * validation is how do you recover if we discover that the 20862306a36Sopenharmony_ci * host is not to be trusted? Simply dropping the packet, I 20962306a36Sopenharmony_ci * don't think is an appropriate recovery. In the interest 21062306a36Sopenharmony_ci * of failing fast, it may be better to crash the guest. 21162306a36Sopenharmony_ci * For now, I will just drop the packet! 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci msg_sz = bytes_recvd - (desc->offset8 << 3); 21562306a36Sopenharmony_ci if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) { 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * Drop the packet and hope 21862306a36Sopenharmony_ci * the problem magically goes away. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci dev_err(&hv_dev->device, 22162306a36Sopenharmony_ci "Illegal packet (type: %d, tid: %llx, size: %d)\n", 22262306a36Sopenharmony_ci desc->type, req_id, msg_sz); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci msg = (void *)desc + (desc->offset8 << 3); 22762306a36Sopenharmony_ci hv_kbd_on_receive(hv_dev, msg, msg_sz); 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci default: 23162306a36Sopenharmony_ci dev_err(&hv_dev->device, 23262306a36Sopenharmony_ci "unhandled packet type %d, tid %llx len %d\n", 23362306a36Sopenharmony_ci desc->type, req_id, bytes_recvd); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic void hv_kbd_on_channel_callback(void *context) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct vmpacket_descriptor *desc; 24162306a36Sopenharmony_ci struct hv_device *hv_dev = context; 24262306a36Sopenharmony_ci u32 bytes_recvd; 24362306a36Sopenharmony_ci u64 req_id; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci foreach_vmbus_pkt(desc, hv_dev->channel) { 24662306a36Sopenharmony_ci bytes_recvd = desc->len8 * 8; 24762306a36Sopenharmony_ci req_id = desc->trans_id; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci hv_kbd_handle_received_packet(hv_dev, desc, bytes_recvd, 25062306a36Sopenharmony_ci req_id); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int hv_kbd_connect_to_vsp(struct hv_device *hv_dev) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); 25762306a36Sopenharmony_ci struct synth_kbd_protocol_request *request; 25862306a36Sopenharmony_ci struct synth_kbd_protocol_response *response; 25962306a36Sopenharmony_ci u32 proto_status; 26062306a36Sopenharmony_ci int error; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci reinit_completion(&kbd_dev->wait_event); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci request = &kbd_dev->protocol_req; 26562306a36Sopenharmony_ci memset(request, 0, sizeof(struct synth_kbd_protocol_request)); 26662306a36Sopenharmony_ci request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST); 26762306a36Sopenharmony_ci request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci error = vmbus_sendpacket(hv_dev->channel, request, 27062306a36Sopenharmony_ci sizeof(struct synth_kbd_protocol_request), 27162306a36Sopenharmony_ci (unsigned long)request, 27262306a36Sopenharmony_ci VM_PKT_DATA_INBAND, 27362306a36Sopenharmony_ci VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 27462306a36Sopenharmony_ci if (error) 27562306a36Sopenharmony_ci return error; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ)) 27862306a36Sopenharmony_ci return -ETIMEDOUT; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci response = &kbd_dev->protocol_resp; 28162306a36Sopenharmony_ci proto_status = __le32_to_cpu(response->proto_status); 28262306a36Sopenharmony_ci if (!(proto_status & PROTOCOL_ACCEPTED)) { 28362306a36Sopenharmony_ci dev_err(&hv_dev->device, 28462306a36Sopenharmony_ci "synth_kbd protocol request failed (version %d)\n", 28562306a36Sopenharmony_ci SYNTH_KBD_VERSION); 28662306a36Sopenharmony_ci return -ENODEV; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int hv_kbd_start(struct serio *serio) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct hv_kbd_dev *kbd_dev = serio->port_data; 29562306a36Sopenharmony_ci unsigned long flags; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci spin_lock_irqsave(&kbd_dev->lock, flags); 29862306a36Sopenharmony_ci kbd_dev->started = true; 29962306a36Sopenharmony_ci spin_unlock_irqrestore(&kbd_dev->lock, flags); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void hv_kbd_stop(struct serio *serio) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct hv_kbd_dev *kbd_dev = serio->port_data; 30762306a36Sopenharmony_ci unsigned long flags; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci spin_lock_irqsave(&kbd_dev->lock, flags); 31062306a36Sopenharmony_ci kbd_dev->started = false; 31162306a36Sopenharmony_ci spin_unlock_irqrestore(&kbd_dev->lock, flags); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int hv_kbd_probe(struct hv_device *hv_dev, 31562306a36Sopenharmony_ci const struct hv_vmbus_device_id *dev_id) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct hv_kbd_dev *kbd_dev; 31862306a36Sopenharmony_ci struct serio *hv_serio; 31962306a36Sopenharmony_ci int error; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci kbd_dev = kzalloc(sizeof(struct hv_kbd_dev), GFP_KERNEL); 32262306a36Sopenharmony_ci hv_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 32362306a36Sopenharmony_ci if (!kbd_dev || !hv_serio) { 32462306a36Sopenharmony_ci error = -ENOMEM; 32562306a36Sopenharmony_ci goto err_free_mem; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci kbd_dev->hv_dev = hv_dev; 32962306a36Sopenharmony_ci kbd_dev->hv_serio = hv_serio; 33062306a36Sopenharmony_ci spin_lock_init(&kbd_dev->lock); 33162306a36Sopenharmony_ci init_completion(&kbd_dev->wait_event); 33262306a36Sopenharmony_ci hv_set_drvdata(hv_dev, kbd_dev); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci hv_serio->dev.parent = &hv_dev->device; 33562306a36Sopenharmony_ci hv_serio->id.type = SERIO_8042_XL; 33662306a36Sopenharmony_ci hv_serio->port_data = kbd_dev; 33762306a36Sopenharmony_ci strscpy(hv_serio->name, dev_name(&hv_dev->device), 33862306a36Sopenharmony_ci sizeof(hv_serio->name)); 33962306a36Sopenharmony_ci strscpy(hv_serio->phys, dev_name(&hv_dev->device), 34062306a36Sopenharmony_ci sizeof(hv_serio->phys)); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci hv_serio->start = hv_kbd_start; 34362306a36Sopenharmony_ci hv_serio->stop = hv_kbd_stop; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci error = vmbus_open(hv_dev->channel, 34662306a36Sopenharmony_ci KBD_VSC_SEND_RING_BUFFER_SIZE, 34762306a36Sopenharmony_ci KBD_VSC_RECV_RING_BUFFER_SIZE, 34862306a36Sopenharmony_ci NULL, 0, 34962306a36Sopenharmony_ci hv_kbd_on_channel_callback, 35062306a36Sopenharmony_ci hv_dev); 35162306a36Sopenharmony_ci if (error) 35262306a36Sopenharmony_ci goto err_free_mem; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci error = hv_kbd_connect_to_vsp(hv_dev); 35562306a36Sopenharmony_ci if (error) 35662306a36Sopenharmony_ci goto err_close_vmbus; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci serio_register_port(kbd_dev->hv_serio); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci device_init_wakeup(&hv_dev->device, true); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cierr_close_vmbus: 36562306a36Sopenharmony_ci vmbus_close(hv_dev->channel); 36662306a36Sopenharmony_cierr_free_mem: 36762306a36Sopenharmony_ci kfree(hv_serio); 36862306a36Sopenharmony_ci kfree(kbd_dev); 36962306a36Sopenharmony_ci return error; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void hv_kbd_remove(struct hv_device *hv_dev) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci serio_unregister_port(kbd_dev->hv_serio); 37762306a36Sopenharmony_ci vmbus_close(hv_dev->channel); 37862306a36Sopenharmony_ci kfree(kbd_dev); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci hv_set_drvdata(hv_dev, NULL); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int hv_kbd_suspend(struct hv_device *hv_dev) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci vmbus_close(hv_dev->channel); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int hv_kbd_resume(struct hv_device *hv_dev) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int ret; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci ret = vmbus_open(hv_dev->channel, 39562306a36Sopenharmony_ci KBD_VSC_SEND_RING_BUFFER_SIZE, 39662306a36Sopenharmony_ci KBD_VSC_RECV_RING_BUFFER_SIZE, 39762306a36Sopenharmony_ci NULL, 0, 39862306a36Sopenharmony_ci hv_kbd_on_channel_callback, 39962306a36Sopenharmony_ci hv_dev); 40062306a36Sopenharmony_ci if (ret == 0) 40162306a36Sopenharmony_ci ret = hv_kbd_connect_to_vsp(hv_dev); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic const struct hv_vmbus_device_id id_table[] = { 40762306a36Sopenharmony_ci /* Keyboard guid */ 40862306a36Sopenharmony_ci { HV_KBD_GUID, }, 40962306a36Sopenharmony_ci { }, 41062306a36Sopenharmony_ci}; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vmbus, id_table); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic struct hv_driver hv_kbd_drv = { 41562306a36Sopenharmony_ci .name = KBUILD_MODNAME, 41662306a36Sopenharmony_ci .id_table = id_table, 41762306a36Sopenharmony_ci .probe = hv_kbd_probe, 41862306a36Sopenharmony_ci .remove = hv_kbd_remove, 41962306a36Sopenharmony_ci .suspend = hv_kbd_suspend, 42062306a36Sopenharmony_ci .resume = hv_kbd_resume, 42162306a36Sopenharmony_ci .driver = { 42262306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 42362306a36Sopenharmony_ci }, 42462306a36Sopenharmony_ci}; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int __init hv_kbd_init(void) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci return vmbus_driver_register(&hv_kbd_drv); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void __exit hv_kbd_exit(void) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci vmbus_driver_unregister(&hv_kbd_drv); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 43762306a36Sopenharmony_ciMODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Keyboard Driver"); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cimodule_init(hv_kbd_init); 44062306a36Sopenharmony_cimodule_exit(hv_kbd_exit); 441