GPIO子系统的API
1.解析设备树节点
struct device_node *of_find_node_by_path(const char *path)
功能:通过节点路径获取设备节点
参数:
path:节点路径("/mynode@0x12345678")
返回值:成功返回指向设备节点信息结构体空间的指针,失败返回NULL#include<linux/of_gpio.h>
2.获取GPIO编号
int of_get_named_gpio(struct device_node *np,
const char *propname, int index)
功能:根据gpio设备节点获取gpio编号
np:设备树节点指针(node_p)
propname:键名
index:索引号
返回值:成功返回gpio编号,失败返回错误码#include<linux/gpio.h>
3.int gpio_request(unsigned gpio, const char *label)
功能:申请要使用的gpio编号(要使用得到的gpio号,需要先申请)
参数:
gpio:gpio编号
label:不用,填NULL返回值:成功返回0,失败返回错误码
4. int gpio_direction_input(unsigned gpio)
功能:设备引脚为输入模式(direction:方向)
参数:GPIO编号
返回值:成功返回0,失败返回错误码
5.int gpio_direction_output(unsigned gpio, int value)
功能:设备引脚为输出模式
参数:
gpio:GPIO编号
value:1(高电平) 0(低电平)
返回值:成功返回0,失败返回错误码6.void gpio_set_value(unsigned gpio, int value)
功能:设备引脚输出的电平值
参数:
gpio:GPIO编号
value:1:高电平 0(低电平)7.int gpio_get_value(unsigned gpio)
功能:获取gpio电平值
参数:gpio编号
返回值:0(低电平) 1(高电平)8.void gpio_free(unsigned gpio)
功能:释放gpio编号
参数:目标gpio编号
ioctlAPI
系统调用函数:
#include <sys/ioctl.h>int ioctl(int fd, unsigned long request, ...);
功能:通过功能码实现设备的控制
参数:
fd:设备文件的文件描述符
request:功能码
...:可以写参数,也可以不写,写参数的时候写地址
返回值:成功返回0,失败返回错误码
An ioctl() request has encoded in it whether the argument is an in parameter or out
parameter, and the size of the argument argp in bytes.
struct file_operations {
long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long arg);
//将应用层request传递给cmd,
//将应用层的...传给arg
};
ioctl功能代码
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
dir<<30|size<<16|type<<8|nr<<0#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))//如果想要得到一个具有某种特定含义的功能码只需要_IO\_IOW\_IOR\_IOWR就可以得到
ex:以灯的开和关为例
#define LED_ON _IOW('a',1,int)
LED_ON 01 00000000000100 01100001 00000001
#define LED_OFF _IOW('a',0,int)
LED_OFF 01 00000000000100 01100001 00000000
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include "myled.h"
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/of_gpio.h>
#define CNAME "myled"
int major; //定义变量接受主设备号
char kbuf[128] = {}; //定义数组用于存放和用户之间拷贝的数据
struct class *cls;//句柄
struct device *dev;
//定义一个指向设备节点的指针
struct device_node *node;
struct property *pr; //属性结构体指针
struct gpio_desc *desc;
//给定时器分配对象
struct timer_list mytimer;
//定时器处理函数
void timer_handler(struct timer_list *timer)
{
//电位翻转
gpiod_set_value(desc, !gpiod_get_value(desc));
//再次使用定时器
mod_timer(&mytimer, jiffies+HZ);
}
long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret, witch;
ret = copy_from_user(&witch, (void*)arg, sizeof(int)); if(ret)
{
printk("拷贝失败\n");
return -EIO;
}
switch (witch)
{
case 1:
//获取gpio编号
desc = gpiod_get_from_of_node(node, "led1", 0, GPIOD_OUT_LOW, NULL);
if (IS_ERR(desc))
{
printk("获取gpio编号失败\n");
return PTR_ERR(desc);
}
// printk("获取gpio编号成功\n");
//添加定时器
add_timer(&mytimer);
break;
case 2:
//获取gpio编号
desc = gpiod_get_from_of_node(node, "led2", 0, GPIOD_OUT_LOW, NULL);
if (IS_ERR(desc))
{
printk("获取gpio编号失败\n");
return PTR_ERR(desc);
}
// printk("获取gpio编号成功\n");
//添加定时器
add_timer(&mytimer);
break;
case 3:
//获取gpio编号
desc = gpiod_get_from_of_node(node, "led3", 0, GPIOD_OUT_LOW, NULL);
if (IS_ERR(desc))
{
printk("获取gpio编号失败\n");
return PTR_ERR(desc);
}
// printk("获取gpio编号成功\n");
//添加定时器
add_timer(&mytimer);
break;
default:
printk("功能码错误\n");
break;
}
return 0;
}
struct file_operations fops=
{
.unlocked_ioctl = ioctl,
};
//入口函数
static int __init mycdev_init(void)
{
//根据设备节点路径获取设备节点信息
node = of_find_node_by_path("/myleds");
if (node == NULL)
{
printk("获取节点信息失败\n");
return ENODATA;
}
printk("获取节点信息成功\n");
//动态注册字符设备驱动
major=register_chrdev(0,CNAME,&fops);
if(major<0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功major=%d\n",major);
//向上提交节点目录
cls=class_create(THIS_MODULE,"LED");
if(IS_ERR(cls))
{
printk("向上提交目录失败\n");
return PTR_ERR(cls);
}
printk("向上提交目录成功\n");
//创建设备节点
dev=device_create(cls,NULL,MKDEV(major,0),NULL,"myled");
if(IS_ERR(dev))
{
printk("创建节点失败\n");
return PTR_ERR(dev);
}
printk("创建节点成功\n");
//初始化定时器
mytimer.expires = jiffies + HZ; //定时1s
timer_setup(&mytimer, timer_handler, 0);
return 0;
}
//出口函数
static void __exit mycdev_exit(void)
{
//删除定时器
del_timer(&mytimer);
//释放gpio编号
gpiod_set_value(desc, 0);
gpiod_put(desc);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
#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 "myled.h"
int main(int argc, char const *argv[])
{
int fd1 = open("/dev/myled",O_RDWR); //打开设备文件
int which;
if(fd1 < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
printf("设备文件打开成功\n");
//在终端输入
while(1)
{
printf("请选择要操作:\n1-->led1\n2-->led2\n3-->led3\n");
scanf("%d", &which);
getchar();
if(which==1 || which==2 || which==3)
ioctl(fd1, 0, &which); //输入为1、2、3时,把输入的数用which传入内核
}
close(fd1);
return 0;
}