Newer
Older
TestKM / mouse-driver / mousek.c
/*
 *  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
 */
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;
	struct input_dev *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
 */
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;
    //}
	
	struct input_dev *input_dev;

    /* 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);

}