嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架

文章目录

  • 1,第三方驱动 黑盒移植
    • 1.1,驱动编译进内核Makefile
      • 1.1.1, 选择驱动存放目录 (或任意目录)
      • 1.1.2, 改Makefile让驱动编译进内核(同步修改,对应目录下的Makefile)
      • 1.1.3,改Kconfig (界面可配置)
        • 1.1.3.1,测试驱动
        • 1.1.3.2,创建设备文件
    • 1.2,图形化配置Kconfig
      • 1.2.1,make menuconfig、Makefile、Kconfig、.config 三者的关系
      • 1.2.2,配置Kconfig
        • 1.2.2.1,在Kconfig中添加一个灯的选项
        • 1.2.2.2,修改Makefile
        • 1.2.2.3,在图形界面选择不编译LED驱动,看是否成功
    • 1.3,编译驱动为独立的模块
      • 1.3.1,C程序代码编写
      • 1.3.2, Makefile 编写
      • 1.3.3,编译出 ko 模块
      • 1.3.4,配置为模块方式
      • 1.3.5,make modules 编译所有的模块
      • 1.3.6,在linux运行时,可以装载和卸载模块
        • 1.3.6.1,拷贝LED驱动模块到根文件系统
        • 1.3.6.2,通过 手动加载模块 insmod *.ko
        • 1.3.6.3,创建设备节点(应用访问驱动的入口)
        • 1.3.6.4,运行测试驱动的应用程序
  • 2,白盒移植
    • 2.1,黑白盒对比
    • 2.2,驱动框架
      • 2.2.1,字符设备框架
        • 2.2.1.1,字符设备引入
        • 2.2.1.2,字符设备驱动相关
        • 2.2.1.3,LED驱动代码---fs4412_led_drv.c
      • 2.2.2,平台设备框架
        • 2.2.2.1,平台设备引入

1,第三方驱动 黑盒移植

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第1张图片

1.1,驱动编译进内核Makefile

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

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

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

拷贝LED驱动程序至drivers目录
LED属于字符设备,所以放在drivers/char/目录下
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第2张图片

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

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第3张图片
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第4张图片

1.1.3,改Kconfig (界面可配置)

1.1.3.1,测试驱动

烧写镜像至开发板
在这里插入图片描述

驱动程序测试代码

#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;
}

编译并拷贝至根文件系统
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第5张图片

在开发板上运行该测试程序
在这里插入图片描述
分析这个打印信息的来源
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第6张图片
需要创建设备文件

1.1.3.2,创建设备文件

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第7张图片

mknod /dev/led c 501 0

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第8张图片
创建好设备文件后,再运行测试程序,就可以看到LED灯在闪烁了

1.2,图形化配置Kconfig

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

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

1.2.2,配置Kconfig

1.2.2.1,在Kconfig中添加一个灯的选项

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第9张图片

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

可以看到图形界面中多了一个对应的选项
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第10张图片

1.2.2.2,修改Makefile

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第11张图片

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

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第12张图片

没有编译LED驱动
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第13张图片

开发板上电重启,也可以测试出LED驱动没有被编译进来
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第14张图片

1.3,编译驱动为独立的模块

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

1.3.1,C程序代码编写

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

1.3.2, Makefile 编写

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第15张图片

1.3.3,编译出 ko 模块

make之后,会自动跳到内核里面,借用内核的Makefile把当前驱动程序编译成ko文件
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第16张图片

1.3.4,配置为模块方式

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第17张图片
嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第18张图片

1.3.5,make modules 编译所有的模块

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第19张图片

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

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

在这里插入图片描述

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

卸载是rmmod

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第20张图片

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

在这里插入图片描述

1.3.6.4,运行测试驱动的应用程序

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第21张图片

2,白盒移植

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

2.1,黑白盒对比

黑盒移植
	两种方式
		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)

在这里插入图片描述

2.2,驱动框架

2.2.1,字符设备框架

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第22张图片

2.2.1.1,字符设备引入

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

2.2.1.2,字符设备驱动相关

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

2.2.1.3,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"); //免费开源声明

2.2.2,平台设备框架

2.2.2.1,平台设备引入

嵌入式linux-系统移植,Linux内核移植,黑盒移植,第三方驱动移植,驱动编译进内核Makefile,图形化配置Kconfig,编译驱动为独立的模块,白盒移植,字符设备框架,平台设备框架_第23张图片
将驱动中的硬件信息剥离出来,放到设备树文件中

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

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