美文网首页
Linux 设备驱动之 hello 驱动

Linux 设备驱动之 hello 驱动

作者: wjundong | 来源:发表于2022-06-19 16:30 被阅读0次

Linux 设备驱动之 hello 驱动

首先编写一个简单的 hello 驱动, drv_hello.c

#include <linux/module.h>
#include <linux/tty.h>

static int major = 0;

static char kernel_buf[1024];

#define MIN(a, b) (((a) < (b)) ? (a) : (b))

int drv_hello_open(struct inode *node, struct file *file)
{
    return 0;
}

int drv_hello_close(struct inode *node, struct file *file)
{
    return 0;
}

/* 读操作 */
ssize_t drv_hello_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    int n;
    n = copy_to_user(buf, kernel_buf, MIN(1024, size));

    return n;
}

/**
 * 写操作
 * __user 只是一个空的宏, 表示 buf 来自用户空间, 你不可以访问
 * 
 * 驱动程序访问用户空间需要使用 copy_from_user 和 copy_to_user
 */
ssize_t drv_hello_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int n;
    n = copy_from_user(kernel_buf, buf, MIN(1024, size));
    
    return n;
}

/* 填充文件操作结构体 */
static struct file_operations drv_hello = {
    .owner   = THIS_MODULE,
    .open    = drv_hello_open,
    .read    = drv_hello_read,
    .write   = drv_hello_write,
    .release = drv_hello_close,
};


static int __init hello_init(void)
{
    /* 传入 0, 代表由操作系统自动分配主设备号 */
    major = register_chrdev(0, "hello", &drv_hello);

    /* 打印动态分配的主设备号 */
    printk("drv_hello major %d\n", major);

    return 0;
}

static void __exit hello_exit(void)
{
    unregister_chrdev(major, "hello");

    return ;
}

module_init(hello_init); 
module_exit(hello_exit);

MODULE_DESCRIPTION("The simple driver hello demo.");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shino");

编写应用层的测试程序
drv_hello_test.c

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int fd;
    char buf[1024];
    int len;

    if(argc < 2)
    {
        printf("Usage: %s -w <string>\n", argv[0]);
        printf("       %s -r \n", argv[0]);

        return -1;
    }

    fd = open("/dev/hello", O_RDWR);
    if(fd == -1)
    {
        printf("Can not open file /dev/hello\n");
        return -1;
    }

    if((0 == strcmp(argv[1], "-w")) && (argc == 3))
    {
        len = strlen(argv[2]) + 1;
        len = len < 1024 ? len : 1024;
        write(fd, argv[2], len);
    }
    else
    {
        len  = read(fd, buf, 1024);
        buf[1023] = '\0';
        printf("App read: %s\n", buf);
    }

    close(fd);

    return 0;
}

makefile 文件

# kernel 源码位置
KDIR ?= /lib/modules/`uname -r`/build

obj-m += drv_hello.o

all:
    make -C $(KDIR) M=$(PWD) modules
    cc drv_hello_test.c -o drv_hello_test -static

clean:
    make -C $(KDIR) M=$(PWD) clean
    rm drv_hello_test

运行测试

$ ls                                                                                                                                               
drv_hello.c  drv_hello_test.c  Makefile
$ make

$ insmod drv_hello.ko               # 装载驱动
$ lsmod | grep hello                # 可以看到驱动已经被加载到内核
drv_hello              16384  0

$ cat /proc/devices | grep hello    # 查看分配的主设备号
$ dmesg| grep hello                 # 查看打印的内核 log, 主设备号 510 和上面的一样
[ 2432.310686] drv_hello major 510

$ mknod /dev/hello c 510 1          # 根据主设备号创建设备节点
$ ./drv_hello_test -w HelloWorld
$ ./drv_hello_test -r 
App read: HelloWorld

$ rmmod drv_hello                   # 卸载驱动
$ cat /proc/devices                 # 驱动已经没有了
$ ls /dev/hello                     # 但是 mknod 创建的节点 /dev/hello 仍在
$ rm /dev/hello                     # 手动删除

装载驱动时自动创建设备节点

可以修改源代码,在驱动创建时直接创建设备节点, drv_hello.c 修改如下

#include <linux/module.h>
#include <linux/tty.h>

static int major = 0;

static char kernel_buf[1024];

#define MIN(a, b) (((a) < (b)) ? (a) : (b))

static struct class *hello_class;

int drv_hello_open(struct inode *node, struct file *file)
{
    return 0;
}

int drv_hello_close(struct inode *node, struct file *file)
{
    return 0;
}

/* 读操作 */
ssize_t drv_hello_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    int n;
    n = copy_to_user(buf, kernel_buf, MIN(1024, size));

    return n;
}

/**
 * 写操作
 * __user 只是一个空的宏, 表示 buf 来自用户空间, 你不可以访问
 * 
 * 驱动程序访问用户空间需要使用 copy_from_user 和 copy_to_user
 */
ssize_t drv_hello_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int n;
    n = copy_from_user(kernel_buf, buf, MIN(1024, size));
    
    return n;
}

/* 填充文件操作结构体 */
static struct file_operations drv_hello = {
    .owner   = THIS_MODULE,
    .open    = drv_hello_open,
    .read    = drv_hello_read,
    .write   = drv_hello_write,
    .release = drv_hello_close,
};


static int __init hello_init(void)
{
    /* 传入 0, 代表由操作系统自动分配主设备号 */
    major = register_chrdev(0, "hello", &drv_hello);

    /* 打印动态分配的主设备号 */
    printk("drv_hello major %d\n", major);

    /* 注册驱动后使用 class_create  device_create 创建设备节点 */
    hello_class = class_create(THIS_MODULE, "hello_class");
    device_create(hello_class, NULL, MKDEV(major, 0), NULL,"hello");

    return 0;
}

static void __exit hello_exit(void)
{
    /* 销毁设备节点 */
    device_destroy(hello_class, MKDEV(major, 0));
    class_destroy(hello_class);

    unregister_chrdev(major, "hello");

    return ;
}

module_init(hello_init); 
module_exit(hello_exit);

MODULE_DESCRIPTION("The simple driver hello demo.");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shino");

运行测试

$ insmod drv_hello.ko
$ ls /dev/hello             # 直接有设备节点, 不需要 mknod 了
$ ./drv_hello_test -w HelloWorld
$ ./drv_hello_test -r 
App read: HelloWorld

$ rmmod drv_hello
$ ls /dev/hello             # 设备节点自动销毁了

相关文章

网友评论

      本文标题:Linux 设备驱动之 hello 驱动

      本文链接:https://www.haomeiwen.com/subject/qumrvrtx.html