Commit 0c3a6ede authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Staging: hv: add mouse driver

This driver adds mouse support to the hyper-v subsystem.

The code was originally written by Citrix, modified heavily by Hank at
Microsoft, and then further by me, to get it to build properly due to
all of the recent hyperv changes in the tree.

At the moment, it is marked "BROKEN" as it has not been tested well, and
it will conflict with other api changes that KY is doing, which I will
fix up later in this driver.

It still needs lots of work, and normal "cleanup".  I don't recommend
anyone running it on their machine unless they are very brave and know
how to debug kernel drivers in a hyperv system.
Signed-off-by: default avatarHank Janssen <hjanssen@microsoft.com>
Cc: K. Y. Srinivasan <kys@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 50d1ae2f
......@@ -36,4 +36,11 @@ config HYPERV_UTILS
help
Select this option to enable the Hyper-V Utilities.
config HYPERV_MOUSE
tristate "Microsoft Hyper-V mouse driver"
depends on HID && BROKEN
default HYPERV
help
Select this option to enable the Hyper-V mouse driver.
endif
......@@ -3,6 +3,7 @@ obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
obj-$(CONFIG_HYPERV_BLOCK) += hv_blkvsc.o
obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o
obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
obj-$(CONFIG_HYPERV_MOUSE) += hv_mouse.o
hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.o \
......@@ -11,3 +12,4 @@ hv_storvsc-y := storvsc_drv.o storvsc.o
hv_blkvsc-y := blkvsc_drv.o blkvsc.o
hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o
hv_utils-y := hv_util.o hv_kvp.o
hv_mouse-objs := hv_mouse_drv.o mouse_vsc.o
/*
* Copyright 2009 Citrix Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* For clarity, the licensor of this program does not intend that a
* "derivative work" include code which compiles header information from
* this program.
*
* This code has been modified from its original by
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/hiddev.h>
#include <linux/pci.h>
#include <linux/dmi.h>
//#include "osd.h"
#include "hv_api.h"
#include "logging.h"
#include "version_info.h"
#include "vmbus.h"
#include "mousevsc_api.h"
#define NBITS(x) (((x)/BITS_PER_LONG)+1)
/*
* Data types
*/
struct input_device_context {
struct vm_device *device_ctx;
struct hid_device *hid_device;
struct input_dev_info device_info;
int connected;
};
struct mousevsc_driver_context {
struct driver_context drv_ctx;
struct mousevsc_drv_obj drv_obj;
};
static struct mousevsc_driver_context g_mousevsc_drv;
void mousevsc_deviceinfo_callback(struct hv_device *dev,
struct input_dev_info *info)
{
struct vm_device *device_ctx = to_vm_device(dev);
struct input_device_context *input_device_ctx =
dev_get_drvdata(&device_ctx->device);
memcpy(&input_device_ctx->device_info, info,
sizeof(struct input_dev_info));
DPRINT_INFO(INPUTVSC_DRV, "mousevsc_deviceinfo_callback()");
}
void mousevsc_inputreport_callback(struct hv_device *dev, void *packet, u32 len)
{
int ret = 0;
struct vm_device *device_ctx = to_vm_device(dev);
struct input_device_context *input_dev_ctx =
dev_get_drvdata(&device_ctx->device);
ret = hid_input_report(input_dev_ctx->hid_device,
HID_INPUT_REPORT, packet, len, 1);
DPRINT_DBG(INPUTVSC_DRV, "hid_input_report (ret %d)", ret);
}
int mousevsc_hid_open(struct hid_device *hid)
{
return 0;
}
void mousevsc_hid_close(struct hid_device *hid)
{
}
int mousevsc_probe(struct device *device)
{
int ret = 0;
struct driver_context *driver_ctx =
driver_to_driver_context(device->driver);
struct mousevsc_driver_context *mousevsc_drv_ctx =
(struct mousevsc_driver_context *)driver_ctx;
struct mousevsc_drv_obj *mousevsc_drv_obj = &mousevsc_drv_ctx->drv_obj;
struct vm_device *device_ctx = device_to_vm_device(device);
struct hv_device *device_obj = &device_ctx->device_obj;
struct input_device_context *input_dev_ctx;
input_dev_ctx = kmalloc(sizeof(struct input_device_context),
GFP_KERNEL);
dev_set_drvdata(device, input_dev_ctx);
/* Call to the vsc driver to add the device */
ret = mousevsc_drv_obj->Base.dev_add(device_obj, NULL);
if (ret != 0) {
DPRINT_ERR(INPUTVSC_DRV, "unable to add input vsc device");
return -1;
}
return 0;
}
int mousevsc_remove(struct device *device)
{
int ret = 0;
struct driver_context *driver_ctx =
driver_to_driver_context(device->driver);
struct mousevsc_driver_context *mousevsc_drv_ctx =
(struct mousevsc_driver_context *)driver_ctx;
struct mousevsc_drv_obj *mousevsc_drv_obj = &mousevsc_drv_ctx->drv_obj;
struct vm_device *device_ctx = device_to_vm_device(device);
struct hv_device *device_obj = &device_ctx->device_obj;
struct input_device_context *input_dev_ctx;
input_dev_ctx = kmalloc(sizeof(struct input_device_context),
GFP_KERNEL);
dev_set_drvdata(device, input_dev_ctx);
if (input_dev_ctx->connected) {
hidinput_disconnect(input_dev_ctx->hid_device);
input_dev_ctx->connected = 0;
}
if (!mousevsc_drv_obj->Base.dev_rm) {
return -1;
}
/*
* Call to the vsc driver to let it know that the device
* is being removed
*/
ret = mousevsc_drv_obj->Base.dev_rm(device_obj);
if (ret != 0) {
DPRINT_ERR(INPUTVSC_DRV,
"unable to remove vsc device (ret %d)", ret);
}
kfree(input_dev_ctx);
return ret;
}
void mousevsc_reportdesc_callback(struct hv_device *dev, void *packet, u32 len)
{
struct vm_device *device_ctx = to_vm_device(dev);
struct input_device_context *input_device_ctx =
dev_get_drvdata(&device_ctx->device);
struct hid_device *hid_dev;
/* hid_debug = -1; */
hid_dev = kmalloc(sizeof(struct hid_device), GFP_KERNEL);
if (hid_parse_report(hid_dev, packet, len)) {
DPRINT_INFO(INPUTVSC_DRV, "Unable to call hd_parse_report");
return;
}
if (hid_dev) {
DPRINT_INFO(INPUTVSC_DRV, "hid_device created");
hid_dev->ll_driver->open = mousevsc_hid_open;
hid_dev->ll_driver->close = mousevsc_hid_close;
hid_dev->bus = 0x06; /* BUS_VIRTUAL */
hid_dev->vendor = input_device_ctx->device_info.VendorID;
hid_dev->product = input_device_ctx->device_info.ProductID;
hid_dev->version = input_device_ctx->device_info.VersionNumber;
hid_dev->dev = device_ctx->device;
sprintf(hid_dev->name, "%s",
input_device_ctx->device_info.Name);
/*
* HJ Do we want to call it with a 0
*/
if (!hidinput_connect(hid_dev, 0)) {
hid_dev->claimed |= HID_CLAIMED_INPUT;
input_device_ctx->connected = 1;
DPRINT_INFO(INPUTVSC_DRV,
"HID device claimed by input\n");
}
if (!hid_dev->claimed) {
DPRINT_ERR(INPUTVSC_DRV,
"HID device not claimed by "
"input or hiddev\n");
}
input_device_ctx->hid_device = hid_dev;
}
kfree(hid_dev);
}
/*
*
* Name: mousevsc_drv_init()
*
* Desc: Driver initialization.
*/
int mousevsc_drv_init(int (*pfn_drv_init)(struct hv_driver *pfn_drv_init))
{
int ret = 0;
struct mousevsc_drv_obj *input_drv_obj = &g_mousevsc_drv.drv_obj;
struct driver_context *drv_ctx = &g_mousevsc_drv.drv_ctx;
// vmbus_get_interface(&input_drv_obj->Base.VmbusChannelInterface);
input_drv_obj->OnDeviceInfo = mousevsc_deviceinfo_callback;
input_drv_obj->OnInputReport = mousevsc_inputreport_callback;
input_drv_obj->OnReportDescriptor = mousevsc_reportdesc_callback;
/* Callback to client driver to complete the initialization */
pfn_drv_init(&input_drv_obj->Base);
drv_ctx->driver.name = input_drv_obj->Base.name;
memcpy(&drv_ctx->class_id, &input_drv_obj->Base.dev_type,
sizeof(struct hv_guid));
drv_ctx->probe = mousevsc_probe;
drv_ctx->remove = mousevsc_remove;
/* The driver belongs to vmbus */
vmbus_child_driver_register(drv_ctx);
return ret;
}
int mousevsc_drv_exit_cb(struct device *dev, void *data)
{
struct device **curr = (struct device **)data;
*curr = dev;
return 1;
}
void mousevsc_drv_exit(void)
{
struct mousevsc_drv_obj *mousevsc_drv_obj = &g_mousevsc_drv.drv_obj;
struct driver_context *drv_ctx = &g_mousevsc_drv.drv_ctx;
int ret;
struct device *current_dev = NULL;
while (1) {
current_dev = NULL;
/* Get the device */
ret = driver_for_each_device(&drv_ctx->driver, NULL, (void *)&current_dev, mousevsc_drv_exit_cb);
if (ret)
printk(KERN_ERR "Can't find mouse device!\n");
if (current_dev == NULL)
break;
/* Initiate removal from the top-down */
device_unregister(current_dev);
}
if (mousevsc_drv_obj->Base.cleanup)
mousevsc_drv_obj->Base.cleanup(&mousevsc_drv_obj->Base);
vmbus_child_driver_unregister(drv_ctx);
return;
}
static int __init mousevsc_init(void)
{
int ret;
DPRINT_INFO(INPUTVSC_DRV, "Hyper-V Mouse driver initializing.");
ret = mousevsc_drv_init(mouse_vsc_initialize);
return ret;
}
static void __exit mousevsc_exit(void)
{
mousevsc_drv_exit();
}
/*
* We use a PCI table to determine if we should autoload this driver This is
* needed by distro tools to determine if the hyperv drivers should be
* installed and/or configured. We don't do anything else with the table, but
* it needs to be present.
*/
const static struct pci_device_id microsoft_hv_pci_table[] = {
{ PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */
{ 0 }
};
MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table);
MODULE_LICENSE("GPL");
MODULE_VERSION(HV_DRV_VERSION);
module_init(mousevsc_init);
module_exit(mousevsc_exit);
This diff is collapsed.
/*
* Copyright 2009 Citrix Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* For clarity, the licensor of this program does not intend that a
* "derivative work" include code which compiles header information from
* this program.
*
* This code has been modified from its original by
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#ifndef _INPUTVSC_API_H_
#define _INPUTVSC_API_H_
#include "vmbus_api.h"
/*
* Defines
*/
#define INPUTVSC_SEND_RING_BUFFER_SIZE 10*PAGE_SIZE
#define INPUTVSC_RECV_RING_BUFFER_SIZE 10*PAGE_SIZE
/*
* Data types
*/
struct input_dev_info {
unsigned short VendorID;
unsigned short ProductID;
unsigned short VersionNumber;
char Name[128];
};
/* Represents the input vsc driver */
struct mousevsc_drv_obj {
struct hv_driver Base; // Must be the first field
/*
* This is set by the caller to allow us to callback when
* we receive a packet from the "wire"
*/
void (*OnDeviceInfo)(struct hv_device *dev,
struct input_dev_info* info);
void (*OnInputReport)(struct hv_device *dev, void* packet, u32 len);
void (*OnReportDescriptor)(struct hv_device *dev,
void* packet, u32 len);
/* Specific to this driver */
int (*OnOpen)(struct hv_device *Device);
int (*OnClose)(struct hv_device *Device);
void *Context;
};
/*
* Interface
*/
int mouse_vsc_initialize(struct hv_driver *drv);
#endif // _INPUTVSC_API_H_
/*
* Copyright (c) 2009, 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.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Hank Janssen <hjanssen@microsoft.com>
*/
#ifndef _VMBUS_HID_PROTOCOL_
#define _VMBUS_HID_PROTOCOL_
/* The maximum size of a synthetic input message. */
#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16
/*
* 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_DWORD (SYNTHHID_INPUT_VERSION_MINOR | \
(SYNTHHID_INPUT_VERSION_MAJOR << 16))
#pragma pack(push,1)
/*
* Message types in the synthetic input protocol
*/
enum synthhid_msg_type
{
SynthHidProtocolRequest,
SynthHidProtocolResponse,
SynthHidInitialDeviceInfo,
SynthHidInitialDeviceInfoAck,
SynthHidInputReport,
SynthHidMax
};
/*
* Basic message structures.
*/
typedef struct
{
enum synthhid_msg_type Type; /* Type of the enclosed message */
u32 Size; /* Size of the enclosed message
* (size of the data payload)
*/
} SYNTHHID_MESSAGE_HEADER, *PSYNTHHID_MESSAGE_HEADER;
typedef struct
{
SYNTHHID_MESSAGE_HEADER Header;
char Data[1]; /* Enclosed message */
} SYNTHHID_MESSAGE, *PSYNTHHID_MESSAGE;
typedef union
{
struct {
u16 Minor;
u16 Major;
};
u32 AsDWord;
} SYNTHHID_VERSION, *PSYNTHHID_VERSION;
/*
* Protocol messages
*/
typedef struct
{
SYNTHHID_MESSAGE_HEADER Header;
SYNTHHID_VERSION VersionRequested;
} SYNTHHID_PROTOCOL_REQUEST, *PSYNTHHID_PROTOCOL_REQUEST;
typedef struct
{
SYNTHHID_MESSAGE_HEADER Header;
SYNTHHID_VERSION VersionRequested;
unsigned char Approved;
} SYNTHHID_PROTOCOL_RESPONSE, *PSYNTHHID_PROTOCOL_RESPONSE;
typedef struct
{
SYNTHHID_MESSAGE_HEADER Header;
struct input_dev_info HidDeviceAttributes;
unsigned char HidDescriptorInformation[1];
} SYNTHHID_DEVICE_INFO, *PSYNTHHID_DEVICE_INFO;
typedef struct
{
SYNTHHID_MESSAGE_HEADER Header;
unsigned char Reserved;
} SYNTHHID_DEVICE_INFO_ACK, *PSYNTHHID_DEVICE_INFO_ACK;
typedef struct
{
SYNTHHID_MESSAGE_HEADER Header;
char ReportBuffer[1];
} SYNTHHID_INPUT_REPORT, *PSYNTHHID_INPUT_REPORT;
#pragma pack(pop)
#endif /* _VMBUS_HID_PROTOCOL_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment