方法一:字符设备驱动程序步骤和应用程序验证(简单总结)

字符设备驱动程序步骤

使用register_chrdev 函数注册字符设备

使用 unregister_chrdev 函数注销字符设备

1. 定义设备号

详细步骤

  • 选择设备号:确定一个未被使用的主设备号。这通常是一个小于256的正整数。你也可以选择让内核动态分配一个主设备号。
  • 定义设备名:确定一个未被使用的设备名称,尽量不要重复名称,要有所区分。

分配设备号:在驱动程序的初始化函数中,使用alloc_chrdev_region函数动态分配设备号,或者使用register_chrdev函数静态注册设备号。

设备号的数据类型是unsigned int,32位,设备号分为 主设备号次设备号两部分,其中12 位为主设备号,20 位为次设备号。因此 Linux 系统中主设备号范围为 0~4095

相关函数

1.#define MINORBITS 20

2.#define MINORMASK ((1U << MINORBITS) - 1)

3.#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))

4.#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))

5.#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

第 1行,宏 MINORBITS 表示次设备号位数,一共是 20 位。

第 2 行,宏 MINORMASK 表示次设备号掩码。

第 3 行,宏 MAJOR 用于从 dev_t 中获取主设备号,将 dev_t 右移 20 位即可。

第 4 行,宏 MINOR 用于从 dev_t 中获取次设备号,取 dev_t 的低 20 位的值即可。

第 5 行,宏 MKDEV 用于将给定的主设备号和次设备号的值组合成 dev_t 类型的设备号。

查看设备号命令

cat /proc/devices

2. 编写设备相关操作函数 和 file_operations构体赋值

详细步骤

  • 实现操作函数:为file_operations结构体中的每个函数指针提供实现。这些函数包括open、read、write、close等,用于处理对设备的文件操作。
  • 定义file_operations结构体实例:在你的驱动程序中定义一个file_operations结构体的实例。
  • 初始化file_operations结构体:在驱动程序的初始化函数中,初始化file_operations结构体,将你的操作函数与对应的函数指针关联起来。

3. 注册和注销设备

详细步骤

  • 注册设备:在驱动程序的初始化函数中,使用register_chrdev或cdev_init和cdev_add函数将你的设备注册到内核中。这将使内核能够识别和管理你的设备。
  • 处理错误:在注册设备时,检查返回值以确保设备已成功注册。如果注册失败,应该清理已分配的资源并返回错误。
  • 注销设备:在驱动程序的卸载函数中,使用unregister_chrdev或cdev_del函数注销你的设备。这将使内核停止管理你的设备,并释放相关资源

应用程序

        驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件,应用程序就是通过操作这个设备节点文件来完成对具体设备的操作。输入如下命令创建/dev/chrdevbase 这个设备节点文件:

mknod /dev/chrdevbase c 108 0

        其中“mknod”是创建节点命令,

        “/dev/chrdevbase”是要创建的节点文件,

        “c”表示这是个字符设备,

        “108”是设备的主设备号,

        “0”是设备的次设备号。

代码展示

1.驱动代码

/*************************************************************************
#	> File Name: chardevbase.c
#	> Author: HENG-W
#	> Mail: 
#	> Created Time: 2024年08月29日 星期四 11时57分41秒
#	> Describe: 练习驱动代码的框架
#*************************************************************************/
/*头文件*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>

/*第一步、设备号,设备名称*/
#define CHARDEVBASE_MAJOR  108 				/*字符设备主设备号*/
#define CHAREDVBASE_NAME "MyCharDev01"		/*设备名称*/

/*第二步、设备相关操作函数 和 file_operations构体赋值*/
static int MyCharDev_open(struct inode *inodep, struct file *filep)
{
	printk("MyCharDev_open!\r\n");
	return 0;
}
static ssize_t MyCharDev_read(struct file *filep, char __user *buf, size_t cnt, loff_t *offt)
{
	printk("MyCharDev_read!\r\n");
	return 0;
}
static ssize_t MyCharDev_write (struct file *filep, const char __user *buf, size_t cnt, loff_t *offt)
{
	printk("MyCharDev_write!\r\n");
	return 0;
}
static int MyCharDev_release(struct inode *inodep, struct file *filep)
{
	printk("MyCharDev_release!\r\n");
	return 0;
}
/*
* 设备操作函数结构体,结构体的相关值按照需求自己定义
*/
struct file_operations  Mychrdev_fops = {
	.owner = THIS_MODULE,					/*指向拥有这个结构的模块的指针,主要用于模块的支持和卸载*/
	.open  	= MyCharDev_open,
	.read  	= MyCharDev_read,
	.write 	= MyCharDev_write,
	.release=MyCharDev_release,
};

/*第三步、再驱动入口函数中注册字符设备*/
/*驱动入口函数*/
static int __init chardevbase_init(void)
{
	int retvalue  = 0;
	/* 入口函数具体内容 */
	printk("chardevbase_init 1111!!!\n");
	/*注册字符设备*/
	retvalue = register_chrdev(CHARDEVBASE_MAJOR, CHAREDVBASE_NAME,&Mychrdev_fops);
	if (retvalue < 0)
	{
		/* code */
		printk("register_chrdev fail!!!\n");
		return retvalue; 
	}
	
	return 0;
}
/*第四步、再驱动卸载函数中注销字符设备*/
/* 驱动卸载函数 */
static void __exit chardevbase_exit(void)
{ 
	printk("chardevbase_exit is end!!!\n");
	/*注销字符设备*/
	unregister_chrdev(CHARDEVBASE_MAJOR, CHAREDVBASE_NAME);
}
/*第五步、声明相关信息*/
/* 将上面两个函数指定为驱动的加载和卸载函数 */
module_init(chardevbase_init);
module_exit(chardevbase_exit);
MODULE_LICENSE("GPL");	//添加模块 LICENSE 信息
MODULE_AUTHOR("HENG-W"); //添加模块作者信息

2.应用程序

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

#define DEVICE_NAME "/dev/MyCharDev01"  // 替换为您的设备文件名

int main() {
    int fd;
    char read_buf[100];
    const char *write_buf = "Hello Kernel!";

    // 打开设备文件
    fd = open(DEVICE_NAME, O_RDWR);
    if (fd < 0) {
        perror("Failed to open the device");
        return -1;
    }

    // 写入数据到设备文件
    write(fd, write_buf, strlen(write_buf));

    // 读取设备文件中的数据
    read(fd, read_buf, sizeof(read_buf));

    // 关闭设备文件
    close(fd);

    return 0;
}

3.效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值