嵌入式系统移植-06第三方驱动移植(驱动编译进内核Makefile,图像化配置Kconfig,驱动模块,黑白盒对比)

文章目录

  • 第三方驱动 黑盒移植
    • 驱动编译进内核Makefile
      • 选择驱动存放目录 (或任意目录)
      • 改Makefile让驱动编译进内核(同步修改,对应目录下的Makefile)
      • 改Kconfig (界面可配置)
        • 测试驱动
        • 创建设备文件
    • 图形化配置Kconfig
      • make menuconfig、Makefile、Kconfig、.config 三者的关系
      • 配置Kconfig
        • 在Kconfig中添加一个灯的选项
        • 修改Makefile
        • 在图形界面选择不编译LED驱动,看是否成功
    • 编译驱动为独立的模块
      • C程序代码编写
      • Makefile 编写
      • 编译出 ko 模块
      • 配置为模块方式
      • make modules 编译所有的模块
      • 在linux运行时,可以装载和卸载模块
        • 拷贝LED驱动模块到根文件系统
        • 通过 手动加载模块 insmod *.ko
        • 创建设备节点(应用访问驱动的入口)
        • 运行测试驱动的应用程序
  • 白盒移植
    • 黑白盒对比
    • 驱动框架
      • 字符设备框架
        • 字符设备引入
        • 字符设备驱动相关
        • LED驱动代码—fs4412_led_drv.c
      • 平台设备框架
        • 平台设备引入
  • 参考

第三方驱动 黑盒移植

在这里插入图片描述

驱动编译进内核Makefile

1、首先找内核中是否已经支持设备的驱动  选配
2、内核中没有的驱动移植进来 --第三方驱动移植

将第三方驱动代码放到linux源码树中的driver目录中
修改 Makefile Kconfig(界面配置) 
修改过的和新添加的代码会重新编译
程序需要在板子上运行 就需要使用交叉编译工具编译
mknod /dev/led c或者b  主设备号  次设备号

选择驱动存放目录 (或任意目录)

拷贝LED驱动程序至drivers目录
LED属于字符设备,所以放在drivers/char/目录下
在这里插入图片描述

改Makefile让驱动编译进内核(同步修改,对应目录下的Makefile)

在这里插入图片描述
在这里插入图片描述

改Kconfig (界面可配置)

测试驱动

烧写镜像至开发板
在这里插入图片描述
驱动程序测试代码

#include 
#include 
#include 
#include 
#include 

#define LED_MAGIC 'L'
#define LED_ON  _IOW(LED_MAGIC, 1, int)
#define LED_OFF _IOW(LED_MAGIC, 2, int)

int main(int argc, char **argv)
{
	int fd;
	
	fd = open("/dev/led", O_RDWR);
	if (fd < 0) {
		perror("open");
		exit(1);
	}
	
	printf("open led ok\n");
	
	//实现LED灯闪烁
	while(1)
	{
	  ioctl(fd, LED_ON); //点亮灯
	  usleep(100000);
	  ioctl(fd, LED_OFF); //灭灯
	  usleep(100000);
	}
	
	return 0;
}

编译并拷贝至根文件系统
在这里插入图片描述
在开发板上运行该测试程序
在这里插入图片描述
分析这个打印信息的来源
在这里插入图片描述
需要创建设备文件

创建设备文件

在这里插入图片描述

mknod /dev/led c 501 0

在这里插入图片描述
创建好设备文件后,再运行测试程序,就可以看到LED灯在闪烁了

图形化配置Kconfig

make menuconfig、Makefile、Kconfig、.config 三者的关系

menuconfig 运行的时候,会读取内核下面所有的Kconfig,根据Kconfig生成对应的图形界面
Makfile 具体编译文件
Kconfig 选配,生成图形化界面(在界面中显示一些选项)
	在Kconfig中添加一个灯的选项
	界面修改好了, 还需要再次修改 Makfile
	[] 只有两种选项编译(y)或者不编译(n)  
	<> 三种选项 编译(y)、不编译(n)或者编译成模块(m)
	obj-y obj-n obj-$(CONFIG_XXX)
方便驱动管理

配置Kconfig

在Kconfig中添加一个灯的选项

在这里插入图片描述

linux@linux:~/linux-3.14.79$ make menuconfig

可以看到图形界面中多了一个对应的选项
在这里插入图片描述

修改Makefile

在这里插入图片描述

在图形界面选择不编译LED驱动,看是否成功

在这里插入图片描述
没有编译LED驱动

在这里插入图片描述

开发板上电重启,也可以测试出LED驱动没有被编译进来

在这里插入图片描述

编译驱动为独立的模块

驱动模块学习 详细视频
linux 内核模块 http://www.makeru.com.cn/live/1392_586.html
驱动初级也会详细讲解

C程序代码编写

创建驱动程序的文件夹,将文件拷贝进来,并为之编写Makfile
在这里插入图片描述

Makefile 编写

在这里插入图片描述

编译出 ko 模块

  • make之后,会自动跳到内核里面,借用内核的Makefile把当前驱动程序编译成ko文件
  • 在这里插入图片描述

配置为模块方式

在这里插入图片描述
在这里插入图片描述

make modules 编译所有的模块

在这里插入图片描述

在linux运行时,可以装载和卸载模块

拷贝LED驱动模块到根文件系统

在这里插入图片描述

通过 手动加载模块 insmod *.ko

卸载是rmmod
在这里插入图片描述

创建设备节点(应用访问驱动的入口)

在这里插入图片描述

运行测试驱动的应用程序

在这里插入图片描述

白盒移植

  1. 打印跟踪
    所谓白盒移植,就是在驱动代码中加一些打印信息(printk)来跟踪
  2. 驱动框架
    要加打印信息,就要熟悉框架是怎样的

黑白盒对比

黑盒移植
	两种方式
		1. 驱动编译进内核
			选则源码目录
			修改Makefile
			修改Kconfig
			
		2. 将驱动编译成独立的模块
			配置为模块方式 通过修改Kconfig
			使用make modules 编译为模块
			装载模块 insmod xxx.ko
			创建设备结点 mknod /dev/xxx c xx xx
			运行测试驱动的应用程序
白盒移植
	需要阅读阅读源码,熟悉驱动框架
	1. 字符设备   2. 平台设备
		字符设备框架
								application
		User Mode					||
									\/
		--------------------------------------------------	
							System Call Interface
									||
		Kernel Mode				\/
							Virture File System(VFS)
								|      |     |
						   Character Block  Network
								|      |     |
							Device interface
									||
									\/
		-------------------------------------------------
		Hardware		Physical Device (Hardware)

在这里插入图片描述

驱动框架

字符设备框架

在这里插入图片描述

字符设备引入

1.一切设备皆文件
	对设备的操作的就是 对设备文件的 read write
2. open read write ioctl 
3. 将设备进行编号 设备号(主次设备号组成)

字符设备驱动相关

1. 注册获取设备号
2. 初始化设备
3. 操作设备 file_operations		-- open release read write ioctl...
4. 两个宏定义 module_init  module_exit  两个命令 insmod   rmmod
5. 注册设备号  register_chrdev_region
6. cdev_init 初始化字符设备
7. cdev_add 添加字符设备到系统

驱动是被动调用的,是被应用程序触发的 
应用程序的 open  调用到驱动的file_operations 的 open
应用程序的 read  调用到驱动的file_operations 的 read
应用程序的 write 调用到驱动的file_operations 的 write
应用程序的 ioctl 调用到驱动的file_operations 的 ioctl
应用程序的 close 调用到驱动的file_operations 的 release

LED驱动代码—fs4412_led_drv.c

#include 
#include 
#include 	
#include 
#include 


#define LED_MAGIC 'L'
#define LED_ON  _IOW(LED_MAGIC, 1, int)
#define LED_OFF _IOW(LED_MAGIC, 2, int)

#define LED_MA   501
#define LED_MI    0
#define LED_NUM   1

#define LED_CON 0x11000C20
#define LED_DAT 0x11000C24

struct cdev cdev;

unsigned int *ledcon;
unsigned int *leddat;

int led_open (struct inode *inode, struct file *file)
{
  printk("led_open\n");

  ledcon = ioremap(LED_CON, 4);  	//内核访问硬件需要地址映射
  if(ledcon == NULL)
  {
	  printk("ioremap LED_CON error\n");
      return -1;
  }
  writel(0x01, ledcon);  //设置LED3 GPX1_0 为输出模式

 leddat = ioremap(LED_DAT, 4);  	//内核访问硬件需要地址映射
 if(leddat == NULL)
 {
	 printk("ioremap LED_DAT error\n");
	 return -1;
 }
 writel(1, leddat);	//设置LED3 GPX1_0 输出高电平
  
  return 0;
}

int led_release(struct inode *inode, struct file *file)
{
   printk("led_close\n");
   return 0;
}

long led_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
  switch(cmd)
  {
  case  LED_ON:
	   printk("led on ..\n");
       writel(1, leddat);	//设置LED3 GPX1_0 输出高电平
  	   break;
  case  LED_OFF:
	   printk("led off ..\n");  	
       writel(0, leddat);	//设置LED3 GPX1_0 输出高电平	  
	   break;
  default:
  	   printk("no command\n");
  	   break;
  }
  return 0;
}

struct file_operations led_fops = { //文件操作
	.owner = THIS_MODULE,
    .open = led_open,
    .release =led_release,
    .unlocked_ioctl = led_ioctl,
};

static led_init(void)
{
   int ret;
   dev_t devno = MKDEV(LED_MA, LED_MI); 
   ret= register_chrdev_region(devno, LED_NUM, "newled");  //注册设备号
   if(ret)
   {
	   printk("register_chrdev_region error\n");
       return -1;
   }

   cdev_init(&cdev, &led_fops);           //初始化字符设备
   ret = cdev_add(&cdev, devno, LED_NUM); //添加字符设备到系统中
   if (ret < 0) {
	   printk("cdev_add error\n");
	   return -1;
   }

	printk("Led init  5    \n");
	return 0;
}

static void led_exit(void)
{
	dev_t devno = MKDEV(LED_MA, LED_MI);
	cdev_del(&cdev);
	unregister_chrdev_region(devno, LED_NUM);  //取消注册	
	printk("Led exit\n");	
}

module_init(led_init);   		//模块加载入口  对应命令insmod
module_exit(led_exit); 			//模块卸载入口  对应命令rmmod
MODULE_LICENSE("Dual BSD/GPL"); //免费开源声明

平台设备框架

平台设备引入

在这里插入图片描述
将驱动中的硬件信息剥离出来,放到设备树文件中

1. 寄存器的地址值是根据芯片手册 和原理图找到的
2. 对寄存器地址内容操作不能直接使用物理地址,需要ioremap
3. ioremap 将物理地址 映射到虚拟地址
4. platform 用于将硬件信息 和驱动代码做分离
5. 通过名字匹配,匹配成功执行 probe函数
6. 驱动通过 platform_get_resource 获取硬件设备资源
7. 作用 容易维护

参考

https://blog.csdn.net/m0_37542524/article/details/86476109

你可能感兴趣的:(嵌入式系统移植)