2016年3月3日 星期四

[Kernel][Linux] How to write kenel driver

Template of Kernel Code
/* example.c */
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int example_init(void) {
    printk("<1>EXAMPLE: init\n");
    return 0;
}

static void example_exit(void) {
    printk("<1>EXAMPLE: exit\n");
}

module_init(example_init);
module_exit(example_exit);


Makefile
obj-m := example.o

ifeq ($(KERNELDIR),)
KERNELDIR=/lib/modules/$(shell uname -r)/build
endif

all:
    make -C $(KERNELDIR) M=$(PWD) modules

clean:
    make -C $(KERNELDIR) M=$(PWD) clean

$ sudo insmod ./example.ko
$ sudo rmmod example


Use following comamnd to show any message from our kernel.
$ sudo dmesg | tail






Register as Character Device
Add following code into example.c.
#include <linux/fs.h>


static int example_open(struct inode *inode, struct file *filp) {
    printk("<1>EXAMPLE: open\n");
    return 0;
}

static int example_close(struct inode *inode, struct file *filp) {
    printk("<1>EXAMPLE: close\n");
    return 0;
}

static ssize_t example_read(struct file *filp, char *buf, size_t size, loff_t *f_pos) {
    printk("<1>EXAMPLE: read  (size=%zu)\n", size);
    return 0;
}

static ssize_t example_write(struct file *filp, const char *buf, size_t size, loff_t *f_pos) {
    printk("<1>EXAMPLE: write  (size=%zu)\n", size);
    return size;
}

static struct file_operations example_fops = {
    .open = example_open,
    .release = example_close,
    .read = example_read,
    .write = example_write,
};
Change example_init
#define EXAMPLE_MAJOR 60
#define EXAMPLE_NAME "example"

static int example_init(void) {
    int result;
    printk("<1>EXAMPLE: init\n");

    /* Register character device */
    result = register_chrdev(EXAMPLE_MAJOR, EXAMPLE_NAME, &example_fops);
    if (result < 0) {
        printk("<1>EXAMPLE: Failed to register character device\n");
        return result;
    }

    return 0;
}
Change example_exit
static void example_exit(void) {
    printk("<1>EXAMPLE: exit\n");

    /* Unregister character device */
    unregister_chrdev(EXAMPLE_MAJOR, EXAMPLE_NAME);
}
Compile code (example.c)


Remove the example from kernel
$ sudo rmmod example


Insert the kernel
$ sudo insmod ./example.ko


Create a node under /dev
$ sudo mknod /dev/example c 60 0
# /dev/example (the path of file),c represent Character Device,60 (Major ID),0 (Minor ID)。

$ sudo chmod 666 /dev/example (permission - all can read and write)


Write message to /dev/example
$ echo -n 'abcd' > /dev/example


Show message
$ sudo dmesg | tail


Read from user space.
Kernel space and user space have different storage address, so can’t load them directly.
Must use the API of copy_from_user() to read from user space.


Add follow code to modify example.c
#include <asm/uaccess.h>

ssize_t example_write(struct file *filp, const char *buf, size_t size, loff_t *f_pos) {
    size_t pos;
    uint8_t byte;
    printk("<1>EXAMPLE: write  (size=%zu)\n", size);
    for (pos = 0; pos < size; ++pos) {
        if (copy_from_user(&byte, buf + pos, 1) != 0) {
            break;
        }
        printk("<1>EXAMPLE: write  (buf[%zu] = %02x)\n", pos, (unsigned)byte);
    }
    return pos;
}


If data want copy from kernel space to user space must use copy_to_user().


Remove the example from kernel
$ sudo rmmod example


Insert the kernel
$ sudo insmod ./example.ko


Show message
$ sudo dmesg | tail

Reference:

0 意見:

張貼留言