Linux驱动开发之字符设备读操作

本文介绍了一个简单的字符设备驱动开发过程,包括驱动源代码、Makefile配置、应用程序编写及验证步骤。该驱动通过注册字符设备号并实现基本的读写操作。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值