三、openwrt嵌入式驱动设计

嵌入式内核系统结构

	1. 内核启动流程
	2. 系统调用
	 * 应用层想与内核层交互,要通过系统调用,应用层不能直接访问内核的内存
	 * 	内核层驱动--->提供接口(绑定在某些系统调用的功能下)---
	    -->别人调用时-->通过系统调用的接口绑定模块--->实现具体功能
	 *  钩子 -- 把系统调用传递到内核层的某个模块的一条路勾走
	3. 进程管理系统
	4. 内存管理系统(MMU)
	5. 字符设备驱动(按字节为单位读取)
		输入子系统
		V4L2子系统
		USB总线系统
		misc系统
		ADSL音频系统
		fb_mem 视频缓存系统
	6. 块设备驱动(选择目标所在的一块,把它放进内存,再选择要读的目标)
	7.  内核网络
			netfilter 监控层

内核驱动设计方法(LKM)

  • LKM : Lodaing Kernel Moudle 可加载内核模块设计
  • 入口函数 : module_init()
    • static int __init xxx(void)
  为什么使用 static ?

因为 static 在 C 语言里可以限定它的作用域,
从内核>角度来说,很容易发生重名,对于这种
重名函数,为了不>对它的命名空间作污染,
所以给定义的函数用来限定在一个作用域有效

为什么加 __init / __exit?

可加可不加,
linux为了方便管理所有驱动的入口/出口函数,linux会指定专门的内存区域把内核的所有驱动存到这个专门的区域里,即这个指定区域里都是驱动的指针和地址

  • 出口函数 : module_exit()
    • static void __exit xxx(void)
  • 驱动常用函数
  内核是否能用 glic 的函数(标准C库)?

不能,
因为内核启动的时候,所有的库还没有调用/启动起来,库是存在文件系统里,而文件系统在内核启动之后才挂接的,内核无法直接访问文件系统中的库,所以内核是没办法使用文件系统里的库的,所有的内核函数自己实现的

  • printk(KERN_INFO "Hello World enter \n");
    
  • 在kernel.h里,有调试级别比如在 printk 中要带调试级别
  • 优先级 : 0 -7 MAX 0, MINI 7
#define	KERN_EMERG	"<0>"	/* system is unusable			*/
#define	KERN_ALERT	"<1>"	/* action must be taken immediately	*/
#define	KERN_CRIT	"<2>"	/* critical conditions			*/
#define	KERN_ERR	"<3>"	/* error conditions			*/
#define	KERN_WARNING	"<4>"	/* warning conditions			*/
#define	KERN_NOTICE	"<5>"	/* normal but significant condition	*/
#define	KERN_INFO	"<6>"	/* informational			*/
#define	KERN_DEBUG	"<7>"	/* debug-level messages			*/
  •    kzmalloc ()
    
  •   MODULE_AUTHOR()  //指定模块的作者是谁
    
  •   MODULE_LICENSE("GPL v2")
    

//指定模块的license,很固定

  •   MODULE_DESCRIPTION() //模块详细描述
    
  •   MODULE_ALIAS() //简要描述
    
  • 如何传参 : 给内核驱动传参

  •   module_param(book_name,charp,S_IRUGO)
    
  • 第一个是传入参数的变量

  • 第二个是 第一个参数的数据类型

  • 第三个关于内核文件的权限值

  • S_IRUGO相关的参数,一般为S_IRUGO,只给用户可读

#ifndef _LINUX_STAT_H
#define _LINUX_STAT_H

#ifdef __KERNEL__

#include <asm/stat.h>

#endif

#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)

#define S_IFMT  00170000
#define S_IFSOCK 0140000
#define S_IFLNK	 0120000
#define S_IFREG  0100000
#define S_IFBLK  0060000
#define S_IFDIR  0040000
#define S_IFCHR  0020000
#define S_IFIFO  0010000
#define S_ISUID  0004000
#define S_ISGID  0002000
#define S_ISVTX  0001000

#define S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m)	(((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m)	(((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m)	(((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m)	(((m) & S_IFMT) == S_IFSOCK)

#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100

#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010

#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001

#endif

#ifdef __KERNEL__
#define S_IRWXUGO	(S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO		(S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO		(S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO		(S_IXUSR|S_IXGRP|S_IXOTH)
  •   book.c文件
    
#include <linux/init.h>
#include <linux/module.h>

static char *book_name = "dissecting Linux Device Driver";
module_param(book_name,charp,S_IRUGO);

static int book_num = 4000;
module_param(book_num, int ,S_IRUGO);


static int __init book_init(void)
{
	printk(KERN_INFO "BOOK name :%s\n",book_name);
	printk(KERN_INFO "BOOK num :%d\n",book_num);
	return 0;
}
module_init(book_init);

static void __exit book_exit(void)
{
	printk(KERN_INFO "book module exit\n ");
}
module_exit(book_exit);

MODULE_AUTHOR("tanzhou EDU");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple testing  Module");
MODULE_ALIAS("a simplest module");
MODULE_VERSION("V1.0");
  • 默认不给 .ko文件传参
    在这里插入图片描述
  • 给 .ko 文件传参
    在这里插入图片描述
  • 如何定义各类函数 :
  • 如何加载 :
  •    insmod  xxx.ko
    
  • 如何卸载 :
  •   	 rmmod xxx.ko
    
  • 如何调用 :
    <1> dmesg 查看内核有什么
    <2> 调用在xxx.ko 里使用module_init /
    module_exit修饰的函数
    在这里插入图片描述
    • make 完后生成 . ko文件在这里插入图片描述
    • 切换 root权限
    • 用 insmod hello.ko
    • 用dmesg -c 查看内核打印的内容在这里插入图片描述
      在这里插入图片描述
  • 用 rmmod hello.ko 卸载
  • 再用 dmesg -c 查看
  • 有时候执行上面 2 步操作后查看不到内核的打印的内容
  • 这是缓存的问题
  • 用 insmod hello.ko 重新加载,再用dmesg -c 查看即可
    在这里插入图片描述
  • 常用命令
  • modinfo xxx.ko //查看hello.ko的信息
    

在这里插入图片描述

  •   uname -a //查看内核版本号,编译环境的内核要与实物一致就可以
    

在这里插入图片描述

  • 调试方法:

如果内存写的不对,系统崩溃了,重启去做,要调试整个系统,因为内核要依靠系统来执行的,每个内核在加载系统中会有一个内核地址,加载之后,通过一个文件来读取这个地址,然后调试整个系统来断到这个地址上,然后调试刚加载自己加上的内核

  • 查看LEDE 的内核版本

  • 在lede里无法用uname -a 查看内核版本号,可在linux的 …/openwrt-17.01.4-17.01.4/package/kernel/linux/modules 里查看
    在这里插入图片描述

  • 下面用 other.mk 来作为例子创建内核驱动 (不需要改)

  • 如何建立 Makefile && 编译方法

      在.../openwrt-17.01.4-17.01.4/package/kernel/ 
      下创建文件夹 helloworld
    

在这里插入图片描述

  • 随便取 I2C的 顶层Makefile 来作为模板使用
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

PKG_NAME:=helloworld-kernel  //改的名字
PKG_RELEASE:=1

include $(INCLUDE_DIR)/package.mk

define KernelPackage/helloworld-kernel  //在make menuconfig里有用
  SUBMENU:=$(OTHER_MENU)		//在什么菜单生成
  TITLE:= Helloworld kernel drive //题头
//  DEPENDS:=@GPIO_SUPPORT +kmod-i2c-core +kmod-i2c-gpio //依赖
  FILES:=$(PKG_BUILD_DIR)/helloworld.ko //生成的文件名
  KCONFIG:=
  AUTOLOAD:=$(call AutoProbe,81,helloworld)  //81为装载的顺序,不写由系统定义
endef

define KernelPackage/helloworld-kernel/description
 Kernel moudule for test
endef

// EXTRA_KCONFIG:= \
//	CONFIG_I2C_GPIO_CUSTOM=m
// 内核在编译的时候有自己的config配置文件,有宏定义,引入到内核里去,没有就不写

EXTRA_CFLAGS:= \
	$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
	$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \

MAKE_OPTS:= \
	ARCH="$(LINUX_KARCH)" \
	CROSS_COMPILE="$(TARGET_CROSS)" \
	SUBDIRS="$(PKG_BUILD_DIR)" \
	EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
	$(EXTRA_KCONFIG)

	define Build/Prepare	//准备
	mkdir -p $(PKG_BUILD_DIR)/
	$(CP) -R ./src/* $(PKG_BUILD_DIR)
		modules
endef
	
define Build/Compile
	$(MAKE) -C "$(LINUX_DIR)" \
		$(MAKE_OPTS)  CONFIG_HELLOWORLD-KERNEL=m\
		modules
endef

$(eval $(call KernelPackage,helloworld))
  • 在 helloworld 文件夹下的src文件夹 创建Makefile
    在这里插入图片描述
  • 然后在 helloworld 文件夹下的src文件夹 创建 Kconfig
config HELLOWORLD-KERNEL
		tristate "test kernel driver"
	//	depends on GENERIC_GPIO  //依靠的文件
	//	select I2C_GPIO  //如果需要依靠文件,要加上select来选择
		help
			This is an Kernel Driver Test
			if unsure,delete it,just for test
  • 然后把他们放进linux的 …/openwrt-17.01.4-17.01.4/package/kernel/ helloworld 下

在这里插入图片描述

  • 这里的Kconfig一定要写,因为在编译内核文件的时候,编译器make会检测有没有这个文件
  • make menuconfig 后选择 Kernel modules,会发现有下面的选项
    在这里插入图片描述
  • 选择 M,保存退出在这里插入图片描述
  • make ./package/kernel/helloworld/compile V=s
  • 编译完成后在 /bin/packages/i386_entium4/packages
    有内核的文件
  • 在 staging_dir/target-i386_pentium4_musl-1.1.16/root-x86/lib/model里有 编译出来的 .ko 文件
  • 然后上传到LEDE安装使用
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值