26.1 前言
Linux2.6.35 之后就有了gpiolib,其作用为对所有GPIO进行统一管理,避免多个驱动控制一个IO所带来的混乱,需要在编译内核的时候选上支持gpiolib选项。本文是基于Linux gpiolib库初试GPIO驱动编程的学习记录,供学习参考。
26.2 混杂设备
混杂设备主设备号为10,次设备号:0-255共256个设备。minor=MISC_DYNAMIC_MINOR时,表示动态分配次设备号,我们不用管,其会自动分配空闲的次设备号。
26.3 驱动ictol参数构建
在编写ioctl函数时,我们需要构建一些命令用以执行不同操作。_IOWR(幻数,基数,变量),_IOW(幻数,基数,变量), _IOR(幻数,基数,变量), _IO (幻数,基数)。
(1)_IOWR:构建读写命令
(2)_IOW:构建带参数的写命令
(3)_IOR:构建读命令
(4)_IO:构建不带读写的普通命令
示例:
#define IGPIO_IOCTL_MAGIC 0XF12
#define IGPIO_IOCTL_RESET _IO(IGPIO_IOCTL_MAGIC, 0x0)
#define IGPIO_IOCTL_SET _IO(IGPIO_IOCTL_MAGIC, 0x1)
#define IGPIO_IOCSET _IOW(IGPIO_IOCTL_MAGIC, 0, 变量)
#define IGPIO_IOCGET _IOR(IGPIO_IOCTL_MAGIC, 1, 变量)
26.4 GPIOLib常用函数
使用gpio接口需要包含#include <linux/gpio.h> ,在驱动中使用延时函数mdelay,需要包含#include <linux/delay.h>文件
gpio_request(gpioNum,gpioName) | 申请一个空闲的GPIO |
gpio_free(gpioNum) | 释放申请到的GPIO |
gpio_direction_input(gpioNum) | 设置GPIO为输入模式 |
gpio_direction_output(gpioNum,value) | 设置GPIO为输出模式+值 |
gpio_get_value(gpioNum) | 获取GPIO的电平值 |
gpio_set_value(gpioNum,value) | 设置GPIO输出电平值 |
gpio_to_irq(gpioNum) | 返回GPIO对应的中断号 |
irq_to_gpio(irq) | 返回irq对应的GPIO号 |
26.5 GPIOLib实例编程
gpiodrv.c
#include <linux/init.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/sys_config.h>
#include <linux/miscdevice.h>
#define IGPIO_DEVICE_NAME "igpioDrv"
#define IGPIO_IOCTL_MAGIC 0XF12
#define IGPIO_IOCTL_RESET _IO(IGPIO_IOCTL_MAGIC, 0x0)
#define IGPIO_IOCTL_SET _IO(IGPIO_IOCTL_MAGIC, 0x1)
static DEFINE_MUTEX(igpio_ioctlmutex); /* 声明并初始化互斥锁 */
static int gpioNumer = 0; /*用以保存申请到的io*/
static int igpio_open(struct inode *node, struct file *filp)
{
return 0;
}
static long igpio_ioctl(struct file *filp, unsigned int cmd, unsigned long gpioNum)
{
int nRet = 0;
printk(KERN_EMERG "cmd= %d,gpioNum= %d\n",cmd,gpioNum);
mutex_lock(&igpio_ioctlmutex);
if(gpioNumer != gpioNum)
{
nRet = gpio_request(gpioNum, NULL);
if(nRet){
printk(KERN_EMERG "%d gpio_request failed\n", gpioNum);
nRet = -EINVAL;
goto iExit;
}
gpio_free(gpioNumer); //如果申请了新的那就释放掉旧的
gpioNumer = gpioNum;
}
switch(cmd){
case IGPIO_IOCTL_RESET:
gpio_direction_output(gpioNumer,0x00);
break;
case IGPIO_IOCTL_SET:
gpio_direction_output(gpioNumer,0x01);
break;
default:
nRet = -EINVAL;
}
iExit:
mutex_unlock(&igpio_ioctlmutex);
return nRet;
}
static const struct file_operations gpio_fops ={
.owner = THIS_MODULE,
.open = igpio_open,
.unlocked_ioctl = igpio_ioctl,
};
static struct miscdevice gpioMiscDevSt ={
.minor = MISC_DYNAMIC_MINOR,
.name = IGPIO_DEVICE_NAME,
.fops = &gpio_fops,
};
static int __init gpioDrv_init(void)
{
int nRet = 0;
/*注册设备*/
nRet = misc_register(&gpioMiscDevSt);
printk(KERN_EMERG "%s misc_deregister nRet=%d \n",__FUNCTION__,nRet);
return nRet;
}
static void __exit gpioDrv_exit(void)
{
int nRet = -1;
gpio_free(gpioNumer);
nRet = misc_deregister(&gpioMiscDevSt);
if(nRet<0)
printk(KERN_EMERG "%s misc_deregister failed\n",__FUNCTION__);
}
module_init(gpioDrv_init);
module_exit(gpioDrv_exit);
MODULE_LICENSE("GPL");
iotest.c
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#define IGPIO_IOCTL_MAGIC 0XF12
#define IGPIO_IOCTL_RESET _IO(IGPIO_IOCTL_MAGIC, 0x0)
#define IGPIO_IOCTL_SET _IO(IGPIO_IOCTL_MAGIC, 0x1)
int main(int argc, char **argv)
{
unsigned int id = 0;
unsigned int value = 0;
int fd = -1;
int ret;
if(argc <4){
printf("Usage:./app -s 194 0\n");
return -1;
}
sscanf(argv[2], "%d", &id);
sscanf(argv[3], "%x", &value);
fd = open("/dev/igpioDrv", O_RDWR, 0);
if (fd < 0) {
printf("open fail\n");
return -1;
}
if (0 == strncmp(argv[1], "-s", strlen(argv[1])))
{
if(value == 0)
ret = ioctl(fd, IGPIO_IOCTL_RESET, id);
else
ret = ioctl(fd, IGPIO_IOCTL_SET, id);
if (ret < 0) {
perror("ioctl fail\n");
}
}
close(fd);
return 0;
}
Makefile
ifneq ($(KERNELRELEASE),) # linux内核源代码中的顶层makefile中有定义
obj-m += gpiolibdrv.o
else
ARCH = arm
CROSS_COMPILER = $(CROSS_COMPILE)
CC = $(CROSS_COMPILER)gcc
LD = $(CROSS_COMPILER)ld
CURRENT_PATH := $(shell pwd)
KERNEL_PATH := ../../kernel/linux-3.10
CFLAGS+= -O2 -Wall
all:
make -C $(KERNEL_PATH) M=$(CURRENT_PATH) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILER) modules
clean:
rm -rf .tmp_versions
rm -rf *.o *.ko *.mod.* test
rm -rf .*.cmd*
rm -rf modules* Module*
endif