openwrt开发--驱动程序IPK包开发(GPIO控制)

前面一章介绍了应用程序的IPK包开发,今天简单的提供一个驱动程序的IPK包范例,并且提供用户侧的调用代码,实现对GPIO的驱动。主要看的还是这个过程,即如何编译一个IPK的驱动包,至于怎么开发更复杂的驱动程序
在这里插入图片描述
别问,问就是正在学习。

代码结构

首先来看一下代码结构,对照APP开发,简单多了,因为没有页面那些东西。

package/kernel/gpio_control_driver/
├── Makefile  生成IPK包的Makefile
└── src
    ├── gpio_control_driver.c	驱动模块主文件
    ├── gpio_control_driver.h	驱动模块头文件
    └── Makefile				内部编译Makefile

各部分分解一下子
在这里插入图片描述

1.外部的Makefile

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

模块名称
PKG_NAME:=gpio_control_driver
PKG_RELEASE:=1

include $(INCLUDE_DIR)/package.mk

define KernelPackage/gpio_control_driver
  SUBMENU:=Other modules  所属内核子目录,在menuconfig的时候的选项位置
  DEPENDS:=@GPIO_SUPPORT  依赖模块,需要GPIO模块支持
  TITLE:=Driver for JS9331/JS7628 gpios control  模块标题,在在menuconfig的时候的选项名称
  FILES:=$(PKG_BUILD_DIR)/gpio_control_driver.ko  模块名称
  KCONFIG:=
  AUTOLOAD:=$(call AutoLoad,30,gpio_control_driver)  开启自动加载模块
endef

描述信息
define KernelPackage/gpio_control_driver/description
 Kernel module to control gpios for JS9331 and JS7628
endef

EXTRA_KCONFIG:= \
        CONFIG_GPIO_CONTROL_DRIVER=m
编译准备,包括了创建目录,拷贝文件,
define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef

编译过程
define Build/Compile
	$(MAKE) -C "$(LINUX_DIR)" \
		CROSS_COMPILE="$(TARGET_CROSS)" \
		ARCH="$(LINUX_KARCH)" \
		SUBDIRS="$(PKG_BUILD_DIR)" \
		EXTRA_CFLAGS="$(BUILDFLAGS)" \
		$(EXTRA_KCONFIG)
endef

执行编译过程
$(eval $(call KernelPackage,gpio_control_driver))

这里只是简单介绍了一下各行的作用,细节的内容太多了,大部分参数都需要去这些文件

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk

中寻找答案。
在这里插入图片描述

2.src中的Makefile

obj-${CONFIG_GPIO_CONTROL_DRIVER}     += gpio_control_driver.o

这里只需要定义一下中间文件。其他过程会自动完成。
在这里插入图片描述

3.主函数文件

#include <linux/types.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/hrtimer.h>
#include <linux/stat.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <asm-generic/errno-base.h>
#include <linux/miscdevice.h>


#include "gpio_control_driver.h"

static int gpio_control_open(struct inode *pinode, struct file *pfile)
{
	printk("***%s***\n",__func__);
	return 0;
}

static int gpio_control_release(struct inode *pinode, struct file *pfile)
{
	printk("***%s***\n",__func__);
	return 0;
}

核心函数
static long gpio_control_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
	int ret;
	unsigned char gpio_number;
	unsigned char gpio_value;

	printk("***%s***\n",__func__);
	printk("cmd:0x%02X arg:0x%04X\n",cmd, arg);

	gpio_number = GET_GPIO_NUM(arg);
	gpio_value  = GET_GPIO_VALUE(arg);
	printk("gpio number:%d\n", gpio_number);
	printk("gpio value:0x%02X\n", gpio_value);

	switch (cmd)
	{
	case GPIO_CONTROL_SET_OUT:
		printk("command: GPIO_CONTROL_SET_OUT\n");
		ret = gpio_direction_output(gpio_number, gpio_value);
		if (ret < 0)
		{
			printk("###gpio_direction_output ERROR: can't set gpio %d output###\n", gpio_number);
			return -1;
		}
		printk("command: GPIO_CONTROL_SET_OUT done\n");
		break;

	case GPIO_CONTROL_SET_IN:
		ret = gpio_direction_input(gpio_number);
		if (ret < 0)
		{
			printk("###gpio_direction_input ERROR: can't set gpio %d input###\n", gpio_number);
			return -1;
		}
		printk("command: GPIO_CONTROL_SET_IN\n");
		break;
#if 0
	case GPIO_CONTROL_GET_DIRECTION:

		printk("command: GPIO_CONTROL_GET_DIRECTION\n");
		break;
#endif
	case GPIO_CONTROL_SET_VALUE:
		gpio_set_value(gpio_number, gpio_value);
		printk("command: GPIO_CONTROL_SET_VALUE\n");
		break;

	case GPIO_CONTROL_GET_VALUE:
		ret = gpio_get_value(gpio_number);
		if (ret < 0){
			printk("###gpio_get_value ERROR: can't get gpio %d value###\n", gpio_number);
			return -1;
		}
		printk("command: GPIO_CONTROL_GET_VALUE\n");
		break;

	case GPIO_CONTROL_REQUEST_GPIO:
		printk("command: GPIO_CONTROL_REQUEST_ONE\n");
		if (0 > gpio_request(gpio_number, "gpio_ctrl")){
			printk("###gpio_request ERROR: can't request %d pin for output###\n", gpio_number);
			return -1;
		}
		printk("command: GPIO_CONTROL_REQUEST_GPIO done\n");
		break;

	case GPIO_CONTROL_FREE_GPIO:
		gpio_free(gpio_number);
		printk("command: GPIO_CONTROL_FREE_GPIO done\n");
		break;

	default:
		printk("***Unknown command:0x%02X\n***\n", cmd);
		break;

	}

	return 0;
}

static const struct file_operations gpio_control_ops = {
		.owner 			= THIS_MODULE,
		.open			= gpio_control_open,
		.release		= gpio_control_release,
		.unlocked_ioctl	= gpio_control_ioctl,
};

static struct miscdevice s_gpio_control_dev = {
		.minor = MISC_DYNAMIC_MINOR,
		.fops = &gpio_control_ops,
		.name = GPIO_CONTROL_DEV_NAME
};

模块加载
static int gpio_control_init(void)
{
	int result;
	result = misc_register(&s_gpio_control_dev);
	if (result != 0) 
	{
		printk("###misc_register error###\n");
		return -1;
	}

	printk("**gpio_control module initiation OK**\n");
	return result;
}

模块退出
void gpio_control_exit(void)
{
	//unregister what we registered
	misc_deregister(&s_gpio_control_dev);

	printk("**gpio_control module exit**\n");
}

module_init(gpio_control_init);
module_exit(gpio_control_exit);

MODULE_VERSION("V1.0");
MODULE_AUTHOR("wurobinson <wurobinson@zhuotk.com>");
MODULE_LICENSE("Dual BSD/GPL");

这里我们选择使用的设备类型是 --混杂设备,因为它只是驱动一个GPIO,不像那些字符设备,块设备那么复杂,用户只是需要用ioctl即可控制,不需要读写操作。

在Linux驱动中把无法归类的五花八门的设备定义为混杂设备(用miscdevice结构体表述)。miscdevice共享一个主设备号MISC_MAJOR(即10),但次设备号不同。 所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。 在内核中用struct miscdevice表示miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。miscdevice的API实现在drivers/char/misc.c中。

根据代码

static const struct file_operations gpio_control_ops = {
		.owner 			= THIS_MODULE,
		.open			= gpio_control_open,
		.release		= gpio_control_release,
		.unlocked_ioctl	= gpio_control_ioctl,
};

其中可以看出 open和release都没做操作,其实也不需要操作。关键的函数就在于
gpio_control_ioctl中,里面负责实现了gpio方向及值的配置,主要涉及到了这几个函数,看名字也都能看出来是什么功能。

gpio_direction_output(gpio_number, gpio_value);
gpio_direction_input(gpio_number);
gpio_set_value(gpio_number, gpio_value);

4.头文件


#ifndef GPIO_CONTROL_DRIVER_H_
#define GPIO_CONTROL_DRIVER_H_

#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/kfifo.h>

#define GET_GPIO_NUM(arg1) 		(unsigned char)((arg1 >> 24) & 0xff)
#define GET_GPIO_VALUE(arg1) 	(unsigned char)((arg1 >> 16) & 0xff)

#define GPIO_CONTROL_MAJOR 					99//device major number


#define GPIO_CONTROL_DEV_NAME		"gpio_control"

//IOCTRL CMDs
#define GPIO_CONTROL_SET_OUT			0x01
#define GPIO_CONTROL_SET_IN				0x02
//#define GPIO_CONTROL_GET_DIRECTION	0x03
#define GPIO_CONTROL_SET_VALUE			0x04
#define GPIO_CONTROL_GET_VALUE			0x05
#define GPIO_CONTROL_REQUEST_GPIO		0x06
#define GPIO_CONTROL_FREE_GPIO			0x07

#endif /* GPIO_CONTROL_DRIVER_H_ */

编译

整个目录以文件夹形式拷贝到/package/kernel/路径下,然后进行

make menuconfig

进入配置页面,选上如下配置
Kernel modules —>
Other modules —>
kmod-gpio_control_driver… Driver for JS9331/JS7628 gpios control
保存退出,执行

make V=s

重新编译源码,就可以得到安装包

bin/targets/ar71xx/generic/packages/kmod-gpio_control_driver_4.4.79-1_mips_24kc.ipk 

拷贝至开发板,执行安装命令

opkg install kmod-gpio_control_driver_4.4.79-1_mips_24kc.ipk 

即可完成安装。并且可以查看到内核模块挂载。
在这里插入图片描述
到这里内核驱动的部分就完成了。下面是试用过程。

用户侧程序

内核驱动加载之后,需要在用户侧进行设备操作。简单写了一个控制程序

int main(int argc,char **argv)
{
    int fd = 0;
	unsigned int cmd = 0;
	unsigned int value = 0;
	if( argc != 3 )
    {
        printf( "usage: %s cmd value \n", basename( argv[0] ) );
        return 1;
    }
	cmd=atoi(argv[1]);
	value = atoi(argv[2]);
	printf("cmd :0x%04x value:0x%04x \n",cmd,value);
 
     /*打开设备文件*/
    fd = open("/dev/gpio_control", O_RDWR);
	if(fd <0)
	{
		printf("open dev error \n");
	}
	else
	{
		printf("open dev success \n");
	}

	ioctl(fd,cmd,value);
    /*关闭设备*/
    close(fd);
    return 0;    
}

然后编译成目标板的可执行程序,编译过程可以参照前面的APP开发,也可以使用交叉编译工具直接编译出程序,拷贝到开发板中。

然后,如果我要控制GPIO22端口为输出高电平。则执行

root@ZhuoTK:/tmp# ./gpio_control_user 1 369164288
cmd :0x0001 value:0x16010000 
open dev success 

内核打印
[  451.423441] ***gpio_control_open***
[  451.426940] ***gpio_control_ioctl***
[  451.430422] cmd:0x01 arg:0x16010000
[  451.433993] gpio number:22
[  451.436597] gpio value:0x01
[  451.439358] command: GPIO_CONTROL_SET_OUT
[  451.443382] command: GPIO_CONTROL_SET_OUT done
[  451.448710] ***gpio_control_release***

通过使用万用表就可以测量到高电平的输出了,搞定。
在这里插入图片描述

结束语

昨天开始,上海又出现了3位新冠,还是从北京过去的,今天开始又要排查好多单位好多人了在这里插入图片描述
听说国外又发现了更凶残的病毒变种
在这里插入图片描述
赶紧带口罩吧。
在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖哥王老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值