742 lines · 16.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  Copyright (c) 2009, Citrix Systems, Inc. *  Copyright (c) 2010, Microsoft Corporation. *  Copyright (c) 2011, Novell Inc. */#include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/completion.h>#include <linux/input.h>#include <linux/hid.h>#include <linux/hiddev.h>#include <linux/hyperv.h> #if IS_ENABLED(CONFIG_HID_HYPERV_MOUSE_KUNIT_TEST)#include <kunit/test.h>#endif struct hv_input_dev_info {	unsigned int size;	unsigned short vendor;	unsigned short product;	unsigned short version;	unsigned short reserved[11];}; /* * Current version * * History: * Beta, RC < 2008/1/22        1,0 * RC > 2008/1/22              2,0 */#define SYNTHHID_INPUT_VERSION_MAJOR	2#define SYNTHHID_INPUT_VERSION_MINOR	0#define SYNTHHID_INPUT_VERSION		(SYNTHHID_INPUT_VERSION_MINOR | \					 (SYNTHHID_INPUT_VERSION_MAJOR << 16))  #pragma pack(push, 1)/* * Message types in the synthetic input protocol */enum synthhid_msg_type {	SYNTH_HID_PROTOCOL_REQUEST,	SYNTH_HID_PROTOCOL_RESPONSE,	SYNTH_HID_INITIAL_DEVICE_INFO,	SYNTH_HID_INITIAL_DEVICE_INFO_ACK,	SYNTH_HID_INPUT_REPORT,	SYNTH_HID_MAX}; /* * Basic message structures. */struct synthhid_msg_hdr {	enum synthhid_msg_type type;	u32 size;}; union synthhid_version {	struct {		u16 minor_version;		u16 major_version;	};	u32 version;}; /* * Protocol messages */struct synthhid_protocol_request {	struct synthhid_msg_hdr header;	union synthhid_version version_requested;}; struct synthhid_protocol_response {	struct synthhid_msg_hdr header;	union synthhid_version version_requested;	unsigned char approved;}; struct synthhid_device_info {	struct synthhid_msg_hdr header;	struct hv_input_dev_info hid_dev_info;	struct hid_descriptor hid_descriptor;}; struct synthhid_device_info_ack {	struct synthhid_msg_hdr header;	unsigned char reserved;}; struct synthhid_input_report {	struct synthhid_msg_hdr header;	char buffer[];}; #pragma pack(pop) #define INPUTVSC_SEND_RING_BUFFER_SIZE	VMBUS_RING_SIZE(36 * 1024)#define INPUTVSC_RECV_RING_BUFFER_SIZE	VMBUS_RING_SIZE(36 * 1024)  enum pipe_prot_msg_type {	PIPE_MESSAGE_INVALID,	PIPE_MESSAGE_DATA,	PIPE_MESSAGE_MAXIMUM};  struct pipe_prt_msg {	enum pipe_prot_msg_type type;	u32 size;	char data[];}; struct  mousevsc_prt_msg {	enum pipe_prot_msg_type type;	u32 size;	union {		struct synthhid_protocol_request request;		struct synthhid_protocol_response response;		struct synthhid_device_info_ack ack;	};}; /* * Represents an mousevsc device */struct mousevsc_dev {	struct hv_device	*device;	bool			init_complete;	bool			connected;	struct mousevsc_prt_msg	protocol_req;	struct mousevsc_prt_msg	protocol_resp;	/* Synchronize the request/response if needed */	struct completion	wait_event;	int			dev_info_status; 	struct hid_descriptor	*hid_desc;	unsigned char		*report_desc;	u32			report_desc_size;	struct hv_input_dev_info hid_dev_info;	struct hid_device       *hid_device;	u8			input_buf[HID_MAX_BUFFER_SIZE];};  static struct mousevsc_dev *mousevsc_alloc_device(struct hv_device *device){	struct mousevsc_dev *input_dev; 	input_dev = kzalloc_obj(struct mousevsc_dev); 	if (!input_dev)		return NULL; 	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 mousevsc_free_device(struct mousevsc_dev *device){	kfree(device->hid_desc);	kfree(device->report_desc);	hv_set_drvdata(device->device, NULL);	kfree(device);} static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,					    struct synthhid_device_info *device_info,					    u32 device_info_size){	int ret = 0;	struct hid_descriptor *desc;	struct mousevsc_prt_msg ack;	size_t desc_offset;	size_t desc_size; 	input_device->dev_info_status = -ENOMEM; 	if (device_info_size < sizeof(*device_info)) {		input_device->dev_info_status = -EINVAL;		goto cleanup;	} 	input_device->hid_dev_info = device_info->hid_dev_info;	desc = &device_info->hid_descriptor;	desc_offset = offsetof(struct synthhid_device_info, hid_descriptor);	desc_size = device_info_size - desc_offset;	if (desc->bLength == 0)		goto cleanup;	if (desc->bLength < sizeof(*desc) || desc->bLength > desc_size) {		input_device->dev_info_status = -EINVAL;		goto cleanup;	} 	/* The pointer is not NULL when we resume from hibernation */	kfree(input_device->hid_desc);	input_device->hid_desc = kmemdup(desc, desc->bLength, GFP_ATOMIC); 	if (!input_device->hid_desc)		goto cleanup; 	input_device->report_desc_size = le16_to_cpu(					desc->rpt_desc.wDescriptorLength);	if (input_device->report_desc_size == 0) {		input_device->dev_info_status = -EINVAL;		goto cleanup;	}	if (input_device->report_desc_size > desc_size - desc->bLength) {		input_device->dev_info_status = -EINVAL;		goto cleanup;	} 	/* The pointer is not NULL when we resume from hibernation */	kfree(input_device->report_desc);	input_device->report_desc = kzalloc(input_device->report_desc_size,					  GFP_ATOMIC); 	if (!input_device->report_desc) {		input_device->dev_info_status = -ENOMEM;		goto cleanup;	} 	memcpy(input_device->report_desc,	       ((unsigned char *)desc) + desc->bLength,	       le16_to_cpu(desc->rpt_desc.wDescriptorLength)); 	/* Send the ack */	memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); 	ack.type = PIPE_MESSAGE_DATA;	ack.size = sizeof(struct synthhid_device_info_ack); 	ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK;	ack.ack.header.size = 1;	ack.ack.reserved = 0; 	if (IS_ENABLED(CONFIG_HID_HYPERV_MOUSE_KUNIT_TEST) &&	    !input_device->device) {		ret = 0;	} else {		ret = vmbus_sendpacket(input_device->device->channel,				       &ack,				       sizeof(struct pipe_prt_msg) +				       sizeof(struct synthhid_device_info_ack),				       (unsigned long)&ack,				       VM_PKT_DATA_INBAND,				       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);	} 	if (!ret)		input_device->dev_info_status = 0; cleanup:	complete(&input_device->wait_event); 	return;} static void mousevsc_on_receive(struct hv_device *device,				struct vmpacket_descriptor *packet){	struct pipe_prt_msg *pipe_msg;	struct synthhid_msg_hdr *hid_msg_hdr;	struct mousevsc_dev *input_dev = hv_get_drvdata(device);	struct synthhid_input_report *input_report;	size_t len; 	pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet +						(packet->offset8 << 3)); 	if (pipe_msg->type != PIPE_MESSAGE_DATA)		return; 	hid_msg_hdr = (struct synthhid_msg_hdr *)pipe_msg->data; 	switch (hid_msg_hdr->type) {	case SYNTH_HID_PROTOCOL_RESPONSE:		len = struct_size(pipe_msg, data, pipe_msg->size); 		/*		 * 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 (WARN_ON(len > sizeof(struct mousevsc_prt_msg)))			break; 		memcpy(&input_dev->protocol_resp, pipe_msg, len);		complete(&input_dev->wait_event);		break; 	case SYNTH_HID_INITIAL_DEVICE_INFO:		if (WARN_ON_ONCE(pipe_msg->size <				 sizeof(struct synthhid_device_info)))			break; 		/*		 * Parse out the device info into device attr,		 * hid desc and report desc		 */		mousevsc_on_receive_device_info(input_dev,						(struct synthhid_device_info *)pipe_msg->data,						pipe_msg->size);		break;	case SYNTH_HID_INPUT_REPORT:		input_report =			(struct synthhid_input_report *)pipe_msg->data;		if (!input_dev->init_complete)			break; 		len = min(input_report->header.size,			  (u32)sizeof(input_dev->input_buf));		memcpy(input_dev->input_buf, input_report->buffer, len);		hid_input_report(input_dev->hid_device, HID_INPUT_REPORT,				 input_dev->input_buf, len, 1); 		pm_wakeup_hard_event(&input_dev->device->device); 		break;	default:		pr_err("unsupported hid msg type - type %d len %d\n",		       hid_msg_hdr->type, hid_msg_hdr->size);		break;	} } static void mousevsc_on_channel_callback(void *context){	struct hv_device *device = context;	struct vmpacket_descriptor *desc; 	foreach_vmbus_pkt(desc, device->channel) {		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, desc->trans_id, desc->len8 * 8);			break;		}	}} static int mousevsc_connect_to_vsp(struct hv_device *device){	int ret = 0;	unsigned long t;	struct mousevsc_dev *input_dev = hv_get_drvdata(device);	struct mousevsc_prt_msg *request;	struct mousevsc_prt_msg *response; 	reinit_completion(&input_dev->wait_event); 	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(struct synthhid_protocol_request),				(unsigned long)request,				VM_PKT_DATA_INBAND,				VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);	if (ret)		goto cleanup; 	t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ);	if (!t) {		ret = -ETIMEDOUT;		goto cleanup;	} 	response = &input_dev->protocol_resp; 	if (!response->response.approved) {		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) {		ret = -ETIMEDOUT;		goto cleanup;	} 	/*	 * We should have gotten the device attr, hid desc and report	 * desc at this point	 */	ret = input_dev->dev_info_status; cleanup:	return ret;} static int mousevsc_hid_parse(struct hid_device *hid){	struct hv_device *dev = hid_get_drvdata(hid);	struct mousevsc_dev *input_dev = hv_get_drvdata(dev); 	return hid_parse_report(hid, input_dev->report_desc,				input_dev->report_desc_size);} 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 int mousevsc_hid_raw_request(struct hid_device *hid,				    unsigned char report_num,				    __u8 *buf, size_t len,				    unsigned char rtype,				    int reqtype){	return 0;} static int mousevsc_hid_probe(struct hid_device *hid_dev, const struct hid_device_id *id){	int ret; 	ret = hid_parse(hid_dev);	if (ret) {		hid_err(hid_dev, "parse failed\n");		return ret;	} 	ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV);	if (ret) {		hid_err(hid_dev, "hw start failed\n");		return ret;	} 	return 0;} static const struct hid_ll_driver mousevsc_ll_driver = {	.parse = mousevsc_hid_parse,	.open = mousevsc_hid_open,	.close = mousevsc_hid_close,	.start = mousevsc_hid_start,	.stop = mousevsc_hid_stop,	.raw_request = mousevsc_hid_raw_request,}; static const struct hid_device_id mousevsc_devices[] = {	{ HID_DEVICE(BUS_VIRTUAL, HID_GROUP_ANY, 0x045E, 0x0621) },	{ }}; static struct hid_driver mousevsc_hid_driver = {	.name = "hid-hyperv",	.id_table = mousevsc_devices,	.probe = mousevsc_hid_probe,}; static int mousevsc_probe(struct hv_device *device,			const struct hv_vmbus_device_id *dev_id){	int ret;	struct mousevsc_dev *input_dev;	struct hid_device *hid_dev; 	input_dev = mousevsc_alloc_device(device);

Lines 1–500 of 742.