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*/
>;
};