Linux Driver Example: virmouse
我很喜歡在網路上找一些簡單的程式範例,一來可以幫助自己學習,二來以後要拿來用比較方便。找範例程式的最高指導原則是:簡單!一個程式最好只說明一樣東西,而且不需要加上太多的廢話說明就可以看懂,這樣以後拿來用比較快,不用在花太多時間去重新想這個程式在做什麼。今天要放上的程式範例是一個 Linux Driver 的範例,網路上大部分的範例都是「Hello World」,簡單,但這樣的範例幾乎不具備任何參考價值。今天在網路上看到 Fred 所撰寫的一個 virtual mouse 的範例,所以就紀錄在這裡。但因為在下功力太差,所以之後會在加上一些自己的說明。
Makefile 如下:
可以透過 echo "168 68 0" > /sys/devices/platform/virmouse/vmevent 進行操作。
這是一個簡單的程式,但我還是有幾個地方看不懂:第一:明明是一個簡單的 virtual mouse device,為什麼要宣告 input_dev 和 platform_device?;第二:vmevent 和 dev_attr_vmevent 是哪來的?
先回答第二個問題,答案就在 DEVICE_ATTR 這個 MACRO 裡面。來看看它的定義(linux/device.h):
很明顯的可以看出來 DEVICE_ATTR 有作一個宣告的動作。至於第一個問題,「感覺上」,platform_device 製作了一個虛擬的滑鼠(可以看作一個跟User Space 的 IO 介面),之後在透過 input_sync 把 event 同步到 input_device 上面,也就是真正對 kernel 的介面。
應該 ... 是這樣吧 ...
/* * A Virtual Mouse Driver to send fake events from userspace. * * Written by Fred Chien <fred@ullab.org> * Modified by Neokent. * 1. Change some coding styles. * 2. Add some comments for myself. * */ #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:this macro is used to declare a variable with name dev_attr_##_name. 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
可以透過 echo "168 68 0" > /sys/devices/platform/virmouse/vmevent 進行操作。
這是一個簡單的程式,但我還是有幾個地方看不懂:第一:明明是一個簡單的 virtual mouse device,為什麼要宣告 input_dev 和 platform_device?;第二:vmevent 和 dev_attr_vmevent 是哪來的?
先回答第二個問題,答案就在 DEVICE_ATTR 這個 MACRO 裡面。來看看它的定義(linux/device.h):
#define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
很明顯的可以看出來 DEVICE_ATTR 有作一個宣告的動作。至於第一個問題,「感覺上」,platform_device 製作了一個虛擬的滑鼠(可以看作一個跟User Space 的 IO 介面),之後在透過 input_sync 把 event 同步到 input_device 上面,也就是真正對 kernel 的介面。
應該 ... 是這樣吧 ...
留言
張貼留言