Linux设备树驱动编程-基于pinctrl和gpio子系统

my_driver.c

#include "my_led_driver_reg.h" 


static void led_switch(int iLedSta)
{
	switch(iLedSta)
	{
		case LED_ON:
			gpio_set_value(led_device.led_gpio,LOW_LEVEL);
			break;
		case LED_OFF:
			gpio_set_value(led_device.led_gpio,HIGH_LEVEL);
			break;
		default:
			gpio_set_value(led_device.led_gpio,HIGH_LEVEL);
			break;		
	}
			
}

//echo
static ssize_t led_enable_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t len)
{
	bool iLedEnable;
	u32 iRet;

	iRet  = strtobool(buf, &iLedEnable);
	if(iRet < 0)
	{
		debug("strtobool failed\n");
		return iRet;
	}

	debug("led led_enable_store is=%d\n",iLedEnable);
	led_switch(!!iLedEnable);
		
    return len;
}

//声明led_enable文件节点
static DEVICE_ATTR(led_enable, S_IWUSR, NULL,led_enable_store);

static const struct attribute *atk_imx6ul_led_sysfs_attrs[] = {
	&dev_attr_led_enable.attr,
	NULL,
};


static int imx6ull_led_open(struct inode *inode, struct file *file)
{
	file->private_data = &led_device;
	return 0;
}


static int imx6ull_led_close(struct inode *inode, struct file *file)
{
	file->private_data = &led_device;
	return 0;
}

static ssize_t imx6ull_led_read(struct file *file, char __user *buf, size_t cnt, loff_t * loff)
{
	return 0;
}

static ssize_t imx6ull_led_write (struct file *file, const char __user *buf, size_t cnt, loff_t *loff)
{
	u32 iRet;
	unsigned char cALedbuf[1];
	unsigned char iLedSta;
	
	iRet = copy_from_user(cALedbuf,buf,cnt);
	if(iRet < 0)
	{
		debug("copy from user failed\n");
		return -EINVAL;
	}
	
	iLedSta = cALedbuf[0];
	
    debug("++klz write led ,led status %d\n",iLedSta);
	led_switch(!!iLedSta);

	return 0;

}

static struct file_operations led_device_fops = {
	.owner   = THIS_MODULE,
	.read    = imx6ull_led_read,
	.write   = imx6ull_led_write,
	.open    = imx6ull_led_open,
	.release = imx6ull_led_close,
};


static int led_parse_dt(void)
{
	int ret;

	/*1.获取设备树中compatible属性的字符串值klz-led*/
	led_device.dev_node = of_find_compatible_node(NULL,NULL,"klz-led");
	if(led_device.dev_node == NULL)
	{
		printk("led device node find failed\n");
		return -1;
	}
	
	/*2.获取gpio编号,将节点中的“led-gpio”属性值转换为对应的 LED 编号。*/
	led_device.led_gpio = of_get_named_gpio(led_device.dev_node, "led-gpio", 0);
	if(led_device.led_gpio < 0)
	{
		printk("++klz failed to get gpio\n");
		return -1;
	}
	
	/*3.申请gpio管脚*/
	ret = gpio_request(led_device.led_gpio, "klz-led");
	if(ret != 0)
	{
		printk("++klz gpio request failed\n");
		return -1;
	}

	/*4.设置gpio为输出且输出高电平,熄灭*/
	ret = gpio_direction_output(led_device.led_gpio,HIGH_LEVEL);
	if(ret != 0)
	{
		printk("++klz gpio direction output failed\n");
		return -1;
	}

	return 0;
}
 
static int __init led_driver_init(void)
{
	u32 iRet;

	/*1.设备树解析*/
	iRet = led_parse_dt();
	if (iRet < 0) {
		printk("++klz led parse error");
		return iRet;
	}
	
	/*2.字符设备驱动框架那一套*/
	/*2.1 之前定义了主设备号*/
	if(led_device.major)
	{
		/*选择次设备号*/
		led_device.devid = MKDEV(led_device.major,0);
		/*注册设备号*/
		iRet = register_chrdev_region(led_device.devid, DEVICE_CNT, DEVICE_NAME);
		if(iRet < 0)
		{
			debug("register_chrdev_region failed\n");
			return iRet;
		}
	}else
	{
		/*向内核申请主次设备号,DEVICE_NAME体现在/proc/devices*/
		alloc_chrdev_region(&led_device.devid, 0, DEVICE_CNT,  DEVICE_NAME); /* 申请设备号 */
		led_device.major = MAJOR(led_device.devid); /* 获取分配号的主设备号 */
		led_device.minor = MINOR(led_device.devid); /* 获取分配号的次设备号 */
	}

	led_device.cdev.owner = THIS_MODULE;
	cdev_init(&led_device.cdev,&led_device_fops);
	/*自动创建设备结点,在/dev目录下体现*/
	iRet = cdev_add(&led_device.cdev,led_device.devid,DEVICE_CNT);
	if(iRet < 0)
	{
		debug("cdev_add device failed\n");
		goto fail_cdev_add;
	}

	led_device.class = class_create(THIS_MODULE,DEVICE_NAME);
	if(IS_ERR(led_device.class))
	{
		debug("class creat failed\n");
		goto fail_class_create;
	}

	/*生成dev/DEVICE_NAME文件*/
	led_device.device = device_create(led_device.class,NULL,led_device.devid,NULL,DEVICE_NAME);
	if(IS_ERR(led_device.device))
	{
		debug("device class failed\n");
		goto fail_device_create;
	}

	/*创建led_enable结点,直接通过系统调用来操作驱动*/
	iRet = sysfs_create_files(&led_device.device->kobj,atk_imx6ul_led_sysfs_attrs);
	if(iRet)
	{
		debug("failed to create sys files\n");
		return -EINVAL;
	}
	
	debug("my led dirver init sucess\n");

	return 0;
fail_cdev_add:
	unregister_chrdev_region(led_device.devid,DEVICE_CNT);
	return -1;
fail_class_create:
	cdev_del(&led_device.cdev);
	unregister_chrdev_region(led_device.devid,DEVICE_CNT);
	return -1;
fail_device_create:
	cdev_del(&led_device.cdev);
	unregister_chrdev_region(led_device.devid,DEVICE_CNT);
	class_destroy(led_device.class);
	return -1;
	
}

static void __exit led_driver_exit(void)
{
	cdev_del(&led_device.cdev);
	unregister_chrdev_region(led_device.devid,DEVICE_CNT);
	/*依赖于class所以先删除*/
	device_destroy(led_device.class, led_device.devid);
	class_destroy(led_device.class);
}


module_init(led_driver_init);
module_exit(led_driver_exit);


MODULE_AUTHOR("klz <1255713178@qq.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("led driver of atk imx6ull");

“my_led_driver_reg.h”

#ifndef MY_LED_DRIVER_REG_H
#define MY_LED_DRIVER_REG_H

#include <linux/types.h>			/*设备号所在头文件*/
#include <linux/module.h>           /*内核模块声明的相关函数*/
#include <linux/init.h>			    /*module_init和module_exit*/
#include <linux/kernel.h>		    /*内核的各种函数*/
#include <asm/io.h>                 /*readl函数*/
#include <linux/cdev.h>             /*cdev*/
#include <linux/device.h>           /*class & device*/
#include <linux/fs.h>
#include <asm/uaccess.h>           /*copy_from_user*/
#include <linux/gpio.h>            /*gpio fileoperation*/
#include <linux/of.h>             
#include <linux/of_gpio.h>


#define _DEBUG_ 
#ifdef  _DEBUG_
	#define  debug(fmt, args...)  	printk(fmt,##args)
#else
	#define  debug(fmt, args...)  	do{}while(0)
#endif

#define DEVICE_NAME "led_driver"
#define DEVICE_CNT   1
#define LED_ON		 1
#define LED_OFF      0
#define HIGH_LEVEL   1
#define LOW_LEVEL    0

struct led_device
{
	dev_t devid;		    /*设备号*/
	struct cdev cdev;       /*cdev*/
	struct class  *class;	    /*类*/
	struct device *device;	/*设备*/
	int    major;			/*主设备号*/
	int    minor;			/*次设备号*/
	struct device_node* dev_node;
	int led_gpio;
	
};
struct led_device led_device;
#endif

ledApp.c:

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


int main(int argc, char *argv[])
{
	int iLedFd;
	int iRet;
	char *pcFileName;
	unsigned char ucLedSta[1];

	pcFileName = argv[1];
	ucLedSta[0]   = atoi(argv[2]);

	if(argc != 3)
	{
		printf("error usage\n");
		return 0;
	}

	iLedFd = open(pcFileName,O_RDWR);
	if(iLedFd < 0)
	{
		printf("%s open failed",pcFileName);
		return 0;
	}

	/*向文件节写入数据*/
	iRet = write(iLedFd,ucLedSta, sizeof(ucLedSta));
	if(iRet < 0 )
	{
		printf("write data failed\n");
		close(iLedFd);
		return 0;
	}

	iRet = close(iLedFd);
	if(iRet < 0)
	{
		printf("close led failed\n");
		return 0;
	}
	return 0;
}

设备树:
在根结点下面加

	/*klz 2021.1.5*/
	klzled{
		#address-cells = <1>;
		#size-cells    = <1>;
		compatible = "klz-led";
		status = "okay";
		
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led>;
		
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
	};

iomuxx结点下面加:

	/*klz 2021.1.5*/
		pinctrl_led:ledgrp{
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03		0x10B0	/*LED0*/
			>;
		};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值