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