10ba002bcSHans de Goede /* SPDX-License-Identifier: GPL-2.0 */
20ba002bcSHans de Goede /*
30ba002bcSHans de Goede  * vboxguest linux pci driver, char-dev and input-device code,
40ba002bcSHans de Goede  *
50ba002bcSHans de Goede  * Copyright (C) 2006-2016 Oracle Corporation
60ba002bcSHans de Goede  */
70ba002bcSHans de Goede 
80ba002bcSHans de Goede #include <linux/input.h>
90ba002bcSHans de Goede #include <linux/kernel.h>
100ba002bcSHans de Goede #include <linux/miscdevice.h>
110ba002bcSHans de Goede #include <linux/module.h>
120ba002bcSHans de Goede #include <linux/pci.h>
130ba002bcSHans de Goede #include <linux/poll.h>
140ba002bcSHans de Goede #include <linux/vbox_utils.h>
150ba002bcSHans de Goede #include "vboxguest_core.h"
160ba002bcSHans de Goede 
170ba002bcSHans de Goede /** The device name. */
180ba002bcSHans de Goede #define DEVICE_NAME		"vboxguest"
190ba002bcSHans de Goede /** The device name for the device node open to everyone. */
200ba002bcSHans de Goede #define DEVICE_NAME_USER	"vboxuser"
210ba002bcSHans de Goede /** VirtualBox PCI vendor ID. */
220ba002bcSHans de Goede #define VBOX_VENDORID		0x80ee
230ba002bcSHans de Goede /** VMMDev PCI card product ID. */
240ba002bcSHans de Goede #define VMMDEV_DEVICEID		0xcafe
250ba002bcSHans de Goede 
260ba002bcSHans de Goede /** Mutex protecting the global vbg_gdev pointer used by vbg_get/put_gdev. */
270ba002bcSHans de Goede static DEFINE_MUTEX(vbg_gdev_mutex);
280ba002bcSHans de Goede /** Global vbg_gdev pointer used by vbg_get/put_gdev. */
290ba002bcSHans de Goede static struct vbg_dev *vbg_gdev;
300ba002bcSHans de Goede 
310ba002bcSHans de Goede static int vbg_misc_device_open(struct inode *inode, struct file *filp)
320ba002bcSHans de Goede {
330ba002bcSHans de Goede 	struct vbg_session *session;
340ba002bcSHans de Goede 	struct vbg_dev *gdev;
350ba002bcSHans de Goede 
360ba002bcSHans de Goede 	/* misc_open sets filp->private_data to our misc device */
370ba002bcSHans de Goede 	gdev = container_of(filp->private_data, struct vbg_dev, misc_device);
380ba002bcSHans de Goede 
390ba002bcSHans de Goede 	session = vbg_core_open_session(gdev, false);
400ba002bcSHans de Goede 	if (IS_ERR(session))
410ba002bcSHans de Goede 		return PTR_ERR(session);
420ba002bcSHans de Goede 
430ba002bcSHans de Goede 	filp->private_data = session;
440ba002bcSHans de Goede 	return 0;
450ba002bcSHans de Goede }
460ba002bcSHans de Goede 
470ba002bcSHans de Goede static int vbg_misc_device_user_open(struct inode *inode, struct file *filp)
480ba002bcSHans de Goede {
490ba002bcSHans de Goede 	struct vbg_session *session;
500ba002bcSHans de Goede 	struct vbg_dev *gdev;
510ba002bcSHans de Goede 
520ba002bcSHans de Goede 	/* misc_open sets filp->private_data to our misc device */
530ba002bcSHans de Goede 	gdev = container_of(filp->private_data, struct vbg_dev,
540ba002bcSHans de Goede 			    misc_device_user);
550ba002bcSHans de Goede 
560ba002bcSHans de Goede 	session = vbg_core_open_session(gdev, false);
570ba002bcSHans de Goede 	if (IS_ERR(session))
580ba002bcSHans de Goede 		return PTR_ERR(session);
590ba002bcSHans de Goede 
600ba002bcSHans de Goede 	filp->private_data = session;
610ba002bcSHans de Goede 	return 0;
620ba002bcSHans de Goede }
630ba002bcSHans de Goede 
640ba002bcSHans de Goede /**
650ba002bcSHans de Goede  * Close device.
660ba002bcSHans de Goede  * Return: 0 on success, negated errno on failure.
670ba002bcSHans de Goede  * @inode:		Pointer to inode info structure.
680ba002bcSHans de Goede  * @filp:		Associated file pointer.
690ba002bcSHans de Goede  */
700ba002bcSHans de Goede static int vbg_misc_device_close(struct inode *inode, struct file *filp)
710ba002bcSHans de Goede {
720ba002bcSHans de Goede 	vbg_core_close_session(filp->private_data);
730ba002bcSHans de Goede 	filp->private_data = NULL;
740ba002bcSHans de Goede 	return 0;
750ba002bcSHans de Goede }
760ba002bcSHans de Goede 
770ba002bcSHans de Goede /**
780ba002bcSHans de Goede  * Device I/O Control entry point.
790ba002bcSHans de Goede  * Return: 0 on success, negated errno on failure.
800ba002bcSHans de Goede  * @filp:		Associated file pointer.
810ba002bcSHans de Goede  * @req:		The request specified to ioctl().
820ba002bcSHans de Goede  * @arg:		The argument specified to ioctl().
830ba002bcSHans de Goede  */
840ba002bcSHans de Goede static long vbg_misc_device_ioctl(struct file *filp, unsigned int req,
850ba002bcSHans de Goede 				  unsigned long arg)
860ba002bcSHans de Goede {
870ba002bcSHans de Goede 	struct vbg_session *session = filp->private_data;
880ba002bcSHans de Goede 	size_t returned_size, size;
890ba002bcSHans de Goede 	struct vbg_ioctl_hdr hdr;
90faf6a2a4SHans de Goede 	bool is_vmmdev_req;
910ba002bcSHans de Goede 	int ret = 0;
920ba002bcSHans de Goede 	void *buf;
930ba002bcSHans de Goede 
940ba002bcSHans de Goede 	if (copy_from_user(&hdr, (void *)arg, sizeof(hdr)))
950ba002bcSHans de Goede 		return -EFAULT;
960ba002bcSHans de Goede 
970ba002bcSHans de Goede 	if (hdr.version != VBG_IOCTL_HDR_VERSION)
980ba002bcSHans de Goede 		return -EINVAL;
990ba002bcSHans de Goede 
1000ba002bcSHans de Goede 	if (hdr.size_in < sizeof(hdr) ||
1010ba002bcSHans de Goede 	    (hdr.size_out && hdr.size_out < sizeof(hdr)))
1020ba002bcSHans de Goede 		return -EINVAL;
1030ba002bcSHans de Goede 
1040ba002bcSHans de Goede 	size = max(hdr.size_in, hdr.size_out);
1050ba002bcSHans de Goede 	if (_IOC_SIZE(req) && _IOC_SIZE(req) != size)
1060ba002bcSHans de Goede 		return -EINVAL;
1070ba002bcSHans de Goede 	if (size > SZ_16M)
1080ba002bcSHans de Goede 		return -E2BIG;
1090ba002bcSHans de Goede 
110faf6a2a4SHans de Goede 	/*
111faf6a2a4SHans de Goede 	 * IOCTL_VMMDEV_REQUEST needs the buffer to be below 4G to avoid
112faf6a2a4SHans de Goede 	 * the need for a bounce-buffer and another copy later on.
113faf6a2a4SHans de Goede 	 */
114faf6a2a4SHans de Goede 	is_vmmdev_req = (req & ~IOCSIZE_MASK) == VBG_IOCTL_VMMDEV_REQUEST(0) ||
115faf6a2a4SHans de Goede 			 req == VBG_IOCTL_VMMDEV_REQUEST_BIG;
116faf6a2a4SHans de Goede 
117faf6a2a4SHans de Goede 	if (is_vmmdev_req)
118faf6a2a4SHans de Goede 		buf = vbg_req_alloc(size, VBG_IOCTL_HDR_TYPE_DEFAULT);
119faf6a2a4SHans de Goede 	else
120faf6a2a4SHans de Goede 		buf = kmalloc(size, GFP_KERNEL);
1210ba002bcSHans de Goede 	if (!buf)
1220ba002bcSHans de Goede 		return -ENOMEM;
1230ba002bcSHans de Goede 
1240ba002bcSHans de Goede 	if (copy_from_user(buf, (void *)arg, hdr.size_in)) {
1250ba002bcSHans de Goede 		ret = -EFAULT;
1260ba002bcSHans de Goede 		goto out;
1270ba002bcSHans de Goede 	}
1280ba002bcSHans de Goede 	if (hdr.size_in < size)
1290ba002bcSHans de Goede 		memset(buf + hdr.size_in, 0, size -  hdr.size_in);
1300ba002bcSHans de Goede 
1310ba002bcSHans de Goede 	ret = vbg_core_ioctl(session, req, buf);
1320ba002bcSHans de Goede 	if (ret)
1330ba002bcSHans de Goede 		goto out;
1340ba002bcSHans de Goede 
1350ba002bcSHans de Goede 	returned_size = ((struct vbg_ioctl_hdr *)buf)->size_out;
1360ba002bcSHans de Goede 	if (returned_size > size) {
1370ba002bcSHans de Goede 		vbg_debug("%s: too much output data %zu > %zu\n",
1380ba002bcSHans de Goede 			  __func__, returned_size, size);
1390ba002bcSHans de Goede 		returned_size = size;
1400ba002bcSHans de Goede 	}
1410ba002bcSHans de Goede 	if (copy_to_user((void *)arg, buf, returned_size) != 0)
1420ba002bcSHans de Goede 		ret = -EFAULT;
1430ba002bcSHans de Goede 
1440ba002bcSHans de Goede out:
145faf6a2a4SHans de Goede 	if (is_vmmdev_req)
146faf6a2a4SHans de Goede 		vbg_req_free(buf, size);
147faf6a2a4SHans de Goede 	else
1480ba002bcSHans de Goede 		kfree(buf);
1490ba002bcSHans de Goede 
1500ba002bcSHans de Goede 	return ret;
1510ba002bcSHans de Goede }
1520ba002bcSHans de Goede 
1530ba002bcSHans de Goede /** The file_operations structures. */
1540ba002bcSHans de Goede static const struct file_operations vbg_misc_device_fops = {
1550ba002bcSHans de Goede 	.owner			= THIS_MODULE,
1560ba002bcSHans de Goede 	.open			= vbg_misc_device_open,
1570ba002bcSHans de Goede 	.release		= vbg_misc_device_close,
1580ba002bcSHans de Goede 	.unlocked_ioctl		= vbg_misc_device_ioctl,
1590ba002bcSHans de Goede #ifdef CONFIG_COMPAT
1600ba002bcSHans de Goede 	.compat_ioctl		= vbg_misc_device_ioctl,
1610ba002bcSHans de Goede #endif
1620ba002bcSHans de Goede };
1630ba002bcSHans de Goede static const struct file_operations vbg_misc_device_user_fops = {
1640ba002bcSHans de Goede 	.owner			= THIS_MODULE,
1650ba002bcSHans de Goede 	.open			= vbg_misc_device_user_open,
1660ba002bcSHans de Goede 	.release		= vbg_misc_device_close,
1670ba002bcSHans de Goede 	.unlocked_ioctl		= vbg_misc_device_ioctl,
1680ba002bcSHans de Goede #ifdef CONFIG_COMPAT
1690ba002bcSHans de Goede 	.compat_ioctl		= vbg_misc_device_ioctl,
1700ba002bcSHans de Goede #endif
1710ba002bcSHans de Goede };
1720ba002bcSHans de Goede 
1730ba002bcSHans de Goede /**
1740ba002bcSHans de Goede  * Called when the input device is first opened.
1750ba002bcSHans de Goede  *
1760ba002bcSHans de Goede  * Sets up absolute mouse reporting.
1770ba002bcSHans de Goede  */
1780ba002bcSHans de Goede static int vbg_input_open(struct input_dev *input)
1790ba002bcSHans de Goede {
1800ba002bcSHans de Goede 	struct vbg_dev *gdev = input_get_drvdata(input);
1810ba002bcSHans de Goede 	u32 feat = VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE | VMMDEV_MOUSE_NEW_PROTOCOL;
1820ba002bcSHans de Goede 	int ret;
1830ba002bcSHans de Goede 
1840ba002bcSHans de Goede 	ret = vbg_core_set_mouse_status(gdev, feat);
1850ba002bcSHans de Goede 	if (ret)
1860ba002bcSHans de Goede 		return ret;
1870ba002bcSHans de Goede 
1880ba002bcSHans de Goede 	return 0;
1890ba002bcSHans de Goede }
1900ba002bcSHans de Goede 
1910ba002bcSHans de Goede /**
1920ba002bcSHans de Goede  * Called if all open handles to the input device are closed.
1930ba002bcSHans de Goede  *
1940ba002bcSHans de Goede  * Disables absolute reporting.
1950ba002bcSHans de Goede  */
1960ba002bcSHans de Goede static void vbg_input_close(struct input_dev *input)
1970ba002bcSHans de Goede {
1980ba002bcSHans de Goede 	struct vbg_dev *gdev = input_get_drvdata(input);
1990ba002bcSHans de Goede 
2000ba002bcSHans de Goede 	vbg_core_set_mouse_status(gdev, 0);
2010ba002bcSHans de Goede }
2020ba002bcSHans de Goede 
2030ba002bcSHans de Goede /**
2040ba002bcSHans de Goede  * Creates the kernel input device.
2050ba002bcSHans de Goede  *
2060ba002bcSHans de Goede  * Return: 0 on success, negated errno on failure.
2070ba002bcSHans de Goede  */
2080ba002bcSHans de Goede static int vbg_create_input_device(struct vbg_dev *gdev)
2090ba002bcSHans de Goede {
2100ba002bcSHans de Goede 	struct input_dev *input;
2110ba002bcSHans de Goede 
2120ba002bcSHans de Goede 	input = devm_input_allocate_device(gdev->dev);
2130ba002bcSHans de Goede 	if (!input)
2140ba002bcSHans de Goede 		return -ENOMEM;
2150ba002bcSHans de Goede 
2160ba002bcSHans de Goede 	input->id.bustype = BUS_PCI;
2170ba002bcSHans de Goede 	input->id.vendor = VBOX_VENDORID;
2180ba002bcSHans de Goede 	input->id.product = VMMDEV_DEVICEID;
2190ba002bcSHans de Goede 	input->open = vbg_input_open;
2200ba002bcSHans de Goede 	input->close = vbg_input_close;
2210ba002bcSHans de Goede 	input->dev.parent = gdev->dev;
2220ba002bcSHans de Goede 	input->name = "VirtualBox mouse integration";
2230ba002bcSHans de Goede 
2240ba002bcSHans de Goede 	input_set_abs_params(input, ABS_X, VMMDEV_MOUSE_RANGE_MIN,
2250ba002bcSHans de Goede 			     VMMDEV_MOUSE_RANGE_MAX, 0, 0);
2260ba002bcSHans de Goede 	input_set_abs_params(input, ABS_Y, VMMDEV_MOUSE_RANGE_MIN,
2270ba002bcSHans de Goede 			     VMMDEV_MOUSE_RANGE_MAX, 0, 0);
2280ba002bcSHans de Goede 	input_set_capability(input, EV_KEY, BTN_MOUSE);
2290ba002bcSHans de Goede 	input_set_drvdata(input, gdev);
2300ba002bcSHans de Goede 
2310ba002bcSHans de Goede 	gdev->input = input;
2320ba002bcSHans de Goede 
2330ba002bcSHans de Goede 	return input_register_device(gdev->input);
2340ba002bcSHans de Goede }
2350ba002bcSHans de Goede 
2360ba002bcSHans de Goede static ssize_t host_version_show(struct device *dev,
2370ba002bcSHans de Goede 				 struct device_attribute *attr, char *buf)
2380ba002bcSHans de Goede {
2390ba002bcSHans de Goede 	struct vbg_dev *gdev = dev_get_drvdata(dev);
2400ba002bcSHans de Goede 
2410ba002bcSHans de Goede 	return sprintf(buf, "%s\n", gdev->host_version);
2420ba002bcSHans de Goede }
2430ba002bcSHans de Goede 
2440ba002bcSHans de Goede static ssize_t host_features_show(struct device *dev,
2450ba002bcSHans de Goede 				 struct device_attribute *attr, char *buf)
2460ba002bcSHans de Goede {
2470ba002bcSHans de Goede 	struct vbg_dev *gdev = dev_get_drvdata(dev);
2480ba002bcSHans de Goede 
2490ba002bcSHans de Goede 	return sprintf(buf, "%#x\n", gdev->host_features);
2500ba002bcSHans de Goede }
2510ba002bcSHans de Goede 
2520ba002bcSHans de Goede static DEVICE_ATTR_RO(host_version);
2530ba002bcSHans de Goede static DEVICE_ATTR_RO(host_features);
2540ba002bcSHans de Goede 
2550ba002bcSHans de Goede /**
2560ba002bcSHans de Goede  * Does the PCI detection and init of the device.
2570ba002bcSHans de Goede  *
2580ba002bcSHans de Goede  * Return: 0 on success, negated errno on failure.
2590ba002bcSHans de Goede  */
2600ba002bcSHans de Goede static int vbg_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
2610ba002bcSHans de Goede {
2620ba002bcSHans de Goede 	struct device *dev = &pci->dev;
2630ba002bcSHans de Goede 	resource_size_t io, io_len, mmio, mmio_len;
2640ba002bcSHans de Goede 	struct vmmdev_memory *vmmdev;
2650ba002bcSHans de Goede 	struct vbg_dev *gdev;
2660ba002bcSHans de Goede 	int ret;
2670ba002bcSHans de Goede 
2680ba002bcSHans de Goede 	gdev = devm_kzalloc(dev, sizeof(*gdev), GFP_KERNEL);
2690ba002bcSHans de Goede 	if (!gdev)
2700ba002bcSHans de Goede 		return -ENOMEM;
2710ba002bcSHans de Goede 
2720ba002bcSHans de Goede 	ret = pci_enable_device(pci);
2730ba002bcSHans de Goede 	if (ret != 0) {
2740ba002bcSHans de Goede 		vbg_err("vboxguest: Error enabling device: %d\n", ret);
2750ba002bcSHans de Goede 		return ret;
2760ba002bcSHans de Goede 	}
2770ba002bcSHans de Goede 
2780ba002bcSHans de Goede 	ret = -ENODEV;
2790ba002bcSHans de Goede 
2800ba002bcSHans de Goede 	io = pci_resource_start(pci, 0);
2810ba002bcSHans de Goede 	io_len = pci_resource_len(pci, 0);
2820ba002bcSHans de Goede 	if (!io || !io_len) {
2830ba002bcSHans de Goede 		vbg_err("vboxguest: Error IO-port resource (0) is missing\n");
2840ba002bcSHans de Goede 		goto err_disable_pcidev;
2850ba002bcSHans de Goede 	}
2860ba002bcSHans de Goede 	if (devm_request_region(dev, io, io_len, DEVICE_NAME) == NULL) {
2870ba002bcSHans de Goede 		vbg_err("vboxguest: Error could not claim IO resource\n");
2880ba002bcSHans de Goede 		ret = -EBUSY;
2890ba002bcSHans de Goede 		goto err_disable_pcidev;
2900ba002bcSHans de Goede 	}
2910ba002bcSHans de Goede 
2920ba002bcSHans de Goede 	mmio = pci_resource_start(pci, 1);
2930ba002bcSHans de Goede 	mmio_len = pci_resource_len(pci, 1);
2940ba002bcSHans de Goede 	if (!mmio || !mmio_len) {
2950ba002bcSHans de Goede 		vbg_err("vboxguest: Error MMIO resource (1) is missing\n");
2960ba002bcSHans de Goede 		goto err_disable_pcidev;
2970ba002bcSHans de Goede 	}
2980ba002bcSHans de Goede 
2990ba002bcSHans de Goede 	if (devm_request_mem_region(dev, mmio, mmio_len, DEVICE_NAME) == NULL) {
3000ba002bcSHans de Goede 		vbg_err("vboxguest: Error could not claim MMIO resource\n");
3010ba002bcSHans de Goede 		ret = -EBUSY;
3020ba002bcSHans de Goede 		goto err_disable_pcidev;
3030ba002bcSHans de Goede 	}
3040ba002bcSHans de Goede 
3050ba002bcSHans de Goede 	vmmdev = devm_ioremap(dev, mmio, mmio_len);
3060ba002bcSHans de Goede 	if (!vmmdev) {
3070b598e4fSArnd Bergmann 		vbg_err("vboxguest: Error ioremap failed; MMIO addr=%pap size=%pap\n",
3080b598e4fSArnd Bergmann 			&mmio, &mmio_len);
3090ba002bcSHans de Goede 		goto err_disable_pcidev;
3100ba002bcSHans de Goede 	}
3110ba002bcSHans de Goede 
3120ba002bcSHans de Goede 	/* Validate MMIO region version and size. */
3130ba002bcSHans de Goede 	if (vmmdev->version != VMMDEV_MEMORY_VERSION ||
3140ba002bcSHans de Goede 	    vmmdev->size < 32 || vmmdev->size > mmio_len) {
3150ba002bcSHans de Goede 		vbg_err("vboxguest: Bogus VMMDev memory; version=%08x (expected %08x) size=%d (expected <= %d)\n",
3160ba002bcSHans de Goede 			vmmdev->version, VMMDEV_MEMORY_VERSION,
3170ba002bcSHans de Goede 			vmmdev->size, (int)mmio_len);
3180ba002bcSHans de Goede 		goto err_disable_pcidev;
3190ba002bcSHans de Goede 	}
3200ba002bcSHans de Goede 
3210ba002bcSHans de Goede 	gdev->io_port = io;
3220ba002bcSHans de Goede 	gdev->mmio = vmmdev;
3230ba002bcSHans de Goede 	gdev->dev = dev;
3240ba002bcSHans de Goede 	gdev->misc_device.minor = MISC_DYNAMIC_MINOR;
3250ba002bcSHans de Goede 	gdev->misc_device.name = DEVICE_NAME;
3260ba002bcSHans de Goede 	gdev->misc_device.fops = &vbg_misc_device_fops;
3270ba002bcSHans de Goede 	gdev->misc_device_user.minor = MISC_DYNAMIC_MINOR;
3280ba002bcSHans de Goede 	gdev->misc_device_user.name = DEVICE_NAME_USER;
3290ba002bcSHans de Goede 	gdev->misc_device_user.fops = &vbg_misc_device_user_fops;
3300ba002bcSHans de Goede 
3310ba002bcSHans de Goede 	ret = vbg_core_init(gdev, VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
3320ba002bcSHans de Goede 	if (ret)
3330ba002bcSHans de Goede 		goto err_disable_pcidev;
3340ba002bcSHans de Goede 
3350ba002bcSHans de Goede 	ret = vbg_create_input_device(gdev);
3360ba002bcSHans de Goede 	if (ret) {
3370ba002bcSHans de Goede 		vbg_err("vboxguest: Error creating input device: %d\n", ret);
3380ba002bcSHans de Goede 		goto err_vbg_core_exit;
3390ba002bcSHans de Goede 	}
3400ba002bcSHans de Goede 
3410ba002bcSHans de Goede 	ret = devm_request_irq(dev, pci->irq, vbg_core_isr, IRQF_SHARED,
3420ba002bcSHans de Goede 			       DEVICE_NAME, gdev);
3430ba002bcSHans de Goede 	if (ret) {
3440ba002bcSHans de Goede 		vbg_err("vboxguest: Error requesting irq: %d\n", ret);
3450ba002bcSHans de Goede 		goto err_vbg_core_exit;
3460ba002bcSHans de Goede 	}
3470ba002bcSHans de Goede 
3480ba002bcSHans de Goede 	ret = misc_register(&gdev->misc_device);
3490ba002bcSHans de Goede 	if (ret) {
3500ba002bcSHans de Goede 		vbg_err("vboxguest: Error misc_register %s failed: %d\n",
3510ba002bcSHans de Goede 			DEVICE_NAME, ret);
3520ba002bcSHans de Goede 		goto err_vbg_core_exit;
3530ba002bcSHans de Goede 	}
3540ba002bcSHans de Goede 
3550ba002bcSHans de Goede 	ret = misc_register(&gdev->misc_device_user);
3560ba002bcSHans de Goede 	if (ret) {
3570ba002bcSHans de Goede 		vbg_err("vboxguest: Error misc_register %s failed: %d\n",
3580ba002bcSHans de Goede 			DEVICE_NAME_USER, ret);
3590ba002bcSHans de Goede 		goto err_unregister_misc_device;
3600ba002bcSHans de Goede 	}
3610ba002bcSHans de Goede 
3620ba002bcSHans de Goede 	mutex_lock(&vbg_gdev_mutex);
3630ba002bcSHans de Goede 	if (!vbg_gdev)
3640ba002bcSHans de Goede 		vbg_gdev = gdev;
3650ba002bcSHans de Goede 	else
3660ba002bcSHans de Goede 		ret = -EBUSY;
3670ba002bcSHans de Goede 	mutex_unlock(&vbg_gdev_mutex);
3680ba002bcSHans de Goede 
3690ba002bcSHans de Goede 	if (ret) {
3700ba002bcSHans de Goede 		vbg_err("vboxguest: Error more then 1 vbox guest pci device\n");
3710ba002bcSHans de Goede 		goto err_unregister_misc_device_user;
3720ba002bcSHans de Goede 	}
3730ba002bcSHans de Goede 
3740ba002bcSHans de Goede 	pci_set_drvdata(pci, gdev);
3750ba002bcSHans de Goede 	device_create_file(dev, &dev_attr_host_version);
3760ba002bcSHans de Goede 	device_create_file(dev, &dev_attr_host_features);
3770ba002bcSHans de Goede 
3780b598e4fSArnd Bergmann 	vbg_info("vboxguest: misc device minor %d, IRQ %d, I/O port %x, MMIO at %pap (size %pap)\n",
3790ba002bcSHans de Goede 		 gdev->misc_device.minor, pci->irq, gdev->io_port,
3800b598e4fSArnd Bergmann 		 &mmio, &mmio_len);
3810ba002bcSHans de Goede 
3820ba002bcSHans de Goede 	return 0;
3830ba002bcSHans de Goede 
3840ba002bcSHans de Goede err_unregister_misc_device_user:
3850ba002bcSHans de Goede 	misc_deregister(&gdev->misc_device_user);
3860ba002bcSHans de Goede err_unregister_misc_device:
3870ba002bcSHans de Goede 	misc_deregister(&gdev->misc_device);
3880ba002bcSHans de Goede err_vbg_core_exit:
3890ba002bcSHans de Goede 	vbg_core_exit(gdev);
3900ba002bcSHans de Goede err_disable_pcidev:
3910ba002bcSHans de Goede 	pci_disable_device(pci);
3920ba002bcSHans de Goede 
3930ba002bcSHans de Goede 	return ret;
3940ba002bcSHans de Goede }
3950ba002bcSHans de Goede 
3960ba002bcSHans de Goede static void vbg_pci_remove(struct pci_dev *pci)
3970ba002bcSHans de Goede {
3980ba002bcSHans de Goede 	struct vbg_dev *gdev = pci_get_drvdata(pci);
3990ba002bcSHans de Goede 
4000ba002bcSHans de Goede 	mutex_lock(&vbg_gdev_mutex);
4010ba002bcSHans de Goede 	vbg_gdev = NULL;
4020ba002bcSHans de Goede 	mutex_unlock(&vbg_gdev_mutex);
4030ba002bcSHans de Goede 
4040ba002bcSHans de Goede 	device_remove_file(gdev->dev, &dev_attr_host_features);
4050ba002bcSHans de Goede 	device_remove_file(gdev->dev, &dev_attr_host_version);
4060ba002bcSHans de Goede 	misc_deregister(&gdev->misc_device_user);
4070ba002bcSHans de Goede 	misc_deregister(&gdev->misc_device);
4080ba002bcSHans de Goede 	vbg_core_exit(gdev);
4090ba002bcSHans de Goede 	pci_disable_device(pci);
4100ba002bcSHans de Goede }
4110ba002bcSHans de Goede 
4120ba002bcSHans de Goede struct vbg_dev *vbg_get_gdev(void)
4130ba002bcSHans de Goede {
4140ba002bcSHans de Goede 	mutex_lock(&vbg_gdev_mutex);
4150ba002bcSHans de Goede 
4160ba002bcSHans de Goede 	/*
4170ba002bcSHans de Goede 	 * Note on success we keep the mutex locked until vbg_put_gdev(),
4180ba002bcSHans de Goede 	 * this stops vbg_pci_remove from removing the device from underneath
4190ba002bcSHans de Goede 	 * vboxsf. vboxsf will only hold a reference for a short while.
4200ba002bcSHans de Goede 	 */
4210ba002bcSHans de Goede 	if (vbg_gdev)
4220ba002bcSHans de Goede 		return vbg_gdev;
4230ba002bcSHans de Goede 
4240ba002bcSHans de Goede 	mutex_unlock(&vbg_gdev_mutex);
4250ba002bcSHans de Goede 	return ERR_PTR(-ENODEV);
4260ba002bcSHans de Goede }
4270ba002bcSHans de Goede EXPORT_SYMBOL(vbg_get_gdev);
4280ba002bcSHans de Goede 
4290ba002bcSHans de Goede void vbg_put_gdev(struct vbg_dev *gdev)
4300ba002bcSHans de Goede {
4310ba002bcSHans de Goede 	WARN_ON(gdev != vbg_gdev);
4320ba002bcSHans de Goede 	mutex_unlock(&vbg_gdev_mutex);
4330ba002bcSHans de Goede }
4340ba002bcSHans de Goede EXPORT_SYMBOL(vbg_put_gdev);
4350ba002bcSHans de Goede 
4360ba002bcSHans de Goede /**
4370ba002bcSHans de Goede  * Callback for mouse events.
4380ba002bcSHans de Goede  *
4390ba002bcSHans de Goede  * This is called at the end of the ISR, after leaving the event spinlock, if
4400ba002bcSHans de Goede  * VMMDEV_EVENT_MOUSE_POSITION_CHANGED was raised by the host.
4410ba002bcSHans de Goede  *
4420ba002bcSHans de Goede  * @gdev:		The device extension.
4430ba002bcSHans de Goede  */
4440ba002bcSHans de Goede void vbg_linux_mouse_event(struct vbg_dev *gdev)
4450ba002bcSHans de Goede {
4460ba002bcSHans de Goede 	int rc;
4470ba002bcSHans de Goede 
4480ba002bcSHans de Goede 	/* Report events to the kernel input device */
4490ba002bcSHans de Goede 	gdev->mouse_status_req->mouse_features = 0;
4500ba002bcSHans de Goede 	gdev->mouse_status_req->pointer_pos_x = 0;
4510ba002bcSHans de Goede 	gdev->mouse_status_req->pointer_pos_y = 0;
4520ba002bcSHans de Goede 	rc = vbg_req_perform(gdev, gdev->mouse_status_req);
4530ba002bcSHans de Goede 	if (rc >= 0) {
4540ba002bcSHans de Goede 		input_report_abs(gdev->input, ABS_X,
4550ba002bcSHans de Goede 				 gdev->mouse_status_req->pointer_pos_x);
4560ba002bcSHans de Goede 		input_report_abs(gdev->input, ABS_Y,
4570ba002bcSHans de Goede 				 gdev->mouse_status_req->pointer_pos_y);
4580ba002bcSHans de Goede 		input_sync(gdev->input);
4590ba002bcSHans de Goede 	}
4600ba002bcSHans de Goede }
4610ba002bcSHans de Goede 
4620ba002bcSHans de Goede static const struct pci_device_id vbg_pci_ids[] = {
4630ba002bcSHans de Goede 	{ .vendor = VBOX_VENDORID, .device = VMMDEV_DEVICEID },
4640ba002bcSHans de Goede 	{}
4650ba002bcSHans de Goede };
4660ba002bcSHans de Goede MODULE_DEVICE_TABLE(pci,  vbg_pci_ids);
4670ba002bcSHans de Goede 
4680ba002bcSHans de Goede static struct pci_driver vbg_pci_driver = {
4690ba002bcSHans de Goede 	.name		= DEVICE_NAME,
4700ba002bcSHans de Goede 	.id_table	= vbg_pci_ids,
4710ba002bcSHans de Goede 	.probe		= vbg_pci_probe,
4720ba002bcSHans de Goede 	.remove		= vbg_pci_remove,
4730ba002bcSHans de Goede };
4740ba002bcSHans de Goede 
4750ba002bcSHans de Goede module_pci_driver(vbg_pci_driver);
4760ba002bcSHans de Goede 
4770ba002bcSHans de Goede MODULE_AUTHOR("Oracle Corporation");
4780ba002bcSHans de Goede MODULE_DESCRIPTION("Oracle VM VirtualBox Guest Additions for Linux Module");
4790ba002bcSHans de Goede MODULE_LICENSE("GPL");
480