步骤1:在设备树文件中添加灯信息
步骤二:代码编写
实验要求:ioctl封装
驱动文件:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include "led.h"
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define CNAME "myled"
//定义结构体获取设备树节点信息
struct device_node *node;
int gpiono[3];//存放新版本
struct gpio_desc *gpinum[3];//存放老版本
int ret;
struct cdev *cdev;
struct class *cls;
struct device *dev;
unsigned int major=0; //存放主设备号
int minor = 0; //存放次设备号
const int count = 6; //注册节点个数
int mycdev_open(struct inode *inode, struct file *file)
{
int mino;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
//通过inode结构体取到次设备号
mino = MINOR(inode->i_rdev);
//将次设备号放到file结构体私有数据中
file->private_data = (void*)mino;
return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int mino; //接收次设备号
mino=(int)file->private_data;
switch(mino)
{ //6盏灯
case MYLED1:
switch(cmd)
{
case LED_ON:
gpio_set_value(gpiono[0],1);
break;
case LED_OFF:
gpio_set_value(gpiono[0],0);
break;
}
break;
case MYLED2:
switch(cmd)
{
case LED_ON:
gpio_set_value(gpiono[1],1);
break;
case LED_OFF:
gpio_set_value(gpiono[1],0);
break;
}
break;
case MYLED3:
switch(cmd)
{
case LED_ON:
gpio_set_value(gpiono[2],1);
break;
case LED_OFF:
gpio_set_value(gpiono[2],0);
break;
}
break;
case LED1:
switch(cmd)
{
case LED_ON:
gpiod_set_value(gpinum[0],1);
break;
case LED_OFF:
gpiod_set_value(gpinum[0],0);
break;
}
break;
case LED2:
switch(cmd)
{
case LED_ON:
gpiod_set_value(gpinum[1],1);
break;
case LED_OFF:
gpiod_set_value(gpinum[1],0);
break;
}
break;
case LED3:
switch(cmd)
{
case LED_ON:
gpiod_set_value(gpinum[2],1);
break;
case LED_OFF:
gpiod_set_value(gpinum[2],0);
break;
}
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.unlocked_ioctl = mycdev_ioctl,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
dev_t devno;
int ret=0;
int i=0;
//分步注册字符设备驱动
//1.分配cdev结构体
cdev=cdev_alloc();
if(NULL==cdev)
{
printk("分配cdev结构体失败\n");
ret=-EIO;
goto ERR1;
}
//2.初始化结构体
cdev_init(cdev,&fops);
//3.动态申请设备号
ret=alloc_chrdev_region(&devno,MKDEV(major,minor),count,CNAME);
if(ret)
{
printk("动态申请设备号失败\n");
ret=-ENOMEM;
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
printk("动态申请设备号成功\n");
//4.驱动注册
ret=cdev_add(cdev,MKDEV(major,minor),count);
if(ret)
{
printk("驱动注册失败\n");
ret = -EIO;
goto ERR3;
}
//5.自动创建设备节点(提交目录,设备节点)
cls=class_create(THIS_MODULE,CNAME);
if(IS_ERR(cls))
{
printk("目录创建失败\n");
ret=PTR_ERR(cls);
goto ERR4;
}
for(i=0;i<count;i++)
{
dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
if(IS_ERR(dev))
{
printk("节点创建失败\n");
ret=PTR_ERR(dev);
goto ERR5;
}
}
//灯的初始化
led_init();
return 0;
ERR5:
for(--i;i>=0;i--)
{
device_destroy(cls, i);
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major,minor),count);
ERR2:
kfree(cdev);
ERR1:
return -ret;
}
int led_init(void)
{
//1.通过名字获取设备节点信息
node=of_find_node_by_name(NULL,"myleds");
if(node==NULL)
{
printk("failed\n");
return -EFAULT;
}
printk("succeed\n");
//获取gpio编号
gpiono[0]=of_get_named_gpio(node,"myled1",0);
if(gpiono[0]<0)
{
printk("获取gpio编号失败\n");
return gpiono[0];
}
printk("get gpiono succeed\n");
//申请gpio使用权
ret=gpio_request(gpiono[0],NULL);
if(ret)
{
printk("申请gpio编号失败\n");
return ret;
}
printk("gpio_request succeed\n");
//设置gpio管脚输出模式
gpio_direction_output(gpiono[0],0);
//led2
//获取gpio编号
gpiono[1]=of_get_named_gpio(node,"myled2",0);
if(gpiono[1]<0)
{
printk("获取gpio编号失败\n");
return gpiono[1];
}
printk("get gpiono succeed\n");
//申请gpio使用权
ret=gpio_request(gpiono[1],NULL);
if(ret)
{
printk("申请gpio编号失败\n");
return ret;
}
printk("gpio_request succeed\n");
//设置gpio管脚输出模式
gpio_direction_output(gpiono[1],0);
//led3
//获取gpio编号
gpiono[2]=of_get_named_gpio(node,"myled3",0);
if(gpiono[2]<0)
{
printk("获取gpio编号失败\n");
return gpiono[2];
}
printk("get gpiono succeed\n");
//申请gpio使用权
ret=gpio_request(gpiono[2],NULL);
if(ret)
{
printk("申请gpio编号失败\n");
return ret;
}
printk("gpio_request succeed\n");
//设置gpio管脚输出模式
gpio_direction_output(gpiono[2],0);
//方法2.旧的gpio子系统
//获取并申请gpio编号
gpinum[0]=gpiod_get_from_of_node(node,"led1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpinum[0]))
{
printk("获取gpio编号失败\n");
//将错误码指针转换成错误码
return PTR_ERR(gpinum[0]);
}
printk("获取gpio编号成功\n");
//设置管脚输出模式
gpiod_direction_output(gpinum[0],0);
//主板led2
//获取并申请gpio编号
gpinum[1]=gpiod_get_from_of_node(node,"led2",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpinum[1]))
{
printk("获取gpio编号失败\n");
//将错误码指针转换成错误码
return PTR_ERR(gpinum[1]);
}
printk("获取gpio编号成功\n");
//设置管脚输出模式
gpiod_direction_output(gpinum[1],0);
//主板led3
//获取并申请gpio编号
gpinum[2]=gpiod_get_from_of_node(node,"led3",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpinum[2]))
{
printk("获取gpio编号失败\n");
//将错误码指针转换成错误码
return PTR_ERR(gpinum[2]);
}
printk("获取gpio编号成功\n");
//设置管脚输出模式
gpiod_direction_output(gpinum[2],0);
return 0;
}
static void __exit mycdev_exit(void)
{
int i=0;
for(i=0;i<3;i++)
{
//新版本卸载驱动
gpio_free(gpiono[i]);
//旧版本卸载驱动
gpiod_put(gpinum[i]);
}
//1.销毁设备节点信息
for(i=0; i<6; 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);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
.h文件
#ifndef __LED_H__
#define __LED_H__
typedef enum{
MYLED1,
MYLED2,
MYLED3,
LED1,
LED2,
LED3
}led_t; //扩展版
#define LED_ON _IOW('a',1,int)
#define LED_OFF _IOW('a',0,int)
//
int led_init(void);
#endif
测试文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "led.h"
char buf[128] ="";
int fd[6] = {-1};
int i=0;
int main(int argc, char const *argv[])
{
fd[0] = open("/dev/myled0", O_RDWR);
if (-1 == fd[0])
{
perror("open is error\n");
exit(1);
}
fd[1] = open("/dev/myled1", O_RDWR);
if (-1 == fd[1])
{
perror("open is error\n");
exit(1);
}
fd[2] = open("/dev/myled2", O_RDWR);
if (-1 == fd[2])
{
perror("open is error\n");
exit(1);
}
fd[3] = open("/dev/myled3", O_RDWR);
if (-1 == fd[3])
{
perror("open is error\n");
exit(1);
}
fd[4] = open("/dev/myled4", O_RDWR);
if (-1 == fd[4])
{
perror("open is error\n");
exit(1);
}
fd[5] = open("/dev/myled5", O_RDWR);
if (-1 == fd[5])
{
perror("open is error\n");
exit(1);
}
int whitch;
while (1)
{
whitch = LED1;
ioctl(fd[0],LED_ON);
sleep(1);
ioctl(fd[0],LED_OFF);
sleep(1);
whitch = LED2;
ioctl(fd[1],LED_ON);
sleep(1);
ioctl(fd[1],LED_OFF);
sleep(1);
whitch = LED3;
ioctl(fd[2],LED_ON);
sleep(1);
ioctl(fd[2],LED_OFF);
sleep(1);
ioctl(fd[3],LED_ON);
sleep(1);
ioctl(fd[3],LED_OFF);
ioctl(fd[4],LED_ON);
sleep(1);
ioctl(fd[4],LED_OFF);
ioctl(fd[5],LED_ON);
sleep(1);
ioctl(fd[5],LED_OFF);
}
for(i=0;i<6;i++)
{
close(fd[i]);
}
return 0;
}
实验现象