前面一章介绍了应用程序的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控制)相关推荐

  1. Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)

    文章目录 全系列传送门 引言 什么是unlocked_ioctl接口? unlocked_ioctl和read/write函数有什么相同和不同 unlocked_ioctl接口命令规则 命令的合成宏与 ...

  2. openwrt的luci应用ipk包开发(一)

    一.LUCI界面 OpenWrt 的界面其实就是网页界面,默认是由 uhttpd 服务器承载,之所以叫做 LUCI ,因为这是使用 Lua 脚本编写的控制界面,全称 Lua Unified Confi ...

  3. openwrt的luci应用ipk包开发(二)

    界面脚本与配置文件是对应关系,将关联的配置文件 /etc/config/samba4 内容贴出来,两相对照才能更准确的理解各个参数的意义. config sambaoption name 'OpenW ...

  4. openwrt的luci应用ipk包开发(三)

    luci配置页面修改配置文件 先看看network的配置文件: config interface 'loopback' option ifname 'lo'option proto 'static' ...

  5. nvidia linux路径,NVIDIA Jetson Linux驱动程序包开发人员指南 - 快速入门指南

    这里的信息旨在帮助您开始使用NVIDIA很快上手®Jetson™驱动程序支持包(L4T与Jetson开发包一起).(名称L4T源自" Linux for Tegra"的描述,Teg ...

  6. Linux下的C编程实战(开发平台搭建,文件系统编程,进程控制与进程通信编程,“线程”控制与“线程”通信编程,驱动程序设计,专家问答)

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来,Linux ...

  7. ZYNQ开发系列——hw_platform包和BSP包中的一些理解

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 ZYNQ开发系列--hw_platform包和BSP包中的一些理解 前言 hw_platform BSP包 前言 前面我们完成了PS输 ...

  8. 嵌入式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 驱动程序关键代码 ...

  9. linux设备驱动程序--gpio控制

    gpio驱动程序 上一章节linux设备驱动程序--创建设备节点章节主要介绍了linux字符设备驱动程序的框架,从这一章节开始我们讲解各种外设的控制,包括gpio,i2c,dma等等,既然是外设,那就 ...

  10. Shiny平台构建与R包开发(一)——ui布局

    本节为Shiny平台构建与R包开发教程的第一小节. Getting Started 初识Shiny时,了解其工作机理非常重要.下面的案例展示了一个最简单的Shiny APP的工作机理: #DO NOT ...

最新文章

  1. C/C++基础问题归集
  2. 【精品】【分享】盖茨留给职场工作者的十句警告
  3. 读取网络数据缓存在本地 流程图
  4. ABP(现代ASP.NET样板开发框架)系列之2、ABP入门教程
  5. Linux下PortSentry的配置
  6. MongoDB索引问题
  7. 为什么你申请信用卡总是被拒绝?是银行跟你有仇吗?
  8. linux内核分为子系统,Linux内核内存管理子系统分析【转】
  9. C++新手之详细介绍MFC
  10. Arduino文档阅读笔记-RFID工作原理及RC522模块介绍
  11. vue 循环tabs 标签页 组件_Vue render函数实战--实现tabs选项卡组件
  12. 朋友的身份证被骗子注册了支付宝开通了花呗,消费了三千被催债才知道花呗被盗如何处理?
  13. python多线程 _thread没有上lock时程序提前给你把可执行的都执行,不按代码编写顺序执行
  14. java socket 异步回调函数_浅谈socket同步和异步、阻塞和非阻塞、I/O模型
  15. Yii框架上传后展示图片
  16. Download ebook from Syngress Publishing
  17. MediaExtractor、MediaMuxer 分离和合成 mp4
  18. 威5创非凡,领航再出击 | 新华三集团成立五周年庆
  19. java兔子繁殖总数_Java 兔子繁殖迭代问题
  20. 河北官方:邯郸涉县致4死5伤煤气泄漏事故涉嫌瞒报

热门文章

  1. T141基于51单片机出租车计费器公里计数,Proteus设计,keil程序、课题设计
  2. RS锁存器,D锁存器、D触发器简介
  3. JS生成UUID唯一标识方法
  4. java poi dataformat_poi的data format可真不怎么样
  5. android手写计算器,手写计算器MyScript Calculator
  6. 泛函分析 04.03 有界线性算子 - 一致有界原则
  7. HTTP请求的过程和原理
  8. diabetes影响因子2017_DIABETES
  9. 【模电】0010 正弦波产生电路(RC正弦波振荡电路)
  10. Xvid 进行视频编码