设备树文件添加
myplatform{
compatible="hqyj,myplatform";
interrupt-parent=<&gpiof>;
interrupts=<8 0>,<7 0>,<9 0>;
led1-gpio=<&gpioe 10 0>;
led2-gpio=<&gpiof 10 0>;
led3-gpio=<&gpioe 8 0>;
reg=<0x12345678 0x400>;
};
应用层
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/ioctl.h>
#include"head.h"
int main(int argc, char const *argv[])
{
int fd;
int devc;//设备的编号
char buf[128];
printf("选择需要打开的设备文件(led)>>");//配置需要打开的设备文件
scanf("%d",&devc);
sprintf(buf,"/dev/myled%d",devc);
printf("路径为:%s",buf);
fd=open(buf,O_RDWR);
if (fd<0)
{
printf("文件打开失败\n");
return -1;
}
printf("设备文件打开成功\n");
int a,b;//输入的值
while (1)
{
out:
printf("选择需要打开的灯led 1 led 2 led 3>>");
scanf("%d",&a);
printf("选择要实现的功能 0 关 1开>>");
scanf("%d",&b);
switch (b)
{
case 0:
ioctl(fd,LED_OFF,a);
break;
case 1:
ioctl(fd,LED_ON,a);
break;
default:
printf("输入错误请重新输入\n");
goto out;
break;
}
}
return 0;
}
头文件
#ifndef __HEAD_H__
#define __HEAD_H__
#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)
#endif
设备层
#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>
#include<linux/gpio.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include"head.h"
#include <linux/platform_device.h>
void pdev_release(struct device *dev)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
}
struct resource res[]={
[0]={
.start=0x50006000,//起始地址
.end=0x50006000+0x400,//终止地址
.name="pe_mem",//名字
.flags=IORESOURCE_MEM ,//标志位地址空间
},
[1]={
.start=71,
.end=71,
.name="key1_irq",
.flags=IORESOURCE_IRQ,
},
[2]={
.start=72,
.end=72,
.name="key2_irq",
.flags=IORESOURCE_IRQ,
},
[3]={
.start=73,
.end=73,
.name="key3_irq",
.flags=IORESOURCE_IRQ,
},
};
//设备结构体
struct platform_device pdev={
.name="aaaaa",
.id=PLATFORM_DEVID_AUTO,
.dev={
.release=pdev_release,
},
.num_resources=ARRAY_SIZE(res),
.resource=res,
};
static int __init mycdev_init(void)
{
platform_device_register(&pdev);
return 0;
}
static void __exit mycdev_exit(void)
{
platform_device_unregister(&pdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
驱动层
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include"head.h"
struct resource *res;
unsigned int irq_no[3];
int major;
char kbuf[128]={0};
struct class *cls;
struct device *dev_c;
struct device_node *dnode;
struct gpio_desc *lednode1;
struct gpio_desc *lednode2;
struct gpio_desc *lednode3;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
unsigned long ret;
//向用户空间读取拷贝
if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size=sizeof(kbuf);
ret=copy_to_user(ubuf,kbuf,size);
if(ret)//拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
unsigned long ret;
//从用户空间读取数据
if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size=sizeof(kbuf);
ret=copy_from_user(kbuf,ubuf,size);
if(ret)//拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
return 0;
}
long mycdev_ioctl (struct file * file, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case LED_OFF:
switch (arg)
{
case 1:
gpiod_set_value(lednode1,0);
break;
case 2:
gpiod_set_value(lednode2,0);
break;
case 3:
gpiod_set_value(lednode3,0);
break;
}
break;
case LED_ON:
switch (arg)
{
case 1:
gpiod_set_value(lednode1,1);
break;
case 2:
gpiod_set_value(lednode2,1);
break;
case 3:
gpiod_set_value(lednode3,1);
break;
}
break;
default:
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
//定义操作方法结构体变量并赋值
struct file_operations fops={
.open=mycdev_open,
.read=mycdev_read,
.write=mycdev_write,
.unlocked_ioctl=mycdev_ioctl,
.release=mycdev_close,
};
irqreturn_t myirq_handle(int irq,void *dev)
{
switch ((unsigned int)dev)
{
case 0:
//反转电平
gpiod_set_value(lednode1,!gpiod_get_value(lednode1));
//防止其他灯亮
gpiod_set_value(lednode2,0);
gpiod_set_value(lednode3,0);
break;
case 1:
//反转电平
gpiod_set_value(lednode2,!gpiod_get_value(lednode2));
//防止其他灯亮
gpiod_set_value(lednode1,0);
gpiod_set_value(lednode3,0);
break;
case 2:
//反转电平
gpiod_set_value(lednode3,!gpiod_get_value(lednode3));
//防止其他灯亮
gpiod_set_value(lednode1,0);
gpiod_set_value(lednode2,0);
break;
default:
break;
}
return IRQ_HANDLED;
}
int pdrv_probe(struct platform_device *dev)
{
printk("%s:%s:%d",__FILE__,__func__,__LINE__);
//字符设备驱动注册
major=register_chrdev(0,"mychrdev",&fops);
if(major<0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功:major=%d\n",major);
//向上提交目录
cls=class_create(THIS_MODULE,"mychrdev");
if(IS_ERR(cls))
{
printk("向上提交目录失败\n");
return -PTR_ERR(cls);
}
printk("向上提交目录成功\n");
int i;
for(i=0;i<3;i++)
{
dev_c=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);//制作字符设备文件
if(IS_ERR(dev_c))
{
printk("向上提交设备节点失败\n");
return -PTR_ERR(dev_c);
}
}
printk("向上提交设备节点成功\n");
res=platform_get_resource(dev,IORESOURCE_MEM,0);
if(res==NULL)
{
printk("获取MEM类型资源失败\n");
return -EFAULT;
}
printk("MEM类型资源为%x\n",res->start);
//获取中断类型的资源
for ( i = 0; i < 3; i++)
{
irq_no[i]=platform_get_irq(dev,i);
if (irq_no[i]<0)
{
printk("获取中断类型资源失败\n");
return irq_no[i];
}
printk("获取中断类型资源值为%d\n",irq_no[i]);
}
//解析LED1的gpio编号
lednode1=gpiod_get_from_of_node(dev->dev.of_node,"led1-gpio",0,GPIOD_OUT_LOW,NULL);
if (lednode1==NULL)
{
printk("解析gpio编号失败\n");
return -ENXIO;
}
lednode2=gpiod_get_from_of_node(dev->dev.of_node,"led2-gpio",0,GPIOD_OUT_LOW,NULL);
if (lednode2==NULL)
{
printk("解析gpio编号失败\n");
return -ENXIO;
}
lednode3=gpiod_get_from_of_node(dev->dev.of_node,"led3-gpio",0,GPIOD_OUT_LOW,NULL);
if (lednode3==NULL)
{
printk("解析gpio编号失败\n");
return -ENXIO;
}
printk("解析gpio-led编号成功\n");
for ( i = 0; i < 3; i++)
{
irq_no[i]=irq_of_parse_and_map(dev->dev.of_node,i);
if(!irq_no[i])
{
printk("解析软中断失败\n");
return -ENXIO;
}
printk("解析%d 软中断成功\n",i+1);
int ret=request_irq(irq_no[i],myirq_handle,IRQF_TRIGGER_FALLING,"key",(void*)i);
if(ret)
{
printk("注册按键请求中断失败\n");
return ret;
}
}
printk("注册按键请求中断成功\n");
return 0;
}
int pdrv_remove(struct platform_device *dev)
{
gpiod_set_value(lednode1,0);
gpiod_put(lednode1);
gpiod_set_value(lednode2,0);
gpiod_put(lednode2);
gpiod_set_value(lednode3,0);
gpiod_put(lednode3);
int i;
for ( i = 0; i < 3; i++)
{
free_irq(irq_no[i],(void*)i);
device_destroy(cls,MKDEV(major,i));
}
//销毁目录
class_destroy(cls);
//注销字符设备驱动
unregister_chrdev(major,"mychrdev");
printk("%s:%s:%d",__FILE__,__func__,__LINE__);
return 0;
}
struct of_device_id oftable[]=
{
{.compatible="hqyj,myplatform",},
{/*end node*/},//防止数组越界
};
struct platform_driver pdrv={
.probe=pdrv_probe,
.remove=pdrv_remove,
.driver={
.name="bbbbb",
.of_match_table=oftable,//设置设备树匹配
},
};
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
现象演示