入門級 Mouse Linux Kernel Driver

2010年1月15日星期五
每當說起 Linux Kernel Driver 入門,就不免提到如何寫個 Hello World 級的 Module,這樣的第一支程式,除了可供 Linux Kernel 動態載入和卸載,似乎是一點用處也沒有。與一般應用程式不同,開發 Linux Driver 最大的門檻不在於如何撰寫出 Module,而是如何設計系統架構與硬體兩者間的橋樑。其中懂得如何控制和結合 Kernel 內各種機制更是重點,最複雜的莫過於此。

這邊有個 Mouse Kernel Driver,會在 Kernel 上新增一個虛擬滑鼠裝置,然後使用者可從 sysfs 控制該虛擬滑鼠(virmouse.c):
/*
 * A Virtual Mouse Driver to send fake events from userspace.
 *
 * Written by Fred Chien <fred@ullab.org>
 *
 */
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <linux/platform_device.h>

struct input_dev *virmouse_input_dev;
static struct platform_device *virmouse_dev; /* Device structure */

/* Sysfs method to input simulated coordinates */
static ssize_t write_virmouse(struct device *dev,
                              struct device_attribute *attr,
                              const char *buffer, size_t count)
{
        int x, y, key;

        /* parsing input data */
        sscanf(buffer, "%d%d%d", &x, &y, &key);

        /* Report relative coordinates */
        input_report_rel(virmouse_input_dev, REL_X, x);
        input_report_rel(virmouse_input_dev, REL_Y, y);

        printk ("virmouse_event: X:%d Y:%d %d\n", x, y, key);

        /* Report key event */
        if (key>0) {
                if (key==1)
                        input_report_key(virmouse_input_dev, BTN_LEFT, 1);
                else if (key==2)
                        input_report_key(virmouse_input_dev, BTN_MIDDLE, 1);
                else
                        input_report_key(virmouse_input_dev, BTN_RIGHT, 1);
        }

        input_sync(virmouse_input_dev);

        return count;

}

/* Attach the sysfs write method */
DEVICE_ATTR(vmevent, 0644, NULL, write_virmouse);

/* Attribute Descriptor */
static struct attribute *virmouse_attrs[] = {
        &dev_attr_vmevent.attr,
        NULL
};

/* Attribute group */
static struct attribute_group virmouse_attr_group = {
        .attrs = virmouse_attrs,
};

/* Driver Initializing */
int __init virmouse_init(void)
{
        /* Register a platform device */
        virmouse_dev = platform_device_register_simple("virmouse", -1, NULL, 0);
        if (IS_ERR(virmouse_dev)){
                printk ("virmouse_init: error\n");
                return PTR_ERR(virmouse_dev);
        }

        /* Create a sysfs node to read simulated coordinates */
        sysfs_create_group(&virmouse_dev->dev.kobj, &virmouse_attr_group);

        /* Allocate an input device data structure */
        virmouse_input_dev = input_allocate_device();
        if (!virmouse_input_dev) {
                printk("Bad input_allocate_device()\n");
                return -ENOMEM;
        }

        /* Announce that the virtual mouse will generate relative coordinates */
        set_bit(EV_REL, virmouse_input_dev->evbit);
        set_bit(REL_X, virmouse_input_dev->relbit);
        set_bit(REL_Y, virmouse_input_dev->relbit);
        set_bit(REL_WHEEL, virmouse_input_dev->relbit);


        /* Announce key event */
        set_bit(EV_KEY, virmouse_input_dev->evbit);
        set_bit(BTN_LEFT, virmouse_input_dev->keybit);
        set_bit(BTN_MIDDLE, virmouse_input_dev->keybit);
        set_bit(BTN_RIGHT, virmouse_input_dev->keybit);

        /* Register with the input subsystem */
        input_register_device(virmouse_input_dev);

        /* print messages in the dmesg */
        printk("Virtual Mouse Driver Initialized.\n");

        return 0;
}

/* Driver Uninitializing */
void virmouse_uninit(void)
{
        /* Unregister from the input subsystem */
        input_unregister_device(virmouse_input_dev);

        /* Remove sysfs node */
        sysfs_remove_group(&virmouse_dev->dev.kobj, &virmouse_attr_group);

        /* Unregister driver */
        platform_device_unregister(virmouse_dev);

        return;
}

module_init(virmouse_init);
module_exit(virmouse_uninit);

MODULE_AUTHOR("Fred Chien <fred@ullab.org>");
MODULE_DESCRIPTION("Virtual Mouse Driver");
MODULE_LICENSE("GPL");

然後建立 Makefile:
obj-m += virmouse.o

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD  := $(shell pwd)

default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
        @rm -fr *.ko *.o


編譯(需要安裝 Kernel header):
$ make

載入:
$ sudo insmod virmouse.ko

測試:
# 先切換成 root
$ sudo su -
# 滑鼠 X軸移動 168,Y軸移動 68,0 代表純移動不點擊
$ echo "168 68 0" > /sys/devices/platform/virmouse/vmevent

此 Driver 會先註冊成 evdev input 的滑鼠裝置,然後在 sysfs 並建立 group 和 vmevent 檔,Userspace 下的應用程式可以發送命令到 vmevent 使滑鼠移動或點擊左右中鍵。

後記:

此程式極為簡單,因此省略程式碼的說明,讀者直接看 source code 應該就能明瞭。

Comments

18 Comments

RSS

Copyright @ 2013 Fred's blog. Designed by Templateism | MyBloggerLab
載入中…

誰在追蹤 Fred

您可以贊助 Fred 持續寫作

廣告與公益


Blog Archive