学无先后,达者为师

网站首页 编程语言 正文

编写字符设备驱动控制树莓派io口

作者:weixin_42963900 更新时间: 2022-10-29 编程语言

字符设备驱动

  • 1.注册字符类设备号
    • 1.静态分配一个设备号
    • 2.动态分配一个设备号
    • 3.注销设备号
  • 2.注册字符类设备
    • 1.定义一个cdev结构体
    • 2. 使用cdev_init函数初始化cdev结构体成员变量
    • 3.使用cdev_add函数注册到内核
  • 3.创建设备节点
    • 1.方法一
    • 2.方法二
      • 1.使用class_create函数创建一个class类
      • 2.使用device_create函数在我们创建的类下面创建一个设备
  • 4.chrdev.c
  • 5 app.c
  • 6 编译运行
    • 1.编译chrdev_io.c
    • 2.编译app.c
  • 7 在树莓派运行

1.注册字符类设备号

1.静态分配一个设备号

int register_chrdev_region(dev_t, unsigned, const char *);

参数:
第一个:设备号的起始值。类型是dev_t类型
第二个:次设备号的个数
第三个:设备的名称 (使用cat /proc/devices 输出的就是设备号,设备的名称)
返回值:成功返回0,失败返回负数

dev_t类型:
dev_t是用来保存设备号的,是一个32位数。
高12位用来保存主设备号,低20位用来保存次设备号。

操作dev的函数
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
根据dev_t获取主设备号
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
根据dev_t获取次设备号
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
将我们的主设备号和次设备号组成一个dev_t类型,第一个参数是主设备号,第二个参数是次设备号。

2.动态分配一个设备号

int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);

参数:
第一个:保存生成的设备号
第二个:我们请求的第一个设备号,通常是0
第三个:连续申请的设备号的个数。
第四个:设备的名称
返回值:成功返回0 ,失败返回负数。

使用动态分配优先使用234 --255的设备号。

3.注销设备号

void unregister_chrdev_region(dev_t, unsigned);

参数:
第一个:分配设备号的起始地址
第二个: 申请的连续设备号的个数。

2.注册字符类设备

1.定义一个cdev结构体

2. 使用cdev_init函数初始化cdev结构体成员变量

void cdev_init(struct cdev *, const struct file_operations *);

参数:
第一个:要初始化的cdev
第二个:文件操作集
cdev->ops = fops;//实际就是把文件操作集写给ops

3.使用cdev_add函数注册到内核

int cdev_add(struct cdev *, dev_t, unsigned);
参数:
第一个:cdev的结构体指针
第二个:设备号
第三个:次设备号的数量

3.创建设备节点

1.方法一

使用mknod命令创建一个设备节点
格式:
mknod 名称 类型 主设备号 次设备号

2.方法二

1.使用class_create函数创建一个class类

2.使用device_create函数在我们创建的类下面创建一个设备

4.chrdev.c

在/home/kun/build_new/linux_kernel/drivers/char/(这为我内核目录下的字符设备路径)目录下 mkdir 07_chrdev_io ,编写chrdev.c app.c Makefile。

#include <linux/init.h>    //初始化头文件
#include <linux/module.h>  //最基本的文件,支持动态添加和卸载模块
#include <linux/moduleparam.h> //驱动传参头文件
#include <linux/fs.h>             //文件操作相关的struct的定义
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>

#define DEVICE_NUMBER 1            //次设备号个数
#define DEVICE_SNAME   "schrdev"   //静态注册设备的名字
#define DEVICE_ANANME  "achrdev"   //动态注册设备的名字
#define DEVICE_MINOR 0             //次设备号起始地址

#define DEVICE_CLASS_NAME "chrdev_class"
#define DEVICE_NODE_NAME  "chrdev_test"

#define GPIO4_DR 0xfe200000
#define GPIO4_H  0xfe20001c
#define GPIO4_L  0xfe200028


unsigned int *vir_gpio4_dr=NULL;
unsigned int *vir_gpio4_h=NULL;
unsigned int *vir_gpio4_l=NULL;

static int major_num, minor_num;

struct cdev cdev;

static dev_t  dev_num;

struct class *class;
struct device *device;

int chrdev_open( struct inode * inode, struct file *file)
{
	printk( "hello chrdev_open \n");
	
	return 0;
}


ssize_t chrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
	char kbuf[64] = {0};
	if (copy_from_user(kbuf, ubuf, size) != 0)
	{
		printk("copy_from_user error\n ");
		return -1;
	}
	printk("kbuf is %s\n ", kbuf);
	*vir_gpio4_dr |=(001<<(3*4));
	if(kbuf[0]==1)
	{
		
		*vir_gpio4_h |=(1<<4);
	}
	else if(kbuf[0]==0)
	{
		*vir_gpio4_l |=(1<<4);
	}
	return 0;
}


struct file_operations chrdev_ops=
{
	.owner = THIS_MODULE,
	.open = chrdev_open,
	.write = chrdev_write,
};

module_param( major_num, int, S_IRUGO);
module_param( minor_num, int, S_IRUGO);

MODULE_PARM_DESC( major_num, "e.g:a=1"); 
MODULE_PARM_DESC( minor_num, "e.g:a=1"); 


static int 
chrdev_init( void)
{

	int ret;

	if( major_num) //静态注册
	{
		printk( "mjor_num=%d \n", major_num);
		printk( "minor_num=%d \n", minor_num);
		dev_num = MKDEV( major_num, minor_num);
		ret = register_chrdev_region( dev_num, DEVICE_NUMBER, DEVICE_SNAME);

		if( ret<0)
		{
			printk( "register_chrdev_region error\n");
		}
		
		printk( "register_chrdev_region success\n");
	}
	else   //动态注册
	{
		ret = alloc_chrdev_region( &dev_num, DEVICE_MINOR, DEVICE_NUMBER, DEVICE_ANANME);
		if( ret<0)
		{
			printk( "alloc_chrdev_region error\n");
		}
		printk( "alloc_chrdev_region success\n");

		major_num = MAJOR( dev_num);
		minor_num = MINOR( dev_num);
		
		printk( "mjor_num=%d \n", major_num);
		printk( "minor_num=%d \n", minor_num);
	}
	
	//初始化cdev
	cdev.owner = THIS_MODULE;
	
	cdev_init( &cdev, &chrdev_ops);
	//向系统注册设备
	cdev_add( &cdev, dev_num, DEVICE_NUMBER);
	class = class_create( THIS_MODULE, DEVICE_CLASS_NAME);
	device = device_create( class,NULL,dev_num,NULL,DEVICE_NODE_NAME);

	//对物理地址进行虚拟映射
	vir_gpio4_dr = ioremap( 0xfe200000,4);
	if(vir_gpio4_dr == NULL )
	{
		printk( "gpio4dr ioremap error\n");
		return EBUSY;
	}
	
	vir_gpio4_h = ioremap( GPIO4_H,4);
	if( vir_gpio4_h == NULL)
	{
		printk( "gpio4h ioremap error\n");
		return EBUSY;
	}

	vir_gpio4_l = ioremap( GPIO4_L,4);
	if(vir_gpio4_l == NULL)
	{
		printk( "gpio4l ioremap error\n");
		return EBUSY;
	}
	printk( "gpio ioremap success\n");
	return 0;
}

static void 
chrdev_exit( void)
{

	unregister_chrdev_region( MKDEV( major_num, minor_num), DEVICE_NUMBER);

	cdev_del( &cdev);

	device_destroy( class,dev_num);

	class_destroy( class);
	
	printk( "bye bye \n");
}

module_init( chrdev_init);
module_exit( chrdev_exit);

MODULE_LICENSE( "GPL");

5 app.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
	int fd;
	
	char buf[64] = "0";
	
	fd = open( "/dev/chrdev_test",O_RDWR);//打开设备节点
	
	if(fd < 0)
	{
		perror( "open error \n");
		return fd;
	}

	buf[0]= atoi( argv[1]);
	
	write( fd,buf,sizeof(buf)); //向内核层写数据
	
	close( fd);
	
	return 0;
}

6 编译运行

1.编译chrdev_io.c

Makefile文件内容

obj-m +=chrdev_io.o
KDIR:=/home/kun/build_new/linux_kernel
PWD?=$(shell pwd)
all:
	make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) M=$(PWD) modules
clean:
	rm *.mod.c *.order *.ko *.o *.mod *.symvers

KDIR:为自己在linux上存放的内核目录路径
执行make,生成.ko文件

2.编译app.c

arm-linux-gnueabihf-gcc app.c -o app -static

7 在树莓派运行

将步骤6生成的chrdev.ko app移动至树莓派
执行 sudo insmod chrdev.ko即可加载驱动
执行 ./app 1 即可使GPIO4引脚输出高电平
执行 ./app 0即可使GPIO4引脚输出低电平
在这里插入图片描述
在这里插入图片描述

原文链接:https://blog.csdn.net/weixin_42963900/article/details/127536968

栏目分类
最近更新