GPIO实验(2)——基于rk3308b-cc-plus开发板

Linux内核驱动编程:字符设备与GPIO控制LED
本文介绍了如何在Linux内核中实现字符设备驱动,包括设备号的分配、cdev结构的使用以及驱动程序的主要函数如open和read的实现。还涉及到了GPIO控制LED灯的驱动程序编写,包括dts文件的描述和驱动代码,以及用户空间的应用程序如何通过读写设备文件来控制LED的开关状态。

本实验目的为用户可以通过应用层程序输入on/off来控制led灯的亮灭。

1.前置知识——简单字符设备的实现

1.1 设备号

1.1.1 主设备号与次设备号

对字符设备的访问是通过文件系统内的设备名称进行的。通常而言,主设备号标识设备对应的驱动程序。次设备号则由内核使用,用于正确确定设备文件所指的设备。你可以通过在/dev目录下利用ls -l查看设备的主次设备号。

dev_t类型用来保存设备编号。它是一个32位的数,其中12位标识主设备号,20位标识次设备号。

MAJOR(dev_t dev);//获取主设备号
MINOR(dev_t dec);//获取次设备号
MKDEV(int major, int minor);//由主次设备号得到设备号

1.1.2 分配和释放设备编号

在建立一个字符设备之前,我们的驱动程序首先要做的事情就是获得一个或者多个设备编号。

int register_chrdev_region(dev_t first, unsigned int count, char *name);//明确知道设备号选它
int alloc_chrdev_region(dev_t dev, unsigned int firstminor, unsigned count, char *name);//不知道就选择它,由内核自动分配主设备号
/*firstminor是要使用的被请求的第一个次设备号一般为0,count是所请求的连续设备编号的个数,name则是设备名称,你会在用户层的应用函数中使用到它*/

1.2 重要数据结构

1.2.1 file_operation

用于建立设备编号与驱动程序操作的连接。被初始化如下。

struct file_operations chr_ops = {
   
   
	.owner = THIS_MODULE,
	.open = chr_open,
	.read = chr_read
};//将其中对应的操作绑定到对应函数上

1.2.2 file

代表了一个打开的文件。它由内核在open时创建,并且传递给在该文件上进行操作的所有函数,直到close函数。

1.2.3 inode

内核用inode结构在内部表示文件。对于一个文件来说,可能会有多个表示打开的文件描述符的file结构,但是它们都会指向一个inode结构。该结构中对编写驱动程序代码有用的两个字段如下。

dev_t i_rdev; //该字段包含了真正的设备编号
struct cdev *i_cdev;//struct cdev表示字符设备的内核的内部结构。

1.3 字符设备的注册

当用户想要在运行时获取一个独立的cdev结构,应该如此编写代码

/*首先定义出struct cdev结构体,file_operation结构与设备编号*/
static struct cdev chr_dev;//内核内部使用cdev结构来表示字符设备,内核调用设备前,需要先注册该结构
struct file_operations chr_ops = {
   
   
	.owner = THIS_MODULE,
	.open = chr_open,
	.read = chr_read
};
static dev_t ndev;//用来保存设备编号,MAJOR获得主设备号,MINOR获得次设备号

/*然后利用cdev_init函数完成对cdev结构成员的初始化,建立cdev与file_operation的联系*/
cdev_init(&chr_dev, &chr_ops);

/*结构设置好后,利用下面的调用告诉内核该结构的信息*/
alloc_chrdev_region(&ndev, 0, 1, "chr_dev");//动态申请设备号
cdev_add(&chr_dev, ndev, 1);

cdev_add函数可能会失败,一定要对返回值进行检查。

1.4 简单字符设备实现(代码)

open和read函数没有实质内容,可以自己随便写

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/init.h>

//执行顺序:demo_init

static struct cdev chr_dev;
static dev_t ndev;//用来保存设备编号,MAJOR获得主设备号,MINOR获得次设备号

static int chr_open(struct inode *nd, struct file *filp)
{
   
   
	int major;
	int minor;

	major = MAJOR(nd->i_rdev); //inode中的i_rdev包含了真正的设备编号
	minor = MINOR(nd->i_rdev);

	printk("chr_open, major = %d, minor = %d\n", major, minor);
	return 0;
}

static ssize_t chr_read(struct file *filp, char __user *u, size_t sz, loff_t *of)
{
   
   
	printk("chr_read process!\n");
	return 0;
}

struct file_operations chr_ops = {
   
   
	.owner = THIS_MODULE,
	.open = chr_open,
	.read = chr_read
};//将其中对应的操作绑定到对应函数上

static int demo_init(void)
{
   
   
	int ret;

	cdev_init(&chr_dev, &chr_ops);//静态内存定义初始化
	ret = alloc_chrdev_region(
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值