Newer
Older
TestKM / mouse-driver / mousek.c
@motoki miura motoki miura on 11 Nov 12 KB fix
/*
 *  mousek.c, A linux kernel module to control mouse pointer using keyboard
 *  Heavily based on usbmouse.c
 *  **Experimental**
 *
 *  Copyright (c) 2016 Kush Kumar Sharma
 *  Copyright (c) 2000 Alessandro Rubini <rubini@gnu.org>
 *  Copyright (c) 1999 Vojtech Pavlik <vojtech@suse.cz>
 */

/*
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>

/* usb stuff */
#include <linux/input.h>
#include <linux/usb.h>

/* miscdevice stuff */
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>

/*
 * Version Information
 */
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Kush Kumar Sharma"
#define DRIVER_DESC "Mouse driver"
#define DRIVER_LICENSE "GPL"

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);

#define DEVICE_NAME "mousek"
#define MAX_SCREEN 100
#define MIN_SCREEN 0

int Major;

/*
 * We need a local data structure, as it must be allocated for each new
 * mouse device plugged in the USB bus
 */

struct mousek_device
{
	signed char data[4];	/* use a 4-byte protocol */
	struct urb urb;			/* USB Request block, to get USB data*/
	struct input_dev *idev; /* input device, to push out input  data */
	int x, y;				/* keep track of the position of this device */
};

static struct mousek_device *mouse;

/*
 * Handler for data sent in by the device. The function is called by
 * the USB kernel subsystem whenever the device spits out new data

static void mousek_irq(struct urb *urb)
{ //useless right now

	struct mousek_device *mouse = urb->context;
	signed char *data = mouse->data;
	struct input_dev *idev = &mouse->idev;

	if (urb->status != USB_ST_NOERROR) return;

	// ignore data[0] which reports mouse buttons
	mouse->x += data[1];
	mouse->y += data[2];
	printk(KERN_DEBUG "mousek irq, %5i %5i\n", mouse->x, mouse->y);

	input_report_rel(mouse->idev, REL_X,     mouse->x);
	input_report_rel(mouse->idev, REL_Y,     mouse->y);
	input_report_rel(mouse->idev, REL_WHEEL, data[3]);

	input_sync(idev);
}
*/

/*
static irqreturn_t mousek_interrupt(int irq, void *dummy)
{
	input_report_key(mouse->idev, BTN_0, inb(BUTTON_PORT) & 1);
	input_sync(mouse->idev);
	return IRQ_HANDLED;
}
*/

/*
 * Called when a process tries to open the device file, like
 * "cat /dev/mycharfile"
 */
int mousek_open(struct inode *inode, struct file *filp)
{

	// filp->private_data = mouse;

	/* announce yourself */
	printk(KERN_INFO "mousek: faking an USB mouse via the misc device\n");

	// try_module_get(THIS_MODULE);

	return 0; /* Ok */
}

/* close releases the device, like mousek_disconnect */
int mousek_release(struct inode *inode, struct file *filp)
{
	// struct mousek_device *mousek = filp->private_data;
	// input_unregister_device(&mouse->idev);
	// kfree(mousek);
	// usb_kill_urb(mouse->urb);

	printk(KERN_INFO "mousek: closing misc device\n");

	/*
	 * Decrement the usage count, or else once you opened the file, you'll
	 * never get rid of the module.
	 */
	// module_put(THIS_MODULE);

	return 0;
}

/* poll reports the device as writeable */
ssize_t mousek_read(struct file *filp, /* see include/linux/fs.h   */
					char *buffer,	   /* buffer to fill with data */
					size_t length,	   /* length of the buffer     */
					loff_t *offset)
{
	printk(KERN_INFO "mousek: Restricted. That is okay but do not repeat this mistake.\n");
	return 0;
}

/* write accepts data and converts it to mouse movement
 * Called when a process writes to dev file: echo "hi" > /dev/hello
 */
struct input_dev *dev;
ssize_t mousek_write(struct file *filp, const char *buf, size_t count,
					 loff_t *offp)
{
	// struct mousek_device *mouse = filp->private_data;
	static char localbuf[16];
	// struct urb urb;
	int i, command = -1;

	/* accept 16 bytes at a time, at most */
	if (count > 16)
		count = 16;
	copy_from_user(localbuf, buf, count);

	/* prepare the urb for mousek_irq() */
	// urb.status = USB_ST_NOERROR;
	// urb.context = mousek;
	dev = mouse->idev;

	// first character will tell what command to do
	// i : instruction sequence like ududdr
	// x : absolute value in X Axis
	// y : absolute value in Y Axis
	// l : left click single, ll : double left click
	// r : right click

	switch (localbuf[0])
	{
	case 'i':
	case 'I':
	{
		command = 0;
		break;
	}
	case 'x':
	case 'X':
	{
		command = 1;
		break;
	}
	case 'y':
	case 'Y':
	{
		command = 2;
		break;
	}
	case 'l':
	case 'L':
	{
		// left click press down
		input_report_key(dev, BTN_LEFT, 1);
		// left click up
		input_report_key(dev, BTN_LEFT, 0);

		input_sync(dev);

		if (localbuf[1] == 'l')
		{
			// left click press down
			input_report_key(dev, BTN_LEFT, 1);
			// left click up
			input_report_key(dev, BTN_LEFT, 0);
		}

		break;
	}
	case 'r':
	case 'R':
	{
		// right click press down
		input_report_key(dev, BTN_RIGHT, 1);
		// right click up
		input_report_key(dev, BTN_RIGHT, 0);
		break;
	}
	}

	if (command == 0)
	{
		/* scan written sequence */
		for (i = 2; i < count; i++)
		{
			mouse->data[1] = mouse->data[2] = mouse->data[3] = 0;

			switch (localbuf[i])
			{
			case 'u':
			case 'U': /* up */
				mouse->data[2] = -10;
				break;
			case 'd':
			case 'D': /* down */
				mouse->data[2] = 10;
				break;
			case 'l':
			case 'L': /* left */
				mouse->data[1] = -10;
				break;
			case 'r':
			case 'R': /* right */
				mouse->data[1] = 10;
				break;

			case 'q': /* left click down */
				mouse->data[3] = 1;
				break;
			case 'Q': /* left click up */
				mouse->data[3] = 2;
				break;
			case 'w': /* right click down */
				mouse->data[3] = 3;
				break;
			case 'W': /* right click up */
				mouse->data[3] = 4;
				break;
			default:
				i = count;
				continue;
			}
			// input_report_key(dev, BTN_RIGHT, 1);
			// input_report_key(dev, BTN_RIGHT, 0);

			// input_report_key(dev, KEY_UP, 1); /* keypress */
			// input_report_key(dev, KEY_UP, 0); /* release */

			// input_report_abs(dev, ABS_X, 2+i);
			// input_report_abs(dev, ABS_Y, 90+i);
			// input_report_abs(dev, ABS_X, mouse->data[1]);
			// input_report_abs(dev, ABS_Y, mouse->data[2]);

			if (mouse->data[1] != 0)
			{
				input_report_rel(dev, REL_X, mouse->data[1]);

				input_sync(dev);
			}
			if (mouse->data[2] != 0)
			{
				input_report_rel(dev, REL_Y, mouse->data[2]);

				input_sync(dev);
			}

			// handle queue clicks
			if (mouse->data[3] != 0)
			{
				if (mouse->data[3] == 1)
				{
					input_report_key(dev, BTN_LEFT, 1);
				}
				else if (mouse->data[3] == 2)
				{
					input_report_key(dev, BTN_LEFT, 0);
				}
				else if (mouse->data[3] == 3)
				{
					input_report_key(dev, BTN_RIGHT, 1);
				}
				else if (mouse->data[3] == 4)
				{
					input_report_key(dev, BTN_RIGHT, 0);
				}

				input_sync(dev);
			}

			// input_report_rel(dev, REL_WHEEL, mouse->data[3]);

			// printk(KERN_ALERT "mousek: Control actions of %s received %d %d",localbuf, mouse->data[1], mouse->data[2]);
			/* if we are here, a valid key is there, fix the urb */
			// mousek_irq(&urb);

		} // for
	}
	if (command == 1 && count >= 2)
	{
		// X
		int val = 0;
		if (localbuf[2] == '-')
		{
			// negative
			val = localbuf[3] - '0'; // converting to int
			for (i = 4; i < count; i++)
			{
				if (localbuf[i] <= '9' && localbuf[i] >= '0')
				{
					val *= 10;
					val += localbuf[i] - '0';
				}
			}
			val *= -1;
		}
		else
		{
			// if not negative
			val = localbuf[2] - '0'; // converting to int
			for (i = 3; i < count; i++)
			{
				if (localbuf[i] <= '9' && localbuf[i] >= '0')
				{
					val *= 10;
					val += localbuf[i] - '0';
				}
			}
		}
		// printk(KERN_ALERT "Moving with value %d in X\n", val);

		input_report_rel(dev, REL_X, val);
		// input_report_abs(dev, ABS_X, val);
	}
	if (command == 2 && count >= 2)
	{
		// Y
		int val = 0;
		if (localbuf[2] == '-')
		{
			// negative
			val = localbuf[3] - '0'; // converting to int
			for (i = 4; i < count; i++)
			{
				if (localbuf[i] <= '9' && localbuf[i] >= '0')
				{
					val *= 10;
					val += localbuf[i] - '0';
				}
			}
			val *= -1;
		}
		else
		{
			// if not negative
			val = localbuf[2] - '0'; // converting to int
			for (i = 3; i < count; i++)
			{
				if (localbuf[i] <= '9' && localbuf[i] >= '0')
				{
					val *= 10;
					val += localbuf[i] - '0';
				}
			}
		}

		input_report_rel(dev, REL_Y, val);
		// input_report_abs(dev, ABS_Y, val);
	}

	// right click press down
	// input_report_key(dev, BTN_RIGHT, 1);
	// right click up
	// input_report_key(dev, BTN_RIGHT, 0);

	input_sync(dev);

	// input_report_rel(mouse->idev, REL_WHEEL, data[3]);

	return count;
}

struct file_operations mousek_fops = {
	write : mousek_write,
	read : mousek_read,
	open : mousek_open,
	release : mousek_release,
};

/*
 * Functions called at module load and unload time: only register and
 * unregister the USB callbacks and the misc entry point
 */
struct input_dev *input_dev;
int init_module(void)
{
	int retval;

	Major = register_chrdev(0, DEVICE_NAME, &mousek_fops);
	if (Major < 0)
	{
		printk(KERN_ALERT "Registering char device failed with %d\n", Major);
		return Major;
	}

	// if (request_irq(BUTTON_IRQ, mousek_interrupt, 0, DEVICE_NAME, NULL)) {
	//	printk(KERN_ERR "mousek: Can't allocate irq \n");
	//     return -EBUSY;
	// }

	/* allocate and zero a new data structure for the new device */
	mouse = kmalloc(sizeof(struct mousek_device), GFP_KERNEL);
	if (!mouse)
		return -ENOMEM; /* failure */
	memset(mouse, 0, sizeof(*mouse));

	input_dev = input_allocate_device();
	if (!input_dev)
	{
		printk(KERN_ERR "mousek.c: Not enough memory\n");
		retval = -ENOMEM;
		// goto err_free_irq;
	}
	// updating struct
	mouse->idev = input_dev;

	/* tell the features of this input device: fake only keys */
	// mouse->idev.evbit[0] = BIT(EV_KEY);
	/* and tell which keys: only the arrows */
	// set_bit(103, mouse->idev.keybit); /* Up    */
	// set_bit(105, mouse->idev.keybit); /* Left  */
	// set_bit(106, mouse->idev.keybit); /* Right */
	// set_bit(108, mouse->idev.keybit); /* Down  */

	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
	// input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

	// set_bit(103, input_dev->keybit); /* Up    */

	input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
	input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) | BIT_MASK(REL_WHEEL);
	// input_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);

	// input_set_abs_params(input_dev, ABS_X, MIN_SCREEN, MAX_SCREEN, 0, 0);
	// input_set_abs_params(input_dev, ABS_Y, MIN_SCREEN, MAX_SCREEN, 0, 0);

	input_dev->name = DEVICE_NAME;
	input_set_drvdata(input_dev, mouse);

	retval = input_register_device(input_dev);
	if (retval)
	{
		printk(KERN_ERR "mousek: Failed to register device\n");
		goto err_free_dev;
	}

	printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
	printk(KERN_INFO "the driver, create a dev file with\n");
	printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
	printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
	printk(KERN_INFO "the device file.\n");
	printk(KERN_INFO "Remove the device file and module when done.\n");

	return 0;

err_free_dev:
	input_free_device(mouse->idev);
	kfree(mouse);
	// err_free_irq:
	//	free_irq(BUTTON_IRQ, button_interrupt);
	return retval;
}

void cleanup_module(void)
{
	/*
	 * Unregister the device
	 */
	if (!mouse)
		return;

	input_unregister_device(mouse->idev);
	kfree(mouse);
	unregister_chrdev(Major, DEVICE_NAME);

	printk(KERN_ALERT "Uninstalled. Delete device from dev.");

	// ret = free_irq(BUTTON_IRQ, mousek_interrupt);
	// if (ret < 0)
	//	printk(KERN_ALERT "Error in freeing irq: %d\n", ret);
}