需求:通过gpio子系统和ioctl完成6盏灯的亮灭控制
这里我个人选择用流水灯方式完成
gpio.h,内容较少,仅负责完成ioctl的条件码封装和6个灯的枚举
#ifndef __GPIO_H__
#define __GPIO_H__
#define LED_ON _IOW('a',1,int)
#define LED_OFF _IOW('a',0,int)
typedef enum
{
myled1,
myled2,
myled3,
led1,
led2,
led3
}led_t;
#endif
驱动代码 mynode.h,自动申请6个设备结点控制6个灯,申请6个gpio号,在ioctl函数中完成灯的高低电平设置
#include<linux/module.h>
#include<linux/init.h>
#include<linux/of.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/cdev.h>
#include"gpio.h"
#define CNAME "myleds"
#define count 6
struct device_node *node;
int ret,i;
struct class *cls;
struct device *dev;
struct cdev *cdev;
//gpiono前三项(012)为副板灯,(345)为主板灯
struct gpio_desc *gpiono[count];
unsigned int major=0,minor=0;
dev_t devno;
int mydev_open (struct inode *inode, struct file *file)
{
return 0;
}
int mydev_close (struct inode *inode, struct file *file)
{
return 0;
}
long ioctl (struct file *file, unsigned int cmd,unsigned long arg)
{
int ret,witch;
switch(cmd)
{
case LED_ON:
ret=copy_from_user(&witch,(void*)arg,4);
if(ret)
{
printk("copy from led on error");
return EIO;
}
switch (witch)
{
case myled1:
gpiod_set_value(gpiono[0],1);
break;
case myled2:
gpiod_set_value(gpiono[1],1);
break;
case myled3:
gpiod_set_value(gpiono[2],1);
break;
case led1:
gpiod_set_value(gpiono[3],1);
break;
case led2:
gpiod_set_value(gpiono[4],1);
break;
case led3:
gpiod_set_value(gpiono[5],1);
break;
}
break;
case LED_OFF:
ret=copy_from_user(&witch,(void*)arg,4);
if(ret)
{
printk("copy from led off error");
return EIO;
}
switch (witch)
{
case myled1:
gpiod_set_value(gpiono[0],0);
break;
case myled2:
gpiod_set_value(gpiono[1],0);
break;
case myled3:
gpiod_set_value(gpiono[2],0);
break;
case led1:
gpiod_set_value(gpiono[3],0);
break;
case led2:
gpiod_set_value(gpiono[4],0);
break;
case led3:
gpiod_set_value(gpiono[5],0);
break;
}
break;
}
return 0;
}
const struct file_operations fops={
.open=mydev_open,
.unlocked_ioctl=ioctl,
.release=mydev_close,
};
static int __init demo_init(void)
{
//自动申请设备节点
cdev=cdev_alloc();
if(cdev==NULL)
goto ERR1;
cdev_init(cdev,&fops);
ret=alloc_chrdev_region(&devno,0,count,CNAME);
if(ret)
{
printk("alloc chrdev region error\n");
ret=-ENOMEM;
goto ERR2;
}
major=MAJOR(devno);
minor=MINOR(devno);
ret=cdev_add(cdev,MKDEV(major,minor),count);
if(ret)
{
printk("cdev add error\n");
ret=-EIO;
goto ERR3;
}
cls=class_create(THIS_MODULE,CNAME);
if(IS_ERR(cls))
{
printk("class create error \n");
ret=PTR_ERR(cls);
goto ERR4;
}
for(i=0;i<count;i++)
{
dev=device_create(cls,NULL,MKDEV(major,i),NULL,"led%d",i);
if(IS_ERR(dev))
{
printk("device create error\n");
ret=-EIO;
goto ERR5;
}
}
//6个灯的gpio编号申请
node=of_find_node_by_name(NULL,"myleds");
if(node==NULL)
{
printk("查找失败\n");
return -EIO;
}
gpiono[0]=gpiod_get_from_of_node(node,"myled1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono[0]))
{
return PTR_ERR(gpiono[0]);
}
gpiono[1]=gpiod_get_from_of_node(node,"myled2",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono[1]))
{
return PTR_ERR(gpiono[1]);
}
gpiono[2]=gpiod_get_from_of_node(node,"myled3",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono[2]))
{
return PTR_ERR(gpiono[2]);
}
gpiono[3]=gpiod_get_from_of_node(node,"led1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono[3]))
{
return PTR_ERR(gpiono[3]);
}
gpiono[4]=gpiod_get_from_of_node(node,"led2",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono[4]))
{
return PTR_ERR(gpiono[4]);
}
gpiono[5]=gpiod_get_from_of_node(node,"led3",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono[5]))
{
return PTR_ERR(gpiono[5]);
}
printk("获取并申请gpio编号成功\n");
for(i=0;i<count;i++)
{
gpiod_direction_output(gpiono[i],0);
}
return 0;
ERR5:
for(--i;i>=0;i--)
{
device_destroy(cls,MKDEV(major,i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major,minor),count);
ERR2:
kfree(cdev);
ERR1:
return -EIO;
}
static void __exit demo_exit(void)
{
int i;
//1。销毁设备节点信息
for(i=0;i<count;i++)
{
device_destroy(cls,MKDEV(major,i));
}
//2.销毁目录信息
class_destroy(cls);
//3.驱动的注销
cdev_del(cdev);
//4.销毁设备号
unregister_chrdev_region(MKDEV(major,minor),count);
//5.释放cdev结构体
kfree(cdev);
for(i=0;i<count;i++)
{
gpiod_set_value(gpiono[i],0);
gpiod_put(gpiono[i]);
}
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
应用层代码test.c 开6个设备结点,循环点灯灭灯
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ioctl.h>
#include "gpio.h"
char buf[128]={""};
int main(int argc, char const *argv[])
{
int fd[6]={-1,-1,-1,-1,-1,-1};
fd[0]=open("/dev/led0",O_RDWR);
if(-1==fd[0])
{
perror("open error");
return -1;
}
fd[1]=open("/dev/led1",O_RDWR);
if(-1==fd[1])
{
perror("open error");
return -1;
}
fd[2]=open("/dev/led2",O_RDWR);
if(-1==fd[2])
{
perror("open error");
return -1;
}
fd[3]=open("/dev/led3",O_RDWR);
if(-1==fd[3])
{
perror("open error");
return -1;
}
fd[4]=open("/dev/led4",O_RDWR);
if(-1==fd[4])
{
perror("open error");
return -1;
}
fd[5]=open("/dev/led5",O_RDWR);
if(-1==fd[5])
{
perror("open error");
return -1;
}
int witch;
while (1)
{
witch=myled1;
ioctl(fd[0],LED_ON ,&witch);
sleep(1);
ioctl(fd[0],LED_OFF ,&witch);
sleep(1);
witch=myled2;
ioctl(fd[1],LED_ON ,&witch);
sleep(1);
ioctl(fd[1],LED_OFF ,&witch);
sleep(1);
witch=myled3;
ioctl(fd[2],LED_ON ,&witch);
sleep(1);
ioctl(fd[2],LED_OFF ,&witch);
sleep(1);
//主板
witch=led1;
ioctl(fd[3],LED_ON ,&witch);
sleep(1);
ioctl(fd[3],LED_OFF ,&witch);
sleep(1);
witch=led2;
ioctl(fd[4],LED_ON ,&witch);
sleep(1);
ioctl(fd[4],LED_OFF ,&witch);
sleep(1);
witch=led3;
ioctl(fd[5],LED_ON ,&witch);
sleep(1);
ioctl(fd[5],LED_OFF ,&witch);
sleep(1);
}
close(fd[0]);
close(fd[1]);
close(fd[2]);
close(fd[3]);
close(fd[4]);
close(fd[5]);
return 0;
}
流水灯(6灯版)