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:=1include $(INCLUDE_DIR)/package.mkdefine KernelPackage/gpio_control_driverSUBMENU:=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/descriptionKernel module to control gpios for JS9331 and JS7628
endefEXTRA_KCONFIG:= \CONFIG_GPIO_CONTROL_DRIVER=m
编译准备,包括了创建目录,拷贝文件,
define Build/Preparemkdir -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 0case GPIO_CONTROL_GET_DIRECTION:printk("command: GPIO_CONTROL_GET_DIRECTION\n");break;
#endifcase 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 registeredmisc_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位新冠,还是从北京过去的,今天开始又要排查好多单位好多人了
听说国外又发现了更凶残的病毒变种
赶紧带口罩吧。
openwrt开发--驱动程序IPK包开发(GPIO控制)相关推荐
- Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)
文章目录 全系列传送门 引言 什么是unlocked_ioctl接口? unlocked_ioctl和read/write函数有什么相同和不同 unlocked_ioctl接口命令规则 命令的合成宏与 ...
- openwrt的luci应用ipk包开发(一)
一.LUCI界面 OpenWrt 的界面其实就是网页界面,默认是由 uhttpd 服务器承载,之所以叫做 LUCI ,因为这是使用 Lua 脚本编写的控制界面,全称 Lua Unified Confi ...
- openwrt的luci应用ipk包开发(二)
界面脚本与配置文件是对应关系,将关联的配置文件 /etc/config/samba4 内容贴出来,两相对照才能更准确的理解各个参数的意义. config sambaoption name 'OpenW ...
- openwrt的luci应用ipk包开发(三)
luci配置页面修改配置文件 先看看network的配置文件: config interface 'loopback' option ifname 'lo'option proto 'static' ...
- nvidia linux路径,NVIDIA Jetson Linux驱动程序包开发人员指南 - 快速入门指南
这里的信息旨在帮助您开始使用NVIDIA很快上手®Jetson™驱动程序支持包(L4T与Jetson开发包一起).(名称L4T源自" Linux for Tegra"的描述,Teg ...
- Linux下的C编程实战(开发平台搭建,文件系统编程,进程控制与进程通信编程,“线程”控制与“线程”通信编程,驱动程序设计,专家问答)
Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来,Linux ...
- ZYNQ开发系列——hw_platform包和BSP包中的一些理解
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 ZYNQ开发系列--hw_platform包和BSP包中的一些理解 前言 hw_platform BSP包 前言 前面我们完成了PS输 ...
- 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十五)驱动程序基石
文章目录 1.1 休眠与唤醒 1.1.1 适用场景 1.1.2 内核函数 1.1.2.1 休眠函数 1.1.2.2 唤醒函数 19.1.3 驱动框架 1.1.4 编程 1.1.4.1 驱动程序关键代码 ...
- linux设备驱动程序--gpio控制
gpio驱动程序 上一章节linux设备驱动程序--创建设备节点章节主要介绍了linux字符设备驱动程序的框架,从这一章节开始我们讲解各种外设的控制,包括gpio,i2c,dma等等,既然是外设,那就 ...
- Shiny平台构建与R包开发(一)——ui布局
本节为Shiny平台构建与R包开发教程的第一小节. Getting Started 初识Shiny时,了解其工作机理非常重要.下面的案例展示了一个最简单的Shiny APP的工作机理: #DO NOT ...
最新文章
- C/C++基础问题归集
- 【精品】【分享】盖茨留给职场工作者的十句警告
- 读取网络数据缓存在本地 流程图
- ABP(现代ASP.NET样板开发框架)系列之2、ABP入门教程
- Linux下PortSentry的配置
- MongoDB索引问题
- 为什么你申请信用卡总是被拒绝?是银行跟你有仇吗?
- linux内核分为子系统,Linux内核内存管理子系统分析【转】
- C++新手之详细介绍MFC
- Arduino文档阅读笔记-RFID工作原理及RC522模块介绍
- vue 循环tabs 标签页 组件_Vue render函数实战--实现tabs选项卡组件
- 朋友的身份证被骗子注册了支付宝开通了花呗,消费了三千被催债才知道花呗被盗如何处理?
- python多线程 _thread没有上lock时程序提前给你把可执行的都执行,不按代码编写顺序执行
- java socket 异步回调函数_浅谈socket同步和异步、阻塞和非阻塞、I/O模型
- Yii框架上传后展示图片
- Download ebook from Syngress Publishing
- MediaExtractor、MediaMuxer 分离和合成 mp4
- 威5创非凡,领航再出击 | 新华三集团成立五周年庆
- java兔子繁殖总数_Java 兔子繁殖迭代问题
- 河北官方:邯郸涉县致4死5伤煤气泄漏事故涉嫌瞒报