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 # 设备节点自动销毁了
网友评论