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