linux字符设备驱动开发总体框架(pinctrl和gpio子系统)


linux设备驱动开发总体框架

以使用pinctrlgpio子系统来开发的GPIO驱动来描述本节.再具体就是点灯,去他妈点了一个月的灯
相对于32裸机,完成GPIO驱动需要先设置某个PIN的复用功能,速度和上下拉,再设置PIN所对应的GPIO。而linux驱动讲究驱动分离和分层,所以就针对PIN的配置推出了pinctrl子系统,对于GPIO的配置推出了gpio子系统

pinctrl子系统的源码路径为 drivers/pinctrl
pinctrl子系统的功能:

  1. 获取设备树中pin信息
  2. 根据获取到的pin信息来设置pin的复用功能
  3. 根据获取到的pin信息来设置pin的电气属性(上下拉,速度,驱动能力等)

gpio子系统目的是方便驱动开发者使用gpio:

  1. 在设备树中添加gpio相关信息
  2. 在驱动程序中使用gpio子系统提供的API函数来操作GPIO

1.修改设备树

要使用 pinctrl 子系统,我们需要在设备树里面设置 PIN 的配置信息,毕竟 pinctrl 子系统要根据你提供的信息来配置 PIN 功能,一般会在设备树里面创建一个节点来描述 PIN 的配置信息

  • 添加pinctrl节点
    I.MX6ULL的IOMUXC外设(核心功能是IO口复用)对应的节点就是iomuxc,在imx6ull.dtsi和imx6ull-alientek-emmc.dts中有。不同的外设如SPI接口和IIC接口等,使用的PIN不同,配置也不同,因此需要将某个外设使用的所有PIN都集合到一个子节点中,以group为单位,访问、控制多个pin,这就是pin groups,所以一般命名会加上grp
    本节要在 iomuxc 中添加我们自定义外设的 PIN,那么需要在iomuxc下新建一个子节点,然后将这个自定义外设的所有 PIN 配置信息都放到这个子节点中。这里只有一个
pinctrl_led: ledgrp{ 
	fsl,pins = < 
	MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */
	>;
};

将 GPIO1_IO03 这个 PIN 复用为 GPIO1_IO03,电气属性值为 0X10B0

申请IO的时候失败,大部分原因是这个IO被其他外设占用了,小部分原因是检查pinctrl设置。检查设备树,查找有哪些使用同一IO的设备,然后注释
  • 添加设备节点 LED
    在根节点下创建LED节点,然后设置对应的pinctrl节点为上面添加的子节点。设置属性状态名字等
gpioled {
	#address-cells = <1>;
	#size-cells = <1>;
	compatible = "atkalpha-gpioled";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_led>;
	led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
	status = "okay"; 
}

注:改到这里可以make dtbs之后将.dtb放入tftp中,网络启动,去/proc/device-tree中查看是否有节点

2.驱动出口驱动入口

2.1 入口:

注册字符设备驱动

1、创建设备号

register_chrdev_region//给定设备号
alloc_chrdev_region//动态分配设备号

2、初始化并添加一个cdev

cdev_init
cdev_add

3、创建类和创建设备
这个目的是自动创建设备节点

class_create
device_create

内核中定义了struct class结构体,内核同时提供了class_create()函数,可以用它来创建一个类(本质类型是结构体),创建好这个类,再调用device_create()函数来在/dev目录下创建相应的设备节点。
这样,加载模块的时候,用户空间(因为原子使用的Busybox创建的根文件系统使用了udev的简化版本mdev)中的mdev会自动响应device_create()函数,去寻找对应的类从而创建设备节点

设置LED所使用的GPIO

1、获取设备节点:gpioled

of_find_node_by_path

2、 获取设备树中的gpio属性,得到LED所使用的LED编号

of_get_named_gpio

3、 申请IO
不申请也能用,但是无法被检测IO是否被使用,所以最好还是申请一下
4、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯

gpio_direction_output

2.2 出口:

注销字符设备驱动,释放申请的设备号等

unregister_chrdev_region//设备号释放函数
device_destroy //删除设备
class_destroy //删除类

3.定义设备结构体

保存设备基本信息的结构体,一般包括

/* gpioled设备结构体 */
struct gpioled_dev{
	dev_t devid;			/* 设备号 dev_t是一个数据类型,unsigned int	 */
	struct cdev cdev;		/* cdev 描述字符设备	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	struct device_node	*nd; /* 设备节点 */
	int led_gpio;			/* led所使用的GPIO编号		*/
};

然后声明一个变量作为驱动

struct gpioled_dev gpioled;	/* led设备 */

4.实现设备操作函数,

linux内核为设备建立一个设备文件,这样就使得对设备文件的所有操作,就相当于对设备的操作.其实也就是应用程序
file_operations 结构体就是设备的具体操作函数,原型路径·include\linux\fs.h
步骤:

  1. 定义file_operations结构体类型的变量gpioled_fops
  2. 分析设备需求,就是需要对设备进行那些操作(打开,关闭,读写)
  3. 确定操作后,实现操作函数
static struct file_operations gpioled_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};

/* 打开设备 */
static int chrtest_open(struct inode *inode, struct file *filp)
{	/* 用户实现具体功能 */
	return 0;
}
/* 从设备读取 */
static ssize_t chrtest_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{
	return 0;
}
/* 向设备写数据 */
static ssize_t chrtest_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt)
{
	return 0;
}
/* 释放设备 */
static int chrtest_release(struct inode *inode, struct file *filp)
{
	return 0;
}

5.编写设备测试APP

编写测试 APP 就是编写 Linux 应用,运行在用户空间,需要用到 C 库里面和文件操作有关的一些函数,比如open、read、write 和 close 这四个函数。

5.1编写

5.2编译

因为是运行在ARM开发板上,所以使用arm版本的编译器

arm-linux-gnueabihf-gcc ledApp.c -o LEDAPP

5.3运行

./LEDAPP /dev/gpioled 1

6.其他

最后我们需要在驱动中加入 LICENSE 信息和作者信息,其中 LICENSE 是必须添加的,否
则的话编译的时候会报错,作者信息可以添加也可以不添加

MODULE_LICENSE("GPL") //添加LICENSE信息,GPL等
MODULE_AUTHOR("chengx") //添加作者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值