123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- /*
- * 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>
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/completion.h>
- #include <linux/hyperv.h>
- #include <linux/serio.h>
- #include <linux/slab.h>
- /*
- * Current version 1.0
- *
- */
- #define SYNTH_KBD_VERSION_MAJOR 1
- #define SYNTH_KBD_VERSION_MINOR 0
- #define SYNTH_KBD_VERSION (SYNTH_KBD_VERSION_MINOR | \
- (SYNTH_KBD_VERSION_MAJOR << 16))
- /*
- * Message types in the synthetic input protocol
- */
- enum synth_kbd_msg_type {
- SYNTH_KBD_PROTOCOL_REQUEST = 1,
- SYNTH_KBD_PROTOCOL_RESPONSE = 2,
- SYNTH_KBD_EVENT = 3,
- SYNTH_KBD_LED_INDICATORS = 4,
- };
- /*
- * Basic message structures.
- */
- struct synth_kbd_msg_hdr {
- __le32 type;
- };
- struct synth_kbd_msg {
- struct synth_kbd_msg_hdr header;
- char data[]; /* Enclosed message */
- };
- union synth_kbd_version {
- __le32 version;
- };
- /*
- * Protocol messages
- */
- struct synth_kbd_protocol_request {
- struct synth_kbd_msg_hdr header;
- union synth_kbd_version version_requested;
- };
- #define PROTOCOL_ACCEPTED BIT(0)
- struct synth_kbd_protocol_response {
- struct synth_kbd_msg_hdr header;
- __le32 proto_status;
- };
- #define IS_UNICODE BIT(0)
- #define IS_BREAK BIT(1)
- #define IS_E0 BIT(2)
- #define IS_E1 BIT(3)
- struct synth_kbd_keystroke {
- struct synth_kbd_msg_hdr header;
- __le16 make_code;
- __le16 reserved0;
- __le32 info; /* Additional information */
- };
- #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 XTKBD_EMUL0 0xe0
- #define XTKBD_EMUL1 0xe1
- #define XTKBD_RELEASE 0x80
- /*
- * Represents a keyboard device
- */
- struct hv_kbd_dev {
- struct hv_device *hv_dev;
- struct serio *hv_serio;
- struct synth_kbd_protocol_request protocol_req;
- struct synth_kbd_protocol_response protocol_resp;
- /* Synchronize the request/response if needed */
- struct completion wait_event;
- spinlock_t lock; /* protects 'started' field */
- bool started;
- };
- static void hv_kbd_on_receive(struct hv_device *hv_dev,
- struct synth_kbd_msg *msg, u32 msg_length)
- {
- 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;
- switch (msg_type) {
- case SYNTH_KBD_PROTOCOL_RESPONSE:
- /*
- * Validate the information provided by the host.
- * If the host is giving us a bogus packet,
- * drop the packet (hoping the problem
- * goes away).
- */
- if (msg_length < sizeof(struct synth_kbd_protocol_response)) {
- dev_err(&hv_dev->device,
- "Illegal protocol response packet (len: %d)\n",
- msg_length);
- break;
- }
- memcpy(&kbd_dev->protocol_resp, msg,
- sizeof(struct synth_kbd_protocol_response));
- complete(&kbd_dev->wait_event);
- break;
- case SYNTH_KBD_EVENT:
- /*
- * Validate the information provided by the host.
- * If the host is giving us a bogus packet,
- * drop the packet (hoping the problem
- * goes away).
- */
- if (msg_length < sizeof(struct synth_kbd_keystroke)) {
- dev_err(&hv_dev->device,
- "Illegal keyboard event packet (len: %d)\n",
- msg_length);
- break;
- }
- ks_msg = (struct synth_kbd_keystroke *)msg;
- info = __le32_to_cpu(ks_msg->info);
- /*
- * 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;
- serio_interrupt(kbd_dev->hv_serio, scan_code, 0);
- }
- spin_unlock_irqrestore(&kbd_dev->lock, flags);
- /*
- * Only trigger a wakeup on key down, otherwise
- * "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))
- pm_wakeup_event(&hv_dev->device, 0);
- break;
- default:
- dev_err(&hv_dev->device,
- "unhandled message type %d\n", msg_type);
- }
- }
- static void hv_kbd_handle_received_packet(struct hv_device *hv_dev,
- struct vmpacket_descriptor *desc,
- u32 bytes_recvd,
- u64 req_id)
- {
- struct synth_kbd_msg *msg;
- u32 msg_sz;
- switch (desc->type) {
- case VM_PKT_COMP:
- break;
- case VM_PKT_DATA_INBAND:
- /*
- * We have a packet that has "inband" data. The API used
- * for retrieving the packet guarantees that the complete
- * packet is read. So, minimally, we should be able to
- * parse the payload header safely (assuming that the host
- * can be trusted. Trusting the host seems to be a
- * reasonable assumption because in a virtualized
- * environment there is not whole lot you can do if you
- * don't trust the host.
- *
- * Nonetheless, let us validate if the host can be trusted
- * (in a trivial way). The interesting aspect of this
- * validation is how do you recover if we discover that the
- * host is not to be trusted? Simply dropping the packet, I
- * don't think is an appropriate recovery. In the interest
- * of failing fast, it may be better to crash the guest.
- * For now, I will just drop the packet!
- */
- msg_sz = bytes_recvd - (desc->offset8 << 3);
- if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) {
- /*
- * Drop the packet and hope
- * the problem magically goes away.
- */
- dev_err(&hv_dev->device,
- "Illegal packet (type: %d, tid: %llx, size: %d)\n",
- desc->type, req_id, msg_sz);
- break;
- }
- msg = (void *)desc + (desc->offset8 << 3);
- hv_kbd_on_receive(hv_dev, msg, msg_sz);
- break;
- default:
- dev_err(&hv_dev->device,
- "unhandled packet type %d, tid %llx len %d\n",
- desc->type, req_id, bytes_recvd);
- break;
- }
- }
- static void hv_kbd_on_channel_callback(void *context)
- {
- 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;
- case -ENOBUFS:
- kfree(buffer);
- /* Handle large packet */
- bufferlen = bytes_recvd;
- buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
- if (!buffer)
- return;
- break;
- }
- }
- }
- static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev)
- {
- struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
- struct synth_kbd_protocol_request *request;
- struct synth_kbd_protocol_response *response;
- u32 proto_status;
- int error;
- request = &kbd_dev->protocol_req;
- memset(request, 0, sizeof(struct synth_kbd_protocol_request));
- request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST);
- request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION);
- error = vmbus_sendpacket(hv_dev->channel, request,
- sizeof(struct synth_kbd_protocol_request),
- (unsigned long)request,
- VM_PKT_DATA_INBAND,
- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
- if (error)
- return error;
- if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ))
- return -ETIMEDOUT;
- response = &kbd_dev->protocol_resp;
- proto_status = __le32_to_cpu(response->proto_status);
- if (!(proto_status & PROTOCOL_ACCEPTED)) {
- dev_err(&hv_dev->device,
- "synth_kbd protocol request failed (version %d)\n",
- SYNTH_KBD_VERSION);
- return -ENODEV;
- }
- return 0;
- }
- 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);
- kbd_dev->started = true;
- spin_unlock_irqrestore(&kbd_dev->lock, flags);
- return 0;
- }
- 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);
- kbd_dev->started = false;
- spin_unlock_irqrestore(&kbd_dev->lock, flags);
- }
- static int hv_kbd_probe(struct hv_device *hv_dev,
- const struct hv_vmbus_device_id *dev_id)
- {
- struct hv_kbd_dev *kbd_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);
- if (!kbd_dev || !hv_serio) {
- error = -ENOMEM;
- goto err_free_mem;
- }
- kbd_dev->hv_dev = hv_dev;
- kbd_dev->hv_serio = hv_serio;
- spin_lock_init(&kbd_dev->lock);
- init_completion(&kbd_dev->wait_event);
- hv_set_drvdata(hv_dev, kbd_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),
- sizeof(hv_serio->name));
- strlcpy(hv_serio->phys, dev_name(&hv_dev->device),
- sizeof(hv_serio->phys));
- hv_serio->start = hv_kbd_start;
- hv_serio->stop = hv_kbd_stop;
- error = 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 (error)
- goto err_free_mem;
- error = hv_kbd_connect_to_vsp(hv_dev);
- if (error)
- goto err_close_vmbus;
- serio_register_port(kbd_dev->hv_serio);
- device_init_wakeup(&hv_dev->device, true);
- return 0;
- err_close_vmbus:
- vmbus_close(hv_dev->channel);
- err_free_mem:
- kfree(hv_serio);
- kfree(kbd_dev);
- return error;
- }
- static int hv_kbd_remove(struct hv_device *hv_dev)
- {
- struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
- device_init_wakeup(&hv_dev->device, false);
- serio_unregister_port(kbd_dev->hv_serio);
- vmbus_close(hv_dev->channel);
- kfree(kbd_dev);
- hv_set_drvdata(hv_dev, NULL);
- return 0;
- }
- /*
- * Keyboard GUID
- * {f912ad6d-2b17-48ea-bd65-f927a61c7684}
- */
- #define HV_KBD_GUID \
- .guid = { \
- 0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, \
- 0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 \
- }
- static const struct hv_vmbus_device_id id_table[] = {
- /* Keyboard guid */
- { HV_KBD_GUID, },
- { },
- };
- MODULE_DEVICE_TABLE(vmbus, id_table);
- static struct hv_driver hv_kbd_drv = {
- .name = KBUILD_MODNAME,
- .id_table = id_table,
- .probe = hv_kbd_probe,
- .remove = hv_kbd_remove,
- };
- static int __init hv_kbd_init(void)
- {
- return vmbus_driver_register(&hv_kbd_drv);
- }
- static void __exit hv_kbd_exit(void)
- {
- vmbus_driver_unregister(&hv_kbd_drv);
- }
- MODULE_LICENSE("GPL");
- module_init(hv_kbd_init);
- module_exit(hv_kbd_exit);
|