diff options
Diffstat (limited to '')
-rw-r--r-- | drivers/hid/hid-hyperv.c (renamed from drivers/staging/hv/hv_mouse.c) | 303 |
1 files changed, 145 insertions, 158 deletions
diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/hid/hid-hyperv.c index ccd39c70c527..0c33ae9cf0f0 100644 --- a/drivers/staging/hv/hv_mouse.c +++ b/drivers/hid/hid-hyperv.c @@ -14,11 +14,8 @@ */ #include <linux/init.h> #include <linux/module.h> -#include <linux/delay.h> #include <linux/device.h> -#include <linux/workqueue.h> -#include <linux/sched.h> -#include <linux/wait.h> +#include <linux/completion.h> #include <linux/input.h> #include <linux/hid.h> #include <linux/hiddev.h> @@ -118,7 +115,6 @@ struct synthhid_input_report { #define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE) #define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE) -#define NBITS(x) (((x)/BITS_PER_LONG)+1) enum pipe_prot_msg_type { PIPE_MESSAGE_INVALID, @@ -148,7 +144,8 @@ struct mousevsc_prt_msg { */ struct mousevsc_dev { struct hv_device *device; - unsigned char init_complete; + bool init_complete; + bool connected; struct mousevsc_prt_msg protocol_req; struct mousevsc_prt_msg protocol_resp; /* Synchronize the request/response if needed */ @@ -159,12 +156,11 @@ struct mousevsc_dev { unsigned char *report_desc; u32 report_desc_size; struct hv_input_dev_info hid_dev_info; - int connected; struct hid_device *hid_device; }; -static struct mousevsc_dev *alloc_input_device(struct hv_device *device) +static struct mousevsc_dev *mousevsc_alloc_device(struct hv_device *device) { struct mousevsc_dev *input_dev; @@ -176,11 +172,12 @@ static struct mousevsc_dev *alloc_input_device(struct hv_device *device) input_dev->device = device; hv_set_drvdata(device, input_dev); init_completion(&input_dev->wait_event); + input_dev->init_complete = false; return input_dev; } -static void free_input_device(struct mousevsc_dev *device) +static void mousevsc_free_device(struct mousevsc_dev *device) { kfree(device->hid_desc); kfree(device->report_desc); @@ -188,7 +185,6 @@ static void free_input_device(struct mousevsc_dev *device) kfree(device); } - static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, struct synthhid_device_info *device_info) { @@ -196,14 +192,12 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, struct hid_descriptor *desc; struct mousevsc_prt_msg ack; - /* Assume success for now */ - input_device->dev_info_status = 0; - - memcpy(&input_device->hid_dev_info, &device_info->hid_dev_info, - sizeof(struct hv_input_dev_info)); + input_device->dev_info_status = -ENOMEM; + input_device->hid_dev_info = device_info->hid_dev_info; desc = &device_info->hid_descriptor; - WARN_ON(desc->bLength == 0); + if (desc->bLength == 0) + goto cleanup; input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); @@ -213,13 +207,18 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, memcpy(input_device->hid_desc, desc, desc->bLength); input_device->report_desc_size = desc->desc[0].wDescriptorLength; - if (input_device->report_desc_size == 0) + if (input_device->report_desc_size == 0) { + input_device->dev_info_status = -EINVAL; goto cleanup; + } + input_device->report_desc = kzalloc(input_device->report_desc_size, GFP_ATOMIC); - if (!input_device->report_desc) + if (!input_device->report_desc) { + input_device->dev_info_status = -ENOMEM; goto cleanup; + } memcpy(input_device->report_desc, ((unsigned char *)desc) + desc->bLength, @@ -242,22 +241,14 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, (unsigned long)&ack, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) - goto cleanup; - - complete(&input_device->wait_event); - return; + if (!ret) + input_device->dev_info_status = 0; cleanup: - kfree(input_device->hid_desc); - input_device->hid_desc = NULL; - - kfree(input_device->report_desc); - input_device->report_desc = NULL; - - input_device->dev_info_status = -1; complete(&input_device->wait_event); + + return; } static void mousevsc_on_receive(struct hv_device *device, @@ -274,10 +265,22 @@ static void mousevsc_on_receive(struct hv_device *device, if (pipe_msg->type != PIPE_MESSAGE_DATA) return; - hid_msg = (struct synthhid_msg *)&pipe_msg->data[0]; + hid_msg = (struct synthhid_msg *)pipe_msg->data; switch (hid_msg->header.type) { case SYNTH_HID_PROTOCOL_RESPONSE: + /* + * While it will be impossible for us to protect against + * malicious/buggy hypervisor/host, add a check here to + * ensure we don't corrupt memory. + */ + if ((pipe_msg->size + sizeof(struct pipe_prt_msg) + - sizeof(unsigned char)) + > sizeof(struct mousevsc_prt_msg)) { + WARN_ON(1); + break; + } + memcpy(&input_dev->protocol_resp, pipe_msg, pipe_msg->size + sizeof(struct pipe_prt_msg) - sizeof(unsigned char)); @@ -292,11 +295,11 @@ static void mousevsc_on_receive(struct hv_device *device, * hid desc and report desc */ mousevsc_on_receive_device_info(input_dev, - (struct synthhid_device_info *)&pipe_msg->data[0]); + (struct synthhid_device_info *)pipe_msg->data); break; case SYNTH_HID_INPUT_REPORT: input_report = - (struct synthhid_input_report *)&pipe_msg->data[0]; + (struct synthhid_input_report *)pipe_msg->data; if (!input_dev->init_complete) break; hid_input_report(input_dev->hid_device, @@ -313,73 +316,60 @@ static void mousevsc_on_receive(struct hv_device *device, static void mousevsc_on_channel_callback(void *context) { - const int packetSize = 0x100; - int ret = 0; - struct hv_device *device = (struct hv_device *)context; - + const int packet_size = 0x100; + int ret; + struct hv_device *device = context; u32 bytes_recvd; u64 req_id; - unsigned char packet[0x100]; struct vmpacket_descriptor *desc; - unsigned char *buffer = packet; - int bufferlen = packetSize; + unsigned char *buffer; + int bufferlen = packet_size; + buffer = kmalloc(bufferlen, GFP_ATOMIC); + if (!buffer) + return; do { ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, &bytes_recvd, &req_id); - if (ret == 0) { - if (bytes_recvd > 0) { - desc = (struct vmpacket_descriptor *)buffer; - - switch (desc->type) { - case VM_PKT_COMP: - break; - - case VM_PKT_DATA_INBAND: - mousevsc_on_receive( - device, desc); - break; - - default: - pr_err("unhandled packet type %d, tid %llx len %d\n", - desc->type, - req_id, - bytes_recvd); - break; - } - - /* reset */ - if (bufferlen > packetSize) { - kfree(buffer); - - buffer = packet; - bufferlen = packetSize; - } - } else { - if (bufferlen > packetSize) { - kfree(buffer); - - buffer = packet; - bufferlen = packetSize; - } + switch (ret) { + case 0: + if (bytes_recvd <= 0) { + kfree(buffer); + return; + } + desc = (struct vmpacket_descriptor *)buffer; + + switch (desc->type) { + case VM_PKT_COMP: + break; + + case VM_PKT_DATA_INBAND: + mousevsc_on_receive(device, desc); + break; + + default: + pr_err("unhandled packet type %d, tid %llx len %d\n", + desc->type, req_id, bytes_recvd); break; } - } else if (ret == -ENOBUFS) { + + break; + + case -ENOBUFS: + kfree(buffer); /* Handle large packet */ bufferlen = bytes_recvd; - buffer = kzalloc(bytes_recvd, GFP_ATOMIC); + buffer = kmalloc(bytes_recvd, GFP_ATOMIC); - if (buffer == NULL) { - buffer = packet; - bufferlen = packetSize; - break; - } + if (!buffer) + return; + + break; } } while (1); - return; } static int mousevsc_connect_to_vsp(struct hv_device *device) @@ -390,19 +380,15 @@ static int mousevsc_connect_to_vsp(struct hv_device *device) struct mousevsc_prt_msg *request; struct mousevsc_prt_msg *response; - request = &input_dev->protocol_req; - memset(request, 0, sizeof(struct mousevsc_prt_msg)); request->type = PIPE_MESSAGE_DATA; request->size = sizeof(struct synthhid_protocol_request); - request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST; request->request.header.size = sizeof(unsigned int); request->request.version_requested.version = SYNTHHID_INPUT_VERSION; - ret = vmbus_sendpacket(device->channel, request, sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + @@ -410,11 +396,11 @@ static int mousevsc_connect_to_vsp(struct hv_device *device) (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) + if (ret) goto cleanup; t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); - if (t == 0) { + if (!t) { ret = -ETIMEDOUT; goto cleanup; } @@ -422,14 +408,14 @@ static int mousevsc_connect_to_vsp(struct hv_device *device) response = &input_dev->protocol_resp; if (!response->response.approved) { - pr_err("synthhid protocol request failed (version %d)", + pr_err("synthhid protocol request failed (version %d)\n", SYNTHHID_INPUT_VERSION); ret = -ENODEV; goto cleanup; } t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); - if (t == 0) { + if (!t) { ret = -ETIMEDOUT; goto cleanup; } @@ -438,11 +424,9 @@ static int mousevsc_connect_to_vsp(struct hv_device *device) * We should have gotten the device attr, hid desc and report * desc at this point */ - if (input_dev->dev_info_status) - ret = -ENOMEM; + ret = input_dev->dev_info_status; cleanup: - return ret; } @@ -451,61 +435,40 @@ static int mousevsc_hid_open(struct hid_device *hid) return 0; } +static int mousevsc_hid_start(struct hid_device *hid) +{ + return 0; +} + static void mousevsc_hid_close(struct hid_device *hid) { } +static void mousevsc_hid_stop(struct hid_device *hid) +{ +} + static struct hid_ll_driver mousevsc_ll_driver = { .open = mousevsc_hid_open, .close = mousevsc_hid_close, + .start = mousevsc_hid_start, + .stop = mousevsc_hid_stop, }; static struct hid_driver mousevsc_hid_driver; -static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len) -{ - struct hid_device *hid_dev; - struct mousevsc_dev *input_device = hv_get_drvdata(dev); - - hid_dev = hid_allocate_device(); - if (IS_ERR(hid_dev)) - return; - - hid_dev->ll_driver = &mousevsc_ll_driver; - hid_dev->driver = &mousevsc_hid_driver; - - if (hid_parse_report(hid_dev, packet, len)) - return; - - hid_dev->bus = BUS_VIRTUAL; - hid_dev->vendor = input_device->hid_dev_info.vendor; - hid_dev->product = input_device->hid_dev_info.product; - hid_dev->version = input_device->hid_dev_info.version; - - sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); - - if (!hidinput_connect(hid_dev, 0)) { - hid_dev->claimed |= HID_CLAIMED_INPUT; - - input_device->connected = 1; - - } - - input_device->hid_device = hid_dev; -} - -static int mousevsc_on_device_add(struct hv_device *device) +static int mousevsc_probe(struct hv_device *device, + const struct hv_vmbus_device_id *dev_id) { - int ret = 0; + int ret; struct mousevsc_dev *input_dev; + struct hid_device *hid_dev; - input_dev = alloc_input_device(device); + input_dev = mousevsc_alloc_device(device); if (!input_dev) return -ENOMEM; - input_dev->init_complete = false; - ret = vmbus_open(device->channel, INPUTVSC_SEND_RING_BUFFER_SIZE, INPUTVSC_RECV_RING_BUFFER_SIZE, @@ -515,54 +478,78 @@ static int mousevsc_on_device_add(struct hv_device *device) device ); - if (ret != 0) { - free_input_device(input_dev); - return ret; - } - + if (ret) + goto probe_err0; ret = mousevsc_connect_to_vsp(device); - if (ret != 0) { - vmbus_close(device->channel); - free_input_device(input_dev); - return ret; - } - + if (ret) + goto probe_err1; /* workaround SA-167 */ if (input_dev->report_desc[14] == 0x25) input_dev->report_desc[14] = 0x29; - reportdesc_callback(device, input_dev->report_desc, - input_dev->report_desc_size); + hid_dev = hid_allocate_device(); + if (IS_ERR(hid_dev)) { + ret = PTR_ERR(hid_dev); + goto probe_err1; + } + + hid_dev->ll_driver = &mousevsc_ll_driver; + hid_dev->driver = &mousevsc_hid_driver; + hid_dev->bus = BUS_VIRTUAL; + hid_dev->vendor = input_dev->hid_dev_info.vendor; + hid_dev->product = input_dev->hid_dev_info.product; + hid_dev->version = input_dev->hid_dev_info.version; + input_dev->hid_device = hid_dev; + + sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); + + ret = hid_add_device(hid_dev); + if (ret) + goto probe_err1; + + ret = hid_parse_report(hid_dev, input_dev->report_desc, + input_dev->report_desc_size); + + if (ret) { + hid_err(hid_dev, "parse failed\n"); + goto probe_err2; + } + ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV); + + if (ret) { + hid_err(hid_dev, "hw start failed\n"); + goto probe_err2; + } + + input_dev->connected = true; input_dev->init_complete = true; return ret; -} -static int mousevsc_probe(struct hv_device *dev, - const struct hv_vmbus_device_id *dev_id) -{ +probe_err2: + hid_destroy_device(hid_dev); - return mousevsc_on_device_add(dev); +probe_err1: + vmbus_close(device->channel); +probe_err0: + mousevsc_free_device(input_dev); + + return ret; } + static int mousevsc_remove(struct hv_device *dev) { struct mousevsc_dev *input_dev = hv_get_drvdata(dev); vmbus_close(dev->channel); - - if (input_dev->connected) { - hidinput_disconnect(input_dev->hid_device); - input_dev->connected = 0; - hid_destroy_device(input_dev->hid_device); - } - - free_input_device(input_dev); + hid_destroy_device(input_dev->hid_device); + mousevsc_free_device(input_dev); return 0; } @@ -577,7 +564,7 @@ static const struct hv_vmbus_device_id id_table[] = { MODULE_DEVICE_TABLE(vmbus, id_table); static struct hv_driver mousevsc_drv = { - .name = "mousevsc", + .name = KBUILD_MODNAME, .id_table = id_table, .probe = mousevsc_probe, .remove = mousevsc_remove, |