Linux Kernel Driver 字符设备驱动 续

前文

hello 字符设备已经完成了open以及close的操作,但是

第一,不能读写

第二,并发不安全

读写

我们这是字符设备,但是是假的,没有硬件支持,所以,我们写进来的东西,还要能读出去, 我们最好还能简单处理一下,模拟一下硬件设备,我们用个全局的int来存储,模拟一下硬件的状态。

应用层通过系统调用  write / read 进行操作

内核层通过注册对应的 struct file_operations 的对应的op callback, read跟write

需要特别说明:

1. __user ,干嘛的?该部分的变量保存的是用户缓冲区的首地址,内核代码是不允许直接访问该地址指向的内存的,如果驱动想要用用户缓冲区中写入数据,需要利用内核提供的内存copy函数即可完成数据写入/读取操作。

2. read 的 loff_t * ppos 参数

记录上一次的读位置信息,代码操作如下:

首先获取上一次的读位置信息

loff_t pos = *ppos;

这次读了2字节,读完以后,要记得更新读位置

*ppos = pos + 2;

 此形参一般用于多次读!

3. write 的 loff_t * ppos 参数

保存上一次的写位置,操作如下:

首先获取上一次的写位置

loff_t pos = *ppos;

每一次写入12字节,更新写位置信息

*ppos = pos + 12

此参数适合多次写入操作

4. 其实open 与 release 接口,可以不用指定初始化,应用程序永远返回成功。

代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>       // struct file_operations
#include <linux/cdev.h>     // struct cdev
#include <linux/uaccess.h>


MODULE_DESCRIPTION("Frocheng: Driver for DEMO!");
MODULE_AUTHOR("Frodo Cheng");
MODULE_LICENSE("GPL");
MODULE_VERSION("V0.0.1");

static char* name = "hello";

static int state;

// 应用程序调用关系:open->软中断->sys_open->hello_open
static int hello_open(struct inode *inode,
                        struct file *file)
{
    printk("===[frocheng]===[%s]===[%s]===[%d]===\n",__FILE__, __func__, __LINE__);
    return 0; // 执行成功返回0,失败返回负值
}

// 应用程序调用关系:close->软中断->sys_close->hello_close
static int hello_close(struct inode *inode,
                        struct file *file)
{
    printk("===[frocheng]===[%s]===[%s]===[%d]===\n",__FILE__, __func__, __LINE__);
    return 0; //执行成功返回0,失败返回负值
}

// 应用read->软中断->sys_read->hello_read
static ssize_t hello_read(struct file *file,
                        char __user *buf,
                        size_t count,
                        loff_t *ppos)
{
    // 将内核缓冲区的数据拷贝到用户缓冲区
    copy_to_user(buf, &state, sizeof(state));
    return sizeof(state); // 返回实际读取的字节数
}

// 应用write->软中断->sys_write->hello_write
static ssize_t hello_write(struct file *file,
                        const char __user *buf,
                        size_t count,
                        loff_t *ppos)
{
    int m = 0;  // 内核区域的 对象
    copy_from_user(&m, buf, sizeof(m));
    // 模拟操作一下:
    // 如果写进来的是偶数,那么,内部状态 更新为 m * 2;
    // 如果写进来的是计数,那么,内部状态 更新为 m * 3;
    if ((m & 0x01) == 0x00)
    {
        state = m * 2;
    }
    else
    {
        state = m * 3;
    }
    
    return sizeof(m);
}



// 定义初始化LED的硬件操作对象
// open,release一旦加载内存中,静静等待着应用程序来调用
static struct file_operations hello_fops = {
    .owner = THIS_MODULE,
    .open = hello_open,     // 打开设备
    .release = hello_close,  // 关闭设备
    .write = hello_write,
    .read = hello_read
};

// 定义字符设备对象
static struct cdev hello_cdev;

// 定义设备号对象
static dev_t dev;

static int __init hello_init(void)
{
	int rc = -1;
    printk("===[frocheng]===[%s]===[%s]===[%d]===[Hello !]===\n",__FILE__, __func__, __LINE__);

    // 申请设备号
    rc = alloc_chrdev_region(&dev, 0, 1, name);
	if (rc != 0)
	{
    	printk("===[frocheng]===[%s]===[%s]===[alloc_chrdev_region error with %d]===\n",__FILE__, __func__, rc);
		// hello 驱动模块加载失败,因为并没有申请到字符神资源,驱动加载失败。
		return -1;
	}

    printk("===[frocheng]===[%s]===[%s]===[name=%s, major=%u, minor=%u]===\n",__FILE__, __func__, name, MAJOR(dev), MINOR(dev));
    
    // 初始化字符设备对象
    cdev_init(&hello_cdev, &hello_fops);

    // 注册字符设备对象
    cdev_add(&hello_cdev, dev, 1);
   
    return 0;
}

static void __exit hello_exit(void)
{
    // 卸载字符设备对象
    cdev_del(&hello_cdev);
    
    // 释放设备号
    unregister_chrdev_region(dev, 1);
    printk("===[frocheng]===[%s]===[%s]===[%d]===[Bye bye...]===\n",__FILE__, __func__, __LINE__);
}

module_init(hello_init);
module_exit(hello_exit);

测试代码

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
    int fd;

	// open->软中断->sys_open->hello_open
	fd = open("/dev/hello", O_RDWR);
	if (fd < 0)
	{
		perror("open");
		return -1;
	}

	int state = 0;
	read(fd, &state, sizeof(state));
	printf("Get initial state = %d\n", state);

	state = 3;
	printf("Set state = %d\n", state);
	write(fd, &state, sizeof(state));

	read(fd, &state, sizeof(state));
	printf("Get state = %d\n", state);

	state = 2;
	printf("Set state = %d\n", state);
	write(fd, &state, sizeof(state));

	read(fd, &state, sizeof(state));
	printf("Get state = %d\n", state);

	// close->软中断->sys_close->hello_close
	close(fd);
    return 0;
}

结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值