18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013, Microsoft Corporation. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/completion.h> 108c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 118c2ecf20Sopenharmony_ci#include <linux/serio.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * Current version 1.0 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#define SYNTH_KBD_VERSION_MAJOR 1 198c2ecf20Sopenharmony_ci#define SYNTH_KBD_VERSION_MINOR 0 208c2ecf20Sopenharmony_ci#define SYNTH_KBD_VERSION (SYNTH_KBD_VERSION_MINOR | \ 218c2ecf20Sopenharmony_ci (SYNTH_KBD_VERSION_MAJOR << 16)) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * Message types in the synthetic input protocol 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_cienum synth_kbd_msg_type { 288c2ecf20Sopenharmony_ci SYNTH_KBD_PROTOCOL_REQUEST = 1, 298c2ecf20Sopenharmony_ci SYNTH_KBD_PROTOCOL_RESPONSE = 2, 308c2ecf20Sopenharmony_ci SYNTH_KBD_EVENT = 3, 318c2ecf20Sopenharmony_ci SYNTH_KBD_LED_INDICATORS = 4, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * Basic message structures. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistruct synth_kbd_msg_hdr { 388c2ecf20Sopenharmony_ci __le32 type; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct synth_kbd_msg { 428c2ecf20Sopenharmony_ci struct synth_kbd_msg_hdr header; 438c2ecf20Sopenharmony_ci char data[]; /* Enclosed message */ 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciunion synth_kbd_version { 478c2ecf20Sopenharmony_ci __le32 version; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * Protocol messages 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistruct synth_kbd_protocol_request { 548c2ecf20Sopenharmony_ci struct synth_kbd_msg_hdr header; 558c2ecf20Sopenharmony_ci union synth_kbd_version version_requested; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define PROTOCOL_ACCEPTED BIT(0) 598c2ecf20Sopenharmony_cistruct synth_kbd_protocol_response { 608c2ecf20Sopenharmony_ci struct synth_kbd_msg_hdr header; 618c2ecf20Sopenharmony_ci __le32 proto_status; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define IS_UNICODE BIT(0) 658c2ecf20Sopenharmony_ci#define IS_BREAK BIT(1) 668c2ecf20Sopenharmony_ci#define IS_E0 BIT(2) 678c2ecf20Sopenharmony_ci#define IS_E1 BIT(3) 688c2ecf20Sopenharmony_cistruct synth_kbd_keystroke { 698c2ecf20Sopenharmony_ci struct synth_kbd_msg_hdr header; 708c2ecf20Sopenharmony_ci __le16 make_code; 718c2ecf20Sopenharmony_ci __le16 reserved0; 728c2ecf20Sopenharmony_ci __le32 info; /* Additional information */ 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define HK_MAXIMUM_MESSAGE_SIZE 256 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define KBD_VSC_SEND_RING_BUFFER_SIZE VMBUS_RING_SIZE(36 * 1024) 798c2ecf20Sopenharmony_ci#define KBD_VSC_RECV_RING_BUFFER_SIZE VMBUS_RING_SIZE(36 * 1024) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define XTKBD_EMUL0 0xe0 828c2ecf20Sopenharmony_ci#define XTKBD_EMUL1 0xe1 838c2ecf20Sopenharmony_ci#define XTKBD_RELEASE 0x80 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * Represents a keyboard device 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistruct hv_kbd_dev { 908c2ecf20Sopenharmony_ci struct hv_device *hv_dev; 918c2ecf20Sopenharmony_ci struct serio *hv_serio; 928c2ecf20Sopenharmony_ci struct synth_kbd_protocol_request protocol_req; 938c2ecf20Sopenharmony_ci struct synth_kbd_protocol_response protocol_resp; 948c2ecf20Sopenharmony_ci /* Synchronize the request/response if needed */ 958c2ecf20Sopenharmony_ci struct completion wait_event; 968c2ecf20Sopenharmony_ci spinlock_t lock; /* protects 'started' field */ 978c2ecf20Sopenharmony_ci bool started; 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void hv_kbd_on_receive(struct hv_device *hv_dev, 1018c2ecf20Sopenharmony_ci struct synth_kbd_msg *msg, u32 msg_length) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); 1048c2ecf20Sopenharmony_ci struct synth_kbd_keystroke *ks_msg; 1058c2ecf20Sopenharmony_ci unsigned long flags; 1068c2ecf20Sopenharmony_ci u32 msg_type = __le32_to_cpu(msg->header.type); 1078c2ecf20Sopenharmony_ci u32 info; 1088c2ecf20Sopenharmony_ci u16 scan_code; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci switch (msg_type) { 1118c2ecf20Sopenharmony_ci case SYNTH_KBD_PROTOCOL_RESPONSE: 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * Validate the information provided by the host. 1148c2ecf20Sopenharmony_ci * If the host is giving us a bogus packet, 1158c2ecf20Sopenharmony_ci * drop the packet (hoping the problem 1168c2ecf20Sopenharmony_ci * goes away). 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci if (msg_length < sizeof(struct synth_kbd_protocol_response)) { 1198c2ecf20Sopenharmony_ci dev_err(&hv_dev->device, 1208c2ecf20Sopenharmony_ci "Illegal protocol response packet (len: %d)\n", 1218c2ecf20Sopenharmony_ci msg_length); 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci memcpy(&kbd_dev->protocol_resp, msg, 1268c2ecf20Sopenharmony_ci sizeof(struct synth_kbd_protocol_response)); 1278c2ecf20Sopenharmony_ci complete(&kbd_dev->wait_event); 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci case SYNTH_KBD_EVENT: 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * Validate the information provided by the host. 1338c2ecf20Sopenharmony_ci * If the host is giving us a bogus packet, 1348c2ecf20Sopenharmony_ci * drop the packet (hoping the problem 1358c2ecf20Sopenharmony_ci * goes away). 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci if (msg_length < sizeof(struct synth_kbd_keystroke)) { 1388c2ecf20Sopenharmony_ci dev_err(&hv_dev->device, 1398c2ecf20Sopenharmony_ci "Illegal keyboard event packet (len: %d)\n", 1408c2ecf20Sopenharmony_ci msg_length); 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ks_msg = (struct synth_kbd_keystroke *)msg; 1458c2ecf20Sopenharmony_ci info = __le32_to_cpu(ks_msg->info); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * Inject the information through the serio interrupt. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci spin_lock_irqsave(&kbd_dev->lock, flags); 1518c2ecf20Sopenharmony_ci if (kbd_dev->started) { 1528c2ecf20Sopenharmony_ci if (info & IS_E0) 1538c2ecf20Sopenharmony_ci serio_interrupt(kbd_dev->hv_serio, 1548c2ecf20Sopenharmony_ci XTKBD_EMUL0, 0); 1558c2ecf20Sopenharmony_ci if (info & IS_E1) 1568c2ecf20Sopenharmony_ci serio_interrupt(kbd_dev->hv_serio, 1578c2ecf20Sopenharmony_ci XTKBD_EMUL1, 0); 1588c2ecf20Sopenharmony_ci scan_code = __le16_to_cpu(ks_msg->make_code); 1598c2ecf20Sopenharmony_ci if (info & IS_BREAK) 1608c2ecf20Sopenharmony_ci scan_code |= XTKBD_RELEASE; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci serio_interrupt(kbd_dev->hv_serio, scan_code, 0); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kbd_dev->lock, flags); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * Only trigger a wakeup on key down, otherwise 1688c2ecf20Sopenharmony_ci * "echo freeze > /sys/power/state" can't really enter the 1698c2ecf20Sopenharmony_ci * state because the Enter-UP can trigger a wakeup at once. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci if (!(info & IS_BREAK)) 1728c2ecf20Sopenharmony_ci pm_wakeup_hard_event(&hv_dev->device); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci default: 1778c2ecf20Sopenharmony_ci dev_err(&hv_dev->device, 1788c2ecf20Sopenharmony_ci "unhandled message type %d\n", msg_type); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void hv_kbd_handle_received_packet(struct hv_device *hv_dev, 1838c2ecf20Sopenharmony_ci struct vmpacket_descriptor *desc, 1848c2ecf20Sopenharmony_ci u32 bytes_recvd, 1858c2ecf20Sopenharmony_ci u64 req_id) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct synth_kbd_msg *msg; 1888c2ecf20Sopenharmony_ci u32 msg_sz; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci switch (desc->type) { 1918c2ecf20Sopenharmony_ci case VM_PKT_COMP: 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci case VM_PKT_DATA_INBAND: 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * We have a packet that has "inband" data. The API used 1978c2ecf20Sopenharmony_ci * for retrieving the packet guarantees that the complete 1988c2ecf20Sopenharmony_ci * packet is read. So, minimally, we should be able to 1998c2ecf20Sopenharmony_ci * parse the payload header safely (assuming that the host 2008c2ecf20Sopenharmony_ci * can be trusted. Trusting the host seems to be a 2018c2ecf20Sopenharmony_ci * reasonable assumption because in a virtualized 2028c2ecf20Sopenharmony_ci * environment there is not whole lot you can do if you 2038c2ecf20Sopenharmony_ci * don't trust the host. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * Nonetheless, let us validate if the host can be trusted 2068c2ecf20Sopenharmony_ci * (in a trivial way). The interesting aspect of this 2078c2ecf20Sopenharmony_ci * validation is how do you recover if we discover that the 2088c2ecf20Sopenharmony_ci * host is not to be trusted? Simply dropping the packet, I 2098c2ecf20Sopenharmony_ci * don't think is an appropriate recovery. In the interest 2108c2ecf20Sopenharmony_ci * of failing fast, it may be better to crash the guest. 2118c2ecf20Sopenharmony_ci * For now, I will just drop the packet! 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci msg_sz = bytes_recvd - (desc->offset8 << 3); 2158c2ecf20Sopenharmony_ci if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) { 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * Drop the packet and hope 2188c2ecf20Sopenharmony_ci * the problem magically goes away. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci dev_err(&hv_dev->device, 2218c2ecf20Sopenharmony_ci "Illegal packet (type: %d, tid: %llx, size: %d)\n", 2228c2ecf20Sopenharmony_ci desc->type, req_id, msg_sz); 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci msg = (void *)desc + (desc->offset8 << 3); 2278c2ecf20Sopenharmony_ci hv_kbd_on_receive(hv_dev, msg, msg_sz); 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci default: 2318c2ecf20Sopenharmony_ci dev_err(&hv_dev->device, 2328c2ecf20Sopenharmony_ci "unhandled packet type %d, tid %llx len %d\n", 2338c2ecf20Sopenharmony_ci desc->type, req_id, bytes_recvd); 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void hv_kbd_on_channel_callback(void *context) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct vmpacket_descriptor *desc; 2418c2ecf20Sopenharmony_ci struct hv_device *hv_dev = context; 2428c2ecf20Sopenharmony_ci u32 bytes_recvd; 2438c2ecf20Sopenharmony_ci u64 req_id; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci foreach_vmbus_pkt(desc, hv_dev->channel) { 2468c2ecf20Sopenharmony_ci bytes_recvd = desc->len8 * 8; 2478c2ecf20Sopenharmony_ci req_id = desc->trans_id; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci hv_kbd_handle_received_packet(hv_dev, desc, bytes_recvd, 2508c2ecf20Sopenharmony_ci req_id); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int hv_kbd_connect_to_vsp(struct hv_device *hv_dev) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); 2578c2ecf20Sopenharmony_ci struct synth_kbd_protocol_request *request; 2588c2ecf20Sopenharmony_ci struct synth_kbd_protocol_response *response; 2598c2ecf20Sopenharmony_ci u32 proto_status; 2608c2ecf20Sopenharmony_ci int error; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci reinit_completion(&kbd_dev->wait_event); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci request = &kbd_dev->protocol_req; 2658c2ecf20Sopenharmony_ci memset(request, 0, sizeof(struct synth_kbd_protocol_request)); 2668c2ecf20Sopenharmony_ci request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST); 2678c2ecf20Sopenharmony_ci request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci error = vmbus_sendpacket(hv_dev->channel, request, 2708c2ecf20Sopenharmony_ci sizeof(struct synth_kbd_protocol_request), 2718c2ecf20Sopenharmony_ci (unsigned long)request, 2728c2ecf20Sopenharmony_ci VM_PKT_DATA_INBAND, 2738c2ecf20Sopenharmony_ci VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); 2748c2ecf20Sopenharmony_ci if (error) 2758c2ecf20Sopenharmony_ci return error; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ)) 2788c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci response = &kbd_dev->protocol_resp; 2818c2ecf20Sopenharmony_ci proto_status = __le32_to_cpu(response->proto_status); 2828c2ecf20Sopenharmony_ci if (!(proto_status & PROTOCOL_ACCEPTED)) { 2838c2ecf20Sopenharmony_ci dev_err(&hv_dev->device, 2848c2ecf20Sopenharmony_ci "synth_kbd protocol request failed (version %d)\n", 2858c2ecf20Sopenharmony_ci SYNTH_KBD_VERSION); 2868c2ecf20Sopenharmony_ci return -ENODEV; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int hv_kbd_start(struct serio *serio) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct hv_kbd_dev *kbd_dev = serio->port_data; 2958c2ecf20Sopenharmony_ci unsigned long flags; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci spin_lock_irqsave(&kbd_dev->lock, flags); 2988c2ecf20Sopenharmony_ci kbd_dev->started = true; 2998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kbd_dev->lock, flags); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic void hv_kbd_stop(struct serio *serio) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct hv_kbd_dev *kbd_dev = serio->port_data; 3078c2ecf20Sopenharmony_ci unsigned long flags; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci spin_lock_irqsave(&kbd_dev->lock, flags); 3108c2ecf20Sopenharmony_ci kbd_dev->started = false; 3118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kbd_dev->lock, flags); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int hv_kbd_probe(struct hv_device *hv_dev, 3158c2ecf20Sopenharmony_ci const struct hv_vmbus_device_id *dev_id) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct hv_kbd_dev *kbd_dev; 3188c2ecf20Sopenharmony_ci struct serio *hv_serio; 3198c2ecf20Sopenharmony_ci int error; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci kbd_dev = kzalloc(sizeof(struct hv_kbd_dev), GFP_KERNEL); 3228c2ecf20Sopenharmony_ci hv_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 3238c2ecf20Sopenharmony_ci if (!kbd_dev || !hv_serio) { 3248c2ecf20Sopenharmony_ci error = -ENOMEM; 3258c2ecf20Sopenharmony_ci goto err_free_mem; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci kbd_dev->hv_dev = hv_dev; 3298c2ecf20Sopenharmony_ci kbd_dev->hv_serio = hv_serio; 3308c2ecf20Sopenharmony_ci spin_lock_init(&kbd_dev->lock); 3318c2ecf20Sopenharmony_ci init_completion(&kbd_dev->wait_event); 3328c2ecf20Sopenharmony_ci hv_set_drvdata(hv_dev, kbd_dev); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci hv_serio->dev.parent = &hv_dev->device; 3358c2ecf20Sopenharmony_ci hv_serio->id.type = SERIO_8042_XL; 3368c2ecf20Sopenharmony_ci hv_serio->port_data = kbd_dev; 3378c2ecf20Sopenharmony_ci strlcpy(hv_serio->name, dev_name(&hv_dev->device), 3388c2ecf20Sopenharmony_ci sizeof(hv_serio->name)); 3398c2ecf20Sopenharmony_ci strlcpy(hv_serio->phys, dev_name(&hv_dev->device), 3408c2ecf20Sopenharmony_ci sizeof(hv_serio->phys)); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci hv_serio->start = hv_kbd_start; 3438c2ecf20Sopenharmony_ci hv_serio->stop = hv_kbd_stop; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci error = vmbus_open(hv_dev->channel, 3468c2ecf20Sopenharmony_ci KBD_VSC_SEND_RING_BUFFER_SIZE, 3478c2ecf20Sopenharmony_ci KBD_VSC_RECV_RING_BUFFER_SIZE, 3488c2ecf20Sopenharmony_ci NULL, 0, 3498c2ecf20Sopenharmony_ci hv_kbd_on_channel_callback, 3508c2ecf20Sopenharmony_ci hv_dev); 3518c2ecf20Sopenharmony_ci if (error) 3528c2ecf20Sopenharmony_ci goto err_free_mem; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci error = hv_kbd_connect_to_vsp(hv_dev); 3558c2ecf20Sopenharmony_ci if (error) 3568c2ecf20Sopenharmony_ci goto err_close_vmbus; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci serio_register_port(kbd_dev->hv_serio); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci device_init_wakeup(&hv_dev->device, true); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cierr_close_vmbus: 3658c2ecf20Sopenharmony_ci vmbus_close(hv_dev->channel); 3668c2ecf20Sopenharmony_cierr_free_mem: 3678c2ecf20Sopenharmony_ci kfree(hv_serio); 3688c2ecf20Sopenharmony_ci kfree(kbd_dev); 3698c2ecf20Sopenharmony_ci return error; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int hv_kbd_remove(struct hv_device *hv_dev) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci serio_unregister_port(kbd_dev->hv_serio); 3778c2ecf20Sopenharmony_ci vmbus_close(hv_dev->channel); 3788c2ecf20Sopenharmony_ci kfree(kbd_dev); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci hv_set_drvdata(hv_dev, NULL); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int hv_kbd_suspend(struct hv_device *hv_dev) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci vmbus_close(hv_dev->channel); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int hv_kbd_resume(struct hv_device *hv_dev) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ret = vmbus_open(hv_dev->channel, 3978c2ecf20Sopenharmony_ci KBD_VSC_SEND_RING_BUFFER_SIZE, 3988c2ecf20Sopenharmony_ci KBD_VSC_RECV_RING_BUFFER_SIZE, 3998c2ecf20Sopenharmony_ci NULL, 0, 4008c2ecf20Sopenharmony_ci hv_kbd_on_channel_callback, 4018c2ecf20Sopenharmony_ci hv_dev); 4028c2ecf20Sopenharmony_ci if (ret == 0) 4038c2ecf20Sopenharmony_ci ret = hv_kbd_connect_to_vsp(hv_dev); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return ret; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic const struct hv_vmbus_device_id id_table[] = { 4098c2ecf20Sopenharmony_ci /* Keyboard guid */ 4108c2ecf20Sopenharmony_ci { HV_KBD_GUID, }, 4118c2ecf20Sopenharmony_ci { }, 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(vmbus, id_table); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic struct hv_driver hv_kbd_drv = { 4178c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 4188c2ecf20Sopenharmony_ci .id_table = id_table, 4198c2ecf20Sopenharmony_ci .probe = hv_kbd_probe, 4208c2ecf20Sopenharmony_ci .remove = hv_kbd_remove, 4218c2ecf20Sopenharmony_ci .suspend = hv_kbd_suspend, 4228c2ecf20Sopenharmony_ci .resume = hv_kbd_resume, 4238c2ecf20Sopenharmony_ci .driver = { 4248c2ecf20Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 4258c2ecf20Sopenharmony_ci }, 4268c2ecf20Sopenharmony_ci}; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int __init hv_kbd_init(void) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci return vmbus_driver_register(&hv_kbd_drv); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void __exit hv_kbd_exit(void) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci vmbus_driver_unregister(&hv_kbd_drv); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Keyboard Driver"); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cimodule_init(hv_kbd_init); 4428c2ecf20Sopenharmony_cimodule_exit(hv_kbd_exit); 443