pinctrl子系统,gpioctrl子系统

参考文档

内核 Documentation/devicetree/bindings/Pinctrl/Pinctrl-bindings.txt
内核 Documentation/gpio/Pinctrl-bindings.txt
内核 Documentation/devicetree/bindings/gpio/gpio.txt

如何理解Pinctrl子系统?

Pinctrl子系统,顾名思义,就是管理pin引脚的一个系统,比如要点亮LED,即要控制LED对应引脚的高低电平,就要先通过Pintrl子系统将LED对应的引脚复用为GPIO功能 , 如此便不需要操作繁琐寄存器 , Pinctrl子系统依赖于设备树来使用 , 无论是哪种芯片,都有类似下图的结构: 在这里插入图片描述
要想让pinA、B用于GPIO,需要设置IOMUX让它们连接到GPIO模块;
要想让pinA、B用于I2C,需要设置IOMUX让它们连接到I2C模块。

使用Pinctrl子系统

这会涉及2个对象:pin controller、client device。
前者提供服务:可以用它来复用引脚、配置引脚。
后者使用服务:声明自己要使用哪些引脚的哪些功能,怎么配置它们。

pin controller:
在芯片手册里你找不到pin controller,它是一个软件上的概念,你可以认为它对应IOMUX──用来复用引脚,还可以配置引脚(比如上下拉电阻等)。

b. client device
“客户设备”,谁的客户?Pinctrl系统的客户,那就是使用Pinctrl系统的设备,使用引脚的设备。它在设备树里会被定义为一个节点,在节点里声明要用哪些引脚。
在这里插入图片描述

举例分析 pinctrl子系统
pinctrl用于引脚得复用, 在设备树中找到 iomuxc节点
以imx6ull为例:
imx6ull.dtsi中找到:

 iomuxc: iomuxc@020e0000 { 
             compatible = "fsl,imx6ul-iomuxc"; 
             reg = <0x020e0000 0x4000>; 
             };

imx6ull.dts中对其进行补充:

311 &iomuxc { 
312     pinctrl-names = "default"; 
313     pinctrl-0 = <&pinctrl_hog_1>; 
314     imx6ul-evk { 
315         pinctrl_hog_1: hoggrp-1 { 
316             fsl,pins = < 
317                 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19    0x17059  
318                 MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT   0x17059  
319                 MX6UL_PAD_GPIO1_IO09__GPIO1_IO09        0x17059  
320                 MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID   0x13058  
321             >; 
322         }; 
......

添加了一些宏,

190 #define MX6UL_PAD_UART1_RTS_B__UART1_DCE_RTS 0x0090 0x031C 0x0620  0x0 0x3 
191 #define MX6UL_PAD_UART1_RTS_B__UART1_DTE_CTS 0x0090 0x031C 0x0000     0x0 0x0 
192 #define MX6UL_PAD_UART1_RTS_B__ENET1_TX_ER   0x0090 0x031C 0x0000  0x1 0x0 
193 #define MX6UL_PAD_UART1_RTS_B__USDHC1_CD_B   0x0090 0x031C 0x0668    0x2 0x1 
194 #define MX6UL_PAD_UART1_RTS_B__CSI_DATA05    0x0090 0x031C 0x04CC  0x3 0x1
195 #define MX6UL_PAD_UART1_RTS_B__ENET2_1588_EVENT1_OUT 0x0090 0x031C 0x0000 0x4 0x0 
196 #define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19    0x0090 0x031C 0x0000 0x5 0x0 
197 #define MX6UL_PAD_UART1_RTS_B__USDHC2_CD_B   0x0090 0x031C 0x0674  0x8 0x2

仔细观察应该就能发现,这 8个宏定义分别对应 UART1_RTS_B这个 PIN的 8个复用 IO

在这里插入图片描述
这 5个值的含义如下所示:
<mux_reg conf_reg input_reg mux_mode input_val> 分别设置了这些寄存器的值
mux_reg:复用寄存器地址
conf_reg:电气属性配置寄存器地址
input_reg:输入寄存器地址
mux_mode:引脚复用为什么模式, 也就是写入mux_reg的值
input_val:输入寄存器配置值

pinctrl子系统驱动程序便是调用这里的信息来复用引脚
0x0090:mux_reg寄存器偏移地址,设备树中的 iomuxc节点就是 IOMUXC外设对应的节
点 ,根据其 reg 属性可知 IOMUXC 外设寄存器起始地址为 0x020e0000 。因此可知,0x020e0000+mux_reg就是 PIN的复用寄存器地址。在这里插入图片描述
0x031C:conf_reg寄存器偏移地址,和 mux_reg 一样,0x020e0000+0x031c=0x020e031c,
这个就是寄存器 IOMUXC_SW_PAD_CTL_P AD_UART1_RTS_B的地址。
0x0000:input_reg寄存器偏移地址,有些外设有 input_reg寄存器,有 input_reg寄存器的
外设需要配置 input_reg寄存器。没有的话就不需要设置,UART1_RTS_B这个 PIN在做
GPIO1_IO19的时候是没有 input_reg寄存器,因此这里 intput_reg是无效的。
0x5 : mux_reg 寄 存 器 值 , 在 这 里 就 相 当 于 设 置
IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B寄存器为 0x5,也即是设置 UART1_RTS_B这
个 PIN复用为 GPIO1_IO19。
0x0:input_reg寄存器值,在这里无效
0x17059就是 conf_reg寄存器值,此值由用户自行设置,通
过此值来设置一个 IO 的上 /下拉 、驱动能力和速度等 。在这里就相当于设置寄存器
IOMUXC_SW_P AD_CTL_PAD_UART1_RTS_B的值为 0x17059。

设备树中添加 pinctrl节点模板

找到对应的厂商的样板节点
如 6ull样板节点 在iomuxc节点
中的“imx6ul-evk”子节点
1、创建对应的节点

1 pinctrl_test: testgrp { 
2 
3       >; 

2、添加“fsl,pins”属性
设备树是通过属性来保存信息的,因此我们需要添加一个属性,属性名字一定要为“fsl,pins”,
因为对于 I.MX系列 SOC而言,pinctrl驱动程序是通过读取“fsl,pins”属性值来获取 PIN的配
置信息,完成以后如下所示:

1 pinctrl_test: testgrp { 
2   fsl,pins = < 
3      /* 设备所使用的 PIN配置信息 */ 
4      >; 
5 };

3、在“fsl,pins”属性中添加 PIN配置信息
最后在“fsl,pins”属性中添加具体的 PIN配置信息,完成以后如下所示:

1 pinctrl_test: testgrp { 
2   fsl,pins = < 
3      MX6UL_PAD_GPIO1_IO00__GPIO1_IO00  config /*config是具体设置值*/ 
4       >;
5 };

GPIO子系统

当引脚被复用为gpio功能时, 就可以使用gpio子系统对此引脚进行操作
带有 属性 gpio-controller; 的节点就是gpioctrl 节点
#gpio-cells = <2> 表示这个控制器下每一个引脚要用2个32位的数(cell)来描述。
在这里插入图片描述

如何使用GPIO子系统

定义GPIO Controller是芯片厂家的事,我们怎么引用某个引脚呢?在自己的设备节点中使用属性"[-]gpios"在这里插入图片描述
驱动代码中调用GPIO子系统
驱动程序中要包含头文件,
#include <linux/gpio/consumer.h>
下表列出常用的函数:
获得GPIO
gpiod_get
gpiod_get_index
gpiod_get_array
devm_gpiod_get
devm_gpiod_get_index
devm_gpiod_get_array
设置方向
gpiod_direction_input
gpiod_direction_output
读值、写值
gpiod_get_value
gpiod_set_value
释放GPIO
gpio_free
gpiod_put
gpiod_put_array
devm_gpiod_put
devm_gpiod_put_array

有前缀“devm_”的含义是“设备资源管理”(Managed Device Resource),这是一种自动释放资源的机制。它的思想是“资源是属于设备的,设备不存在时资源就可以自动释放

编写驱动,使用pinctrl . gpioctrl点亮led

1,根据原理图, 找到led的引脚
在设备树中要用到
在这里插入图片描述
LED0用到的引脚为GPIO1_IO03
2,修改设备树将设备节点加入设备树
添加设备子节点
(直接在根节点的子节点下添加即可)
指定pinctrl有几个必要的属性:
pinctrl-names
pinctrl-[No] ===> pinctrl-0, pinctrl-1…

gpioled{
	compatible = "alientek,gpioled";//指定匹配的驱动
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_gpioled>;
	status = "okay";
};

3,在设备树中指定好pinctrl
在&iomuxc节点添加子节点 (iomuxc顾名思义 io复用, 设备树中的体现就是用于pinctrl的定义)
引脚宏定义在文件 imx6ul-pinfunc.h中
在这里插入图片描述
显然我们需要的是 MX6UL_PAD_GPIO1_IO03_GPIO1_IO03 这个宏

pinctrl_gpioled:ledgrp{
	fsl,pins = <
		MX6UL_PAD_GPIO1_IO03_GPIO1_IO03 0x10b0
	>;
};

0x10b0是电器属性, 需通过芯片手册查阅寄存器功能得出, (imx芯片有官方引脚配置工具可使用)在这里插入图片描述
4,节点指定gpioctrl
必要属性有
[name-]gpios ===> led-gpios, gpios

gpioled{
	compatible = "alientek,gpioled";//指定匹配的驱动
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_gpioled>;
	led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
	status = "okay";
};

5,编译设备树生成dtb文件并使用
在内核目录下

make dtbs

查看设备节点是否生成

在这里插入图片描述
6,编写驱动代码
准备好makefile

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册

KERN_DIR = /home/yu/bsp_sdk_imax6ull/Linux-4.9.88

all:
	make -C $(KERN_DIR) M=`pwd` modules 
#
	$(CROSS_COMPILE)gcc -o ledtest ledtest.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f ledtest
install:
#根据环境修改
	cp *.ko ~/embedded_code/ledtest/
	cp ledtest ~/embedded_code/ledtest/

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o

obj-m += gpioLed.o

编写好好字符设备驱动框架

#include <linux/module.h>
#include <linux/platform_device.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>

#define GPIOLED_CNT		1
#define GPIOLED_NAME	"gpioled"

/*gpioled设备结构体*/
struct gpioled_dev{
	dev_t devid; 		//设备号
	int major;	 		//主设备号
	int minor;	 		//次设备号
	struct cdev cdev;
	struct class *class;
	struct device *device;
};

static struct gpioled_dev gpioled; /*"LED"*/

static int led_open(struct inode *, struct file *)
{
	return 0;
}
static ssize_t led_write(struct file *, const char __user *, size_t, loff_t *)
{
	return 0
}
static int led_close(struct inode *, struct file *)
{
	return 0;
}



//定义自己的file_operations结构体
static struct file_operations led_fops ={
    .owner = THIS_MODULE,
	.open    = led_open,
	.write   = led_write,
	.release = led_close,
};


/*驱动入口函数*/

static int __init led_init(void)
{
	/*1.注册字符设备驱动*/
	gpioled.major = 0; 				//为0由内核自动分配
	gpioled.minor = 0; 				//次设备号从0开始
	int ret = 0;					//错误返回值
	int i;

	
	if(gpioled.major){ 				//给定了设备号 静态分配
	
		gpioled.devid = MKDEV(gpioled.major, gpioled.minor); 
		/*2.4内核后推出的批量注册字符设备*/
		ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
		if(IS_ERR(ret)){
			printk(KERN_ERR, "line:%d %s:%s *** register_chrdev_region fail\n",  
				__LINE__, __FILE__, __FUNCTION__));
			goto chrdev_fail;
		}

		
	}else{							//没给定设备号 由内核动态分配

		//动态分配
		/*
			动态分配设备号到gpioled.devid 
			从次设备号为0开始 GPIOLED_CNT个设备,
			名称为 GPIOLED_NAME
		*/
		ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
		
		if(IS_ERR(ret)){
			printk(KERN_ERR, "line:%d %s:%s *** alloc_register_chrdev_region fail\n",  
				__LINE__, __FILE__, __FUNCTION__));
			goto chrdev_fail;
		}
		
		gpioled.major = MAJOR(gpioled.devid);
		gpioled.minor = MINOR(gpioled.devid);
		printk("gpioled major = %d, minor = %d\r\n", 
			gpioled.major, gpioled.minor);
			
	}


	/*2.初始化cdev 将file_operations结构体放入cdev-> ops 里*/
	gpioled.cdev.owner = THIS_MODULE;
	ret = cdev_init(&gpioled.cdev, &led_fops);
	if(IS_ERR(ret)){
		printk(KERN_ERR, "line:%d %s:%s *** cdev_init fail\n",  
			__LINE__, __FILE__, __FUNCTION__));
		goto cdev_init_fail;
	}

	/*
		3.添加cdev 将cdev结构体添加到系统中,
		并将dev(注册好的设备编号)放入cdev->dev里,
		count(次设备编号个数)放入cdev->count里
	*/
	ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
	if(IS_ERR(ret)){
		printk(KERN_ERR, "line:%d %s:%s *** cdev_add fail\n",  
			__LINE__, __FILE__, __FUNCTION__));
		goto cdev_add_fail;
	}
	
	/*
		4.创建类
	*/
	gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
	if(IS_ERR(gpioled.class)){
		printk(KERN_ERR, "line:%d %s:%s *** class_create fail\n",  
			__LINE__, __FILE__, __FUNCTION__));
		goto class_create_fail;
	}

	/*
		5.创建设备
	*/
	gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
	if(IS_ERR(gpioled.device)){
		printk(KERN_ERR, "line:%d %s:%s *** device_create fail\n",  
			__LINE__, __FILE__, __FUNCTION__));
		goto device_create_fail;
	}

	
	return 0;


device_create_fail:
	class_destroy(gpioled.device);
class_create_fail:
cdev_add_fail:
	cdev_del(&gpioled.cdev);
cdev_init_fail:
	unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
chrdev_fail:
	return -EINVAL;		
}

/*驱动出口函数*/
static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	device_destroy(gpioled.class, gpioled.devid);
	class_destroy(gpioled.class);
	cdev_del(&gpioled.cdev);
	unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
}

module_init(led_init);
module_exit(led_exit);
MOUDLE_LICENSE("GPL");
MODULE_AUTHOR("jizhiyu");

字符设备驱动框架 中添加 对引脚的操作

#include <linux/module.h>
#include <linux/platform_device.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

#define GPIOLED_CNT		1
#define GPIOLED_NAME	"gpioled"
#define LEDOFF			0
#define LEDON			1

/*gpioled设备结构体*/
struct gpioled_dev{
	dev_t devid; 			//设备号
	int major;	 			//主设备号
	int minor;	 			//次设备号
	struct cdev cdev;
	struct class *class;
	struct device *device;
	struct device_node *nd;
	int led_gpio;			//gpio编号
};

static struct gpioled_dev gpioled; /*"LED"*/

static int led_open(struct inode *node, struct file *filp)
{
	return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
	int ret ;
	unsigned char databuf[1];
	
	struct gpioled_dev *dev = filp->private_data;
	ret = copy_from_user(databuf, buf, 1);
	if(ret < 0){
		return -EINVAL;
	}
	if(databuf[0] == LEDON){
		gpio_set_value(gpioled.led_gpio, 0);
	}else if(databuf[0] == LEDOFF){
		gpio_set_value(gpioled.led_gpio, 1);
	}
		
	return 0
}
static int led_close(struct inode *node, struct file *filp)
{
	return 0;
}



//定义自己的file_operations结构体
static struct file_operations led_fops ={
    .owner = THIS_MODULE,
	.open    = led_open,
	.write   = led_write,
	.release = led_close,
};


/*驱动入口函数*/

static int __init led_init(void)
{
	/*1.注册字符设备驱动*/
	gpioled.major = 0; 				//为0由内核自动分配
	gpioled.minor = 0; 				//次设备号从0开始
	int ret = 0;					//错误返回值
	int i;

	
	if(gpioled.major){ 				//给定了设备号 静态分配
	
		gpioled.devid = MKDEV(gpioled.major, gpioled.minor); 
		/*2.4内核后推出的批量注册字符设备*/
		ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
		if(IS_ERR(ret)){
			printk(KERN_ERR, "line:%d %s:%s *** register_chrdev_region fail\n",  
				__LINE__, __FILE__, __FUNCTION__));
			goto chrdev_fail;
		}

		
	}else{							//没给定设备号 由内核动态分配

		//动态分配
		/*
			动态分配设备号到gpioled.devid 
			从次设备号为0开始 GPIOLED_CNT个设备,
			名称为 GPIOLED_NAME
		*/
		ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
		
		if(IS_ERR(ret)){
			printk(KERN_ERR, "line:%d %s:%s *** alloc_register_chrdev_region fail\n",  
				__LINE__, __FILE__, __FUNCTION__));
			goto chrdev_fail;
		}
		
		gpioled.major = MAJOR(gpioled.devid);
		gpioled.minor = MINOR(gpioled.devid);
		printk("gpioled major = %d, minor = %d\r\n", 
			gpioled.major, gpioled.minor);
			
	}


	/*2.初始化cdev 将file_operations结构体放入cdev-> ops 里*/
	gpioled.cdev.owner = THIS_MODULE;
	ret = cdev_init(&gpioled.cdev, &led_fops);
	if(IS_ERR(ret)){
		printk(KERN_ERR, "line:%d %s:%s *** cdev_init fail\n",  
			__LINE__, __FILE__, __FUNCTION__));
		goto cdev_init_fail;
	}

	/*
		3.添加cdev 将cdev结构体添加到系统中,
		并将dev(注册好的设备编号)放入cdev->dev里,
		count(次设备编号个数)放入cdev->count里
	*/
	ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
	if(IS_ERR(ret)){
		printk(KERN_ERR, "line:%d %s:%s *** cdev_add fail\n",  
			__LINE__, __FILE__, __FUNCTION__));
		goto cdev_add_fail;
	}
	
	/*
		4.创建类
	*/
	gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
	if(IS_ERR(gpioled.class)){
		printk(KERN_ERR, "line:%d %s:%s *** class_create fail\n",  
			__LINE__, __FILE__, __FUNCTION__));
		goto class_create_fail;
	}

	/*
		5.创建设备
	*/
	gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
	if(IS_ERR(gpioled.device)){
		printk(KERN_ERR, "line:%d %s:%s *** device_create fail\n",  
			__LINE__, __FILE__, __FUNCTION__));
		goto device_create_fail;
	}

	/*
		1.获取设备节点
	*/
	gpioled.nd = of_find_node_by_path("/gpioled");
	if(gpioled.nd == NULL){
		printk(KERN_ERR, "line:%d %s:%s *** of_find_node_by_path fail\n",	
			__LINE__, __FILE__, __FUNCTION__));
		goto find_node_fail;
	}

	/*
		2.获取LED所对应的GPIO
	*/
	gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led", 0);
	if(IS_ERR(gpioled.led_gpio)){
		printk(KERN_ERR, "line:%d %s:%s *** of_get_named_gpio fail\n",	
			__LINE__, __FILE__, __FUNCTION__));
		goto find_node_fail;
	}
	printk("led gpio num =%d\r\n", gpioled.led_gpio);


	/*
		3.申请IO 
		也可以使用gpiod_get函数 返回一个gpio_desc用于操作gpio
	*/
	ret = gpio_request(gpioled.led_gpio, "led-gpio");
	if(ret){
		
		printk(KERN_ERR, "line:%d %s:%s *** gpio_request fail\n",	
			__LINE__, __FILE__, __FUNCTION__));
		goto find_node_fail;
	}

	gpiod_get(struct device * dev, const char * con_id, enum gpiod_flags flags);
	
	/*
		4.设置IO为输出
		也可以使用	gpiod_direction_output(struct gpio_desc * desc, int value)
	*/
	ret = gpio_direction_output(gpioled.led_gpio, 1);
	if(IS_ERR(ret)){
		printk(KERN_ERR, "line:%d %s:%s *** gpio_direction_output fail\n",	
			__LINE__, __FILE__, __FUNCTION__));
		goto gpio_direction_output_fail;
	}
	gpiod_direction_output(struct gpio_desc * desc, int value)
	

	/*
		5.输出低电平, 点亮LED
		也可以使用gpiod_set_value(struct gpio_desc * desc, int value)
	*/
	gpio_set_value(gpioled.led_gpio, 0);
	gpiod_set_value(struct gpio_desc * desc, int value)
	
	return 0;
	
	
	return 0;

gpio_direction_output_fail:
	gpio_free(gpioled.led_gpio);
	//gpiod_free(struct gpio_desc * desc)
find_node_fail:
	device_del(gpioled.device);
device_create_fail:
	class_destroy(gpioled.device);
class_create_fail:
cdev_add_fail:
	cdev_del(&gpioled.cdev);
cdev_init_fail:
	unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
chrdev_fail:
	return -EINVAL;		
}

/*驱动出口函数*/
static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	gpio_set_value(gpioled.led_gpio, 1);
	gpio_free(gpioled.led_gpio);
	//gpiod_free(struct gpio_desc * desc)
	device_destroy(gpioled.class, gpioled.devid);
	class_destroy(gpioled.class);
	cdev_del(&gpioled.cdev);
	unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
}

module_init(led_init);
module_exit(led_exit);
MOUDLE_LICENSE("GPL");
MODULE_AUTHOR("jizhiyu");

7,编写驱动测试程序


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

/*
 * ./ledtest /dev/100ask_led0 on
 * ./ledtest /dev/100ask_led0 off
 */
int main(int argc, char **argv)
{
	int fd;
	char status;
	
	/* 1. 判断参数 */
	if (argc != 3) 
	{
		printf("Usage: %s <dev> <on | off>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	/* 3. 写文件 */
	if (0 == strcmp(argv[2], "on"))
	{
		status = 1;
		write(fd, &status, 1);
	}
	else
	{
		status = 0;
		write(fd, &status, 1);
	}
	
	close(fd);
	
	return 0;
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值