1、驱动源代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <asm/uaccess.h>
static int hello_major = 248; // 主设备号
static int hello_minor = 0; // 次设备号
static int number_of_devices = 1; // 字符设备个数
char data[50] = "I come from Liux Kernel!";
struct cdev cdev; // 静态定义字符设备
dev_t dev = 0; // 设备号
static int hello_open(struct inode *inode, struct file *file)
{
printk (KERN_INFO "Hi! device opened\n");
return 0;
}
static int hello_release(struct inode *inode, struct file *file)
{
printk (KERN_INFO "Hi! device closed\n");
return 0;
}
ssize_t
hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
{
ssize_t result = 0;
if(count > 50) count = 50;
if(count < 0) return -EINVAL;
if(copy_to_user(buff, data, count)) //把内核空间中的数据拷贝到用户空间
result = -EFAULT;
else
printk(KERN_INFO "read %d bytes\n", count);
result = count;
return result; // 返回实际读取到的字节数
}
ssize_t
hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
ssize_t ret = 0;
printk(KERN_INFO "Writing %d bytes\n", count);
if (count > 50)
return -ENOMEM;
if (count < 0)
return -EINVAL;
if (copy_from_user(data, buf, count)){ // 把用户空间中的数据拷贝到内核空间
ret = -EFAULT;
}else {
data[49] = '\0';
printk(KERN_INFO "Received: %s\n", data);
ret = count;
}
return ret;
}
struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.write = hello_write,
.read = hello_read ,
static void char_reg_setup_cdev (void)
{
int error, devno = MKDEV(hello_major, hello_minor); // 合并主、次设备号为设备号
cdev_init(&cdev, &hello_fops); // 初始化字符设备
cdev.owner = THIS_MODULE;
//cdev.ops = &hello_fops;
error = cdev_add(&cdev, devno, 1); // 注册字符设备
if (error)
printk (KERN_ERR "Error %d adding char_reg_setup_cdev", error);
}
static int __init hello_init(void)
{
int result;
dev = MKDEV (hello_major, hello_minor);// 合并主、次设备号为设备号
result = register_chrdev_region (dev, number_of_devices, "hello"); // 静态注册设备号 name 体现在"/proc/devices"中
if (result < 0) {
printk(KERN_ERR "hello: can't get major number %d\n", hello_major);
return result;
}
char_reg_setup_cdev ();
printk (KERN_INFO "Write driver registered\n");
return 0;
}
static void __exit hello_exit (void)
{
dev_t devno = MKDEV (hello_major, hello_minor);// 合并主、次设备号为设备号
cdev_del(&cdev); // 注销字符设备
unregister_chrdev_region(devno, number_of_devices); // 注销设备号
printk (KERN_INFO "Write driver exit\n");
}
module_init (hello_init);
module_exit (hello_exit);
MODULE_LICENSE("GPL");
MODULE_VERSION("v1.0");
MODULE_AUTHOR("xz@vichip.com.cn");
MODULE_DESCRIPTION("Char Driver Module");
MODULE_ALIAS("char driver module");
2、Makefile
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
else
obj-m := read_dev.o
endif
3、应用程序
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
int main (void)
{
int fd;
char buff[50] = "hello world!!!!";
fd = open("/dev/hello",O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
printf("/dev/hello opened, fd=%d\n",fd);
printf("Write returns %d\n", write(fd,buff,sizeof(buff) - 1));
memset(buff, 0, 50);
printf ("Read returns %d\n", read(fd, buff, sizeof(buff) - 1));
buff[49] = '0';
printf("buff = %s\n", buff);
close(fd);
printf("/dev/hello closed\n");
return 0;
}
4、验证
1)sudo insmod read_dev.ko
2)手动创建设备文件
sudo mknod /dev/hello c 248 0
chmod 0666 /dev/hello
3)编译应用程序
gcc test.c -o test
执行应用程序 ./test
4)查看内核消息
dmesg
本文介绍了一个简单的字符设备驱动开发过程,包括驱动源代码、Makefile配置、应用程序编写及验证步骤。该驱动通过注册字符设备号并实现基本的读写操作。
1871

被折叠的 条评论
为什么被折叠?



