⇤ ← Revision 1 as of 2005-06-29 20:26:59
5776
Comment:
|
1455
|
Deletions are marked like this. | Additions are marked like this. |
Line 5: | Line 5: |
* Hello World Driver (intended to Linux Kernel 2.6(.12)) | * Virtual Memory Module (intended to Linux Kernel 2.6(.12)) |
Line 7: | Line 7: |
* Driver for the hello device. This driver is intended to show * and teach some basic kernel mechanics : * _ Linux Kernel Module programming * _ Driver programming * _ Debugfs operations * _ Semaphores Locking * _ Wait queues * * All that in a simple module. This module isn't very useful and I don't * know if someone will be interested. * However, to test it, you have to : * _ compile the module (I don't provide the Makefile, do it yourself :p) * _ load it * _ at the module loading, you will see a message : * "Major number X has been assigned to device hello" * If you don't see it, have a look in the file /var/log/messages * You now have to create the device node : * mknod /dev/hello c X 0 * _ you can now test the device driver and the hello device !:) * * How to use the hello device ? * Once you've loaded the module, there is "Nothing" in the device. If you try to read * it, the process will block. * It will block until another process write some data in the device. * You can see the device's data by reading the device or by the debugfs file system. * You can write to the data by writing to the device or by the debugfs interface. * To reset the device, write "Nothing\n" in the device. * * I provide two userland programm to read and write to the hello device : * _ read_to_hello (which can only read 256 bytes) * _ write_to_hello |
* This module just explains the concept and the mechanism of the Linux * virtual memory system |
Line 39: | Line 10: |
#include <linux/kernel.h> #include <linux/fs.h> |
|
Line 40: | Line 13: |
#include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> |
|
Line 45: | Line 15: |
#include <linux/poll.h> #include <linux/errno.h> #include <linux/debugfs.h> |
#include <linux/vmalloc.h> |
Line 49: | Line 17: |
#define DRIVER_AUTHOR "tyler@agat.net" #define DRIVER_DESC "Hello World Driver" #define DEVICE_NAME "hello" |
unsigned long new_page ; unsigned long temp ; |
Line 53: | Line 20: |
#define NOTH_LEN 9 | struct page *new_desc_page ; |
Line 55: | Line 22: |
static int major; static int count_user; static char *hello_msg; static struct semaphore hello_sem; static wait_queue_head_t attente; static wait_queue_head_t inq; static struct dentry *hello_dir; static struct dentry *hello_dentry; |
int init_module() { new_page = __get_free_page(GFP_KERNEL) ; |
Line 64: | Line 26: |
static int hello_open(struct inode *inode, struct file *file) { down(&hello_sem); count_user++; up(&hello_sem); |
new_desc_page = (struct page *)virt_to_page(new_page) ; |
Line 70: | Line 28: |
return 0; } |
/* * That's how the virtual memory system works. * To retrieve the virtual adress corresponding to the page, * we get the index in the page frame (new_desc_page-mem_map). * We then multiply it by the size of the page. We get the physical adress. * And in kernel mode, the differences between virtual and physical adresses * is just an offset of PAGE_OFFSET. So we add PAGE_OFFSET */ temp = (new_desc_page-mem_map)*PAGE_SIZE+PAGE_OFFSET ; |
Line 73: | Line 38: |
static int hello_release(struct inode *inode, struct file *file) { down(&hello_sem); count_user--; up(&hello_sem); |
printk("0x%x\n",temp) ; printk("0x%x\n",new_page) ; |
Line 79: | Line 41: |
return 0; } |
/* * In kernel mode, the virtual adresses has just an offset of PAGE_OFFSET * (the page tables are properly configured to do this) * On i386, the PAGE_OFFSET=0xC0000000 */ temp = __pa(new_page) ; |
Line 82: | Line 48: |
static ssize_t hello_read(struct inode *inode, char __user *user_buf, size_t len, loff_t offset) { int bytes = 0; |
printk("0x%x\n",new_page-PAGE_OFFSET) ; printk("0x%x\n",temp) ; |
Line 86: | Line 51: |
down(&hello_sem); while (strncmp(hello_msg, "Nothing\n", NOTH_LEN) == 0) { up(&hello_sem); printk(KERN_INFO "Process sleeps.\n"); interruptible_sleep_on(&attente); printk(KERN_INFO "Process wakes up.\n"); down(&hello_sem); } bytes = copy_to_user(user_buf, hello_msg, len); if (bytes < 0) { up(&hello_sem); return -EFAULT; } up(&hello_sem); return bytes; } static ssize_t hello_write(struct inode *inode, const char __user *user_buf, size_t len, loff_t offset) { int bytes = 0; down(&hello_sem); if (hello_msg) { kfree(hello_msg); hello_msg = kmalloc(len+1, GFP_KERNEL); if (!hello_msg) { up(&hello_sem); return -ENOMEM; } } bytes = copy_from_user(hello_msg, user_buf, len); if (bytes < 0) { up(&hello_sem); return -EFAULT; } hello_msg[len] = '\0'; up(&hello_sem); wake_up_interruptible(&attente); return bytes; |
return 0 ; |
Line 129: | Line 54: |
static unsigned int hello_poll(struct file *filp, poll_table *wait) | void cleanup_module() |
Line 131: | Line 56: |
unsigned int mask = 0; poll_wait(filp, &inq, wait); if (strncmp(hello_msg, "Nothing\n", NOTH_LEN) != 0) mask |= POLLIN | POLLRDNORM; mask |= POLLOUT | POLLWRNORM; return mask; } static loff_t hello_llseek(struct file *filp, loff_t off) { loff_t new_pos; new_pos = filp->f_pos + off; if (new_pos < 0) return -EINVAL; return new_pos; } static int debug_hello_open(struct inode *inode, struct file *file) { return 0; |
free_page(new_page) ; |
Line 156: | Line 58: |
static int debug_hello_release(struct inode *inode, struct file *file) { return 0; } static ssize_t debug_hello_read(struct file *file, char __user *user_buf, size_t count, loff_t *off) { int len; down(&hello_sem); len = copy_to_user(user_buf,hello_msg,count); up(&hello_sem); if (len < 0) { return -EFAULT; } return len; } static struct file_operations fops = { read: hello_read, write: hello_write, open: hello_open, release: hello_release, poll: hello_poll, llseek: hello_llseek, }; static struct file_operations debug_fops = { read: debug_hello_read, open: debug_hello_open, release: debug_hello_release, }; int __init loading(void) { hello_msg = kmalloc(NOTH_LEN, GFP_KERNEL); if (!hello_msg) { up(&hello_sem); return -ENOMEM; } sprintf(hello_msg, "Nothing\n"); major = register_chrdev(major, DEVICE_NAME, &fops); if (major < 0) { printk(KERN_ERR "Cannot allocate a major number.\n"); return major; } printk(KERN_INFO "Major number %d has been assigned to device %s.\n", major, DEVICE_NAME); sema_init(&hello_sem, 1); init_waitqueue_head(&attente); init_waitqueue_head(&inq); hello_dir = debugfs_create_dir("hello_dir", NULL); hello_dentry = debugfs_create_file("hello", 0644, hello_dir, NULL, &debug_fops); return 0; } void __exit unloading(void) { int ret = 0; ret = unregister_chrdev(major, DEVICE_NAME); if (ret < 0) { printk(KERN_ERR "unregister_chrdev() error.\n"); } debugfs_remove(hello_dentry); debugfs_remove(hello_dir); if (hello_msg) kfree(hello_msg); } module_init(loading); module_exit(unloading); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); |
/* * tyler@agat.net * * Virtual Memory Module (intended to Linux Kernel 2.6(.12)) * * This module just explains the concept and the mechanism of the Linux * virtual memory system */ #include <linux/kernel.h> #include <linux/fs.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/vmalloc.h> unsigned long new_page ; unsigned long temp ; struct page *new_desc_page ; int init_module() { new_page = __get_free_page(GFP_KERNEL) ; new_desc_page = (struct page *)virt_to_page(new_page) ; /* * That's how the virtual memory system works. * To retrieve the virtual adress corresponding to the page, * we get the index in the page frame (new_desc_page-mem_map). * We then multiply it by the size of the page. We get the physical adress. * And in kernel mode, the differences between virtual and physical adresses * is just an offset of PAGE_OFFSET. So we add PAGE_OFFSET */ temp = (new_desc_page-mem_map)*PAGE_SIZE+PAGE_OFFSET ; printk("0x%x\n",temp) ; printk("0x%x\n",new_page) ; /* * In kernel mode, the virtual adresses has just an offset of PAGE_OFFSET * (the page tables are properly configured to do this) * On i386, the PAGE_OFFSET=0xC0000000 */ temp = __pa(new_page) ; printk("0x%x\n",new_page-PAGE_OFFSET) ; printk("0x%x\n",temp) ; return 0 ; } void cleanup_module() { free_page(new_page) ; }