一、基础知识
在设备树根节点添加led子节点
alphaled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-led";
status = "okay";
reg = <
0x020c406c 0x04
0x020e0068 0x04
0x020e02f4 0x04
0x0209c000 0x04
0x0209c004 0x04
>;
};
reg属性中的值分别是gpio的初始化寄存器,比如0x020c406c代表寄存器CCM_CCGR1_BASE,长度为4字节。
修改好设备树,重新编译,使用新的设备树启动内核。
通过命令查看新节点是否成功创建了
二、驱动程序
#include <linux/ide.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
/********************************************************************
* dtsled.c
* 作者:张亚胜
* 版本:V1.0
* 描述:dtsled驱动文件
* 其他:无
* 网站:www.s123.xyz
* 日志:出版V1.0 2020/7/28 张亚胜创建
* ******************************************************************/
#define DTSLED_CNT 1 /* 设备号个数 */
#define DTSLED_NAME "dtsled" /* 设备名字 */
#define LED_OFF 0 /* 关灯 */
#define LED_ON 1 /* 开灯 */
/* 寄存器的物理地址 */
#define CCM_CCGR1_BASE (0x020c406c) /* 时钟 */
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068) /* 复用 */
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4) /* 属性 */
#define GPIO1_DR_BASE (0X0209C000) /* 数据 */
#define GPIO1_GDIR_BASE (0X0209C004) /* 方向 */
/* 映射后的寄存器虚拟地址 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
/* dtsled设备结构体 */
struct dtsled_dev{
dev_t dev_id; /* 设备号 */
struct cdev cdev; /* 字符设备 */
struct class *class; /* mdev 自动节点文件使用 */
struct device *device; /* class使用 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
};
struct dtsled_dev dtsled; /* led设备 */
//led操作函数
//参数:LED_ON LED_OFF
void led_switch(u8 sta)
{
u32 val=0;
if(sta == LED_ON)
{
val = readl(GPIO1_DR);
val &= ~(1<<3);
writel(val,GPIO1_DR);
}
else if(sta == LED_OFF)
{
val = readl(GPIO1_DR);
val |= (1<<3);
writel(val,GPIO1_DR);
}
}
static int led_open(struct inode *inode,struct file *filp)
{
filp->private_data = &dtsled; /* 设置私有数据 */
return 0;
}
static ssize_t led_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{
int retval = 0;
unsigned char databuf[1];
unsigned char ledstat;
retval = copy_from_user(databuf,buf,cnt);
if(retval<0){
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0];
led_switch(ledstat);
return 0;
}
static int led_release(struct inode *inode,struct file *filp)
{
return 0;
}
static struct file_operations dtsled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
static int __init led_init(void)
{
u32 val=0;
int ret;
u32 regdata[14];
const char *str;
struct property *proper;
/* 获取设备树中的属性 */
/* 获取设备节点 */
dtsled.nd = of_find_node_by_path("/alphaled");
if(dtsled.nd == NULL){
printk("alphaled node can not found!\r\n");
return -EINVAL;
}
/* 获取compatible内容 */
proper = of_find_property(dtsled.nd,"compatible",NULL);
if(proper == NULL){
printk("compatible proper find failed!\r\n");
}else {
printk("compatible = %s\r\n",(char*)proper->value);
}
/* 获取status属性内容 */
ret = of_property_read_string(dtsled.nd,"status",&str);
if(ret < 0){
printk("status read failed!\r\n");
} else {
printk("status = %s\r\n",str);
}
/* 获取reg属性内容 */
ret = of_property_read_u32_array(dtsled.nd,"reg",regdata,10);
if(ret < 0){
printk("reg property read failed!\r\n");
} else {
u8 i=0;
printk("reg data:\r\n");
for(i=0;i<10;i++)
{
printk("%#X ",regdata[i]);
}
printk("\r\n");
}
#if 0
/* 1.寄存器地址映射 */
IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4);
SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4);
GPIO1_DR = ioremap(GPIO1_DR_BASE,4);
GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4);
#else
IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd,0);
SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd,1);
SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd,2);
GPIO1_DR = of_iomap(dtsled.nd,3);
GPIO1_GDIR = of_iomap(dtsled.nd,4);
#endif
/* 2.使能GPIO1时钟 */
val = readl(IMX6U_CCM_CCGR1);
val &= ~(3<<26);
val |= (3<<26);
writel(val,IMX6U_CCM_CCGR1);
/* 3.设置GPIO1_IO03的复用 */
writel(5,SW_MUX_GPIO1_IO03);
/* 4.设置寄存器的IO属性 */
writel(0x10b0,SW_PAD_GPIO1_IO03);
/* 4.设置GPIO1_IO03为输出功能 */
val = readl(GPIO1_GDIR);
val &= ~(1<<3);
val |= (1<<3);
writel(val,GPIO1_GDIR);
/* 5.默认关闭LED */
led_switch(LED_OFF);
/* 注册字符设备驱动 */
if(dtsled.major){
dtsled.dev_id = MKDEV(dtsled.major,0);//构建设备号 子设备号一般设置0
register_chrdev_region(dtsled.dev_id,DTSLED_CNT,DTSLED_NAME);
}else{
alloc_chrdev_region(&dtsled.dev_id,0,DTSLED_CNT,DTSLED_NAME);//申请设备号
dtsled.major = MAJOR(dtsled.dev_id);//获取主设备号
dtsled.minor = MINOR(dtsled.dev_id);//获取子设备号
}
printk("dtsled major=%d,minor=%d\r\n",dtsled.major,dtsled.minor);
/* 初始化cdev */
dtsled.cdev.owner = THIS_MODULE;
cdev_init(&dtsled.cdev,&dtsled_fops);//初始化cdev并绑定设备操作集合
/* 添加一个cdev */
cdev_add(&dtsled.cdev,dtsled.dev_id,DTSLED_CNT);
/* 创建类 */
dtsled.class = class_create(THIS_MODULE,DTSLED_NAME);
if(IS_ERR(dtsled.class)){
return PTR_ERR(dtsled.class);
}
/* 创建设备节点 mknod 向类中添加设备 */
dtsled.device = device_create(dtsled.class,NULL,dtsled.dev_id,NULL,DTSLED_NAME);//成功后就会生成/dev/dtsled_NAME这个设备文件
if(IS_ERR(dtsled.device)){
return PTR_ERR(dtsled.device);
}
return 0;
}
static void __exit led_exit(void)
{
/* 取消映射 */
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
/* 注销字符设备 */
cdev_del(&dtsled.cdev);
/* 释放设备号 不管是申请的还是注册的 都用下面打函数释放 */
unregister_chrdev_region(dtsled.dev_id,DTSLED_CNT);
/* 删除设备节点 */
device_destroy(dtsled.class,dtsled.dev_id);
/* 删除类 */
class_destroy(dtsled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zys");
三、测试程序
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
/********************************************************************
* 文件名:dtsled.c
* 作者:张亚胜
* 版本:V1.0
* 描述:dtsled测试文件
* 其他:使用方法:./dtsled /dev/led 0 //写0 关灯
* ./dtsled /dev/led 1 //写1 开灯
* 网站:www.s123.xyz
* 日志:出版V1.0 2020/7/26 张亚胜创建
* ******************************************************************/
/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd,retval;
char *filename;
char buf[100];
if(argc != 3){
printf("params err!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd<0){
printf("open err!\r\n");
return -1;
}
buf[0] = atoi(argv[2]);
write(fd,buf,1);
retval = close(fd);
return retval;
}