1.1-1.3设备树的引入与体验

目录

1.1字符设备驱动程序的三种写法

以前怎么写驱动?

框架

实现

总结

1.2字符设备驱动的传统写法

001_led_drv_traditional

led_drv.c

ledtest.c

Makefile

1.3字符设备编译驱动测试

1. 编译器的选择:

2. 通过设置PATH环境变量来选择使用某个工具链:

2.1 安装工具链:

2.2 设置环境变量使用某个工具链:

3. u-boot的编译:

3. kernel的编译:

4. 制作root filesystem : 

5. 烧写


1.1字符设备驱动程序的三种写法

以前怎么写驱动?

1.看原理图

       确定引脚

      看芯片手册,确定如何操作引脚

2.写驱动===》起封装作用

3.写测试程序

框架

实现

 

总结

a. 驱动程序编写有3种方法:传统方法、使用总线设备驱动模型、使用设备树
b. 这3种方法也核心都是一样的: 分配、设置、注册 file_operations结构体
   这个结构体中有.open, .read, .write, .ioctl等成员
   驱动程序要实现这些成员,在这些成员函数中操作硬件
c. 这3种方法的差别在于:如何指定硬件资源,比如如何指定LED引脚是哪个      
       c.1 传统方法: 在驱动程序代码中写死硬件资源, 代码简单/不易扩展

                   
       c.2 总线设备驱动模型: 把驱动程序分为两部分(platform_driver, platform_device)
               在platform_device中指定硬件资源,
               在platform_driver中分配/设置/注册 file_operations结构体, 并从platform_device获得硬件资源
    
        特点:
        易于扩展,但是有很多冗余代码(每种配置都对应一个platform_device结构体), 
        硬件有变动时需要重新编译内核或驱动程序。


    
   当换了一个板子,只是引脚不同,这样使用不同的板子平台设备的引脚不同,但是驱动相同,所以只需要修改引脚

   c.3 使用设备树指定硬件资源: 驱动程序也分为两部分(platform_driver, 设备树*.dts)
              在设备树*.dts中指定硬件资源, dts被编译为dtb文件, 在启动单板时会将dtb文件传给内核,
              内核根据dtb文件分配/设置/注册多个platform_device
    
    platform_driver的编写方法跟"总线设备驱动模型"一样。
    
    特点:
    易于扩展,没有冗余代码
    硬件有变动时不需要重新编译内核或驱动程序,只需要提供不一样的dtb文件
    
    注: dts  - device tree source  // 设备树源文件
        dtb  - device tree blob    // 设备树二进制文件, 由dts编译得来
        blob - binary large object

1.2字符设备驱动的传统写法

a. 分配file_operations结构体
b. 设置file_operations结构体
   该结构体中有.open,.read,.write等成员,
   在这些成员函数中去操作硬件
c. 注册file_operations结构体:
   register_chrdev(major, name, &fops)
d. 入口函数: 调用register_chrdev
e. 出口函数: 调用unregister_chrdev

 

 

以2期毕亚班3.4.2中的第一个驱动为例

毕业班第4课移植驱动到3.4.2内核_文档_图片_源码\drivers_and_test_new\jz2440\first_drv

001_led_drv_traditional

led_drv.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

#define S3C2440_GPA(n)  (0<<16 | n)
#define S3C2440_GPB(n)  (1<<16 | n)
#define S3C2440_GPC(n)  (2<<16 | n)
#define S3C2440_GPD(n)  (3<<16 | n)
#define S3C2440_GPE(n)  (4<<16 | n)
#define S3C2440_GPF(n)  (5<<16 | n)
#define S3C2440_GPG(n)  (6<<16 | n)
#define S3C2440_GPH(n)  (7<<16 | n)
#define S3C2440_GPI(n)  (8<<16 | n)
#define S3C2440_GPJ(n)  (9<<16 | n)

static int led_pin = S3C2440_GPF(5);
static volatile unsigned int *gpio_con;
static volatile unsigned int *gpio_dat;

/* 123. 分配/设置/注册file_operations 
 * 4. 入口
 * 5. 出口
 */

static int major;
static struct class *led_class;

static unsigned int gpio_base[] = {
	0x56000000, /* GPACON */
	0x56000010, /* GPBCON */
	0x56000020, /* GPCCON */
	0x56000030, /* GPDCON */
	0x56000040, /* GPECON */
	0x56000050, /* GPFCON */
	0x56000060, /* GPGCON */
	0x56000070, /* GPHCON */
	0,          /* GPICON */
	0x560000D0, /* GPJCON */
};

static int led_open (struct inode *node, struct file *filp)
{
	/* 把LED引脚配置为输出引脚 */
	/* GPF5 - 0x56000050 */
	int bank = led_pin >> 16;
	int base = gpio_base[bank];

	int pin = led_pin & 0xffff;
	gpio_con = ioremap(base, 8);
	if (gpio_con) {
		printk("ioremap(0x%x) = 0x%x\n", base, gpio_con);
	}
	else {
		return -EINVAL;
	}
	
	gpio_dat = gpio_con + 1;

	*gpio_con &= ~(3<<(pin * 2));
	*gpio_con |= (1<<(pin * 2));  

	return 0;
}

static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
	/* 根据APP传入的值来设置LED引脚 */
	unsigned char val;
	int pin = led_pin & 0xffff;
	
	copy_from_user(&val, buf, 1);

	if (val)
	{
		/* 点灯 */
		*gpio_dat &= ~(1<<pin);
	}
	else
	{
		/* 灭灯 */
		*gpio_dat |= (1<<pin);
	}

	return 1; /* 已写入1个数据 */
}

static int led_release (struct inode *node, struct file *filp)
{
	printk("iounmap(0x%x)\n", gpio_con);
	iounmap(gpio_con);
	return 0;
}


static struct file_operations myled_oprs = {
	.owner = THIS_MODULE,
	.open  = led_open,
	.write = led_write,
	.release = led_release,
};


static int myled_init(void)
{
	major = register_chrdev(0, "myled", &myled_oprs);

	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */

	return 0;
}

static void myled_exit(void)
{
	unregister_chrdev(major, "myled");
	device_destroy(led_class,  MKDEV(major, 0));
	class_destroy(led_class);
}

module_init(myled_init);
module_exit(myled_exit);


MODULE_LICENSE("GPL");


 

ledtest.c


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* ledtest on
  * ledtest off
  */
int main(int argc, char **argv)
{
	int fd;
	unsigned char val = 1;
	fd = open("/dev/led", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", argv[0]);
		return 0;
	}

	if (strcmp(argv[1], "on") == 0)
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	
	write(fd, &val, 1);
	return 0;
}

 

Makefile

KERN_DIR = /work/system/linux-4.19-rc3

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= led_drv.o

 

 

1.3字符设备编译驱动测试

我用的是荣品的14.04

1. 编译器的选择:

一个完整的Linux系统包含三部分: u-boot, kernel, root filesystem.
a. 对于u-boot:
我们仍然使用u-boot 1.1.6, 在这个版本上我们实现了很多功能: usb下载,菜单操作,网卡永远使能等, 不忍丢弃.

b. 对于kernel:
我下载了目前(2018.09.19)最新的内核 (4.19)

c. 对于root filesystem
中文名为"根文件系统", 它包含一些必须的APP, 一些动态库。
一般来说这些动态库是从工具链里的lib目录复制得到的,
当然也可以自己去编译glibc等库。

在编译u-boot和kernel时, 我们可以使用新的工具链, 
只要这个工具链支持ARM9的指令集(armv4)就可以(这通常可以通过编译参数来指定使用特定的指令集).
工具链可以从某些网站上下载,并不需要自己去制作。
比如可以访问这个网站: https://releases.linaro.org/components/toolchain/binaries/4.9-2017.01/arm-linux-gnueabi/
下载: gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi.tar.xz

但是在制作根文件系统时, 实际上我们是编译各种APP, 
这些APP要用到一些动态库, 为了方便一般直接使用工具链中lib目录里的库。
新版工具链的lib库一般是支持新的芯片,比如cortex A7,A8,A9,并不支持ARM9。
所以在制作根文件系统、编译APP时我们还得使用比较老的工具链: arm-linux-gcc-4.3.2.tar.bz2
 

2. 通过设置PATH环境变量来选择使用某个工具链:

2.1 安装工具链:

    这非常简单, 解压即可:
    sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C /                     (解压到根目录, /usr/local/arm/4.3.2/bin/下就是工具链)
    tar xJf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi.tar.xz  (解压到当前目录, 假设/work/system/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin下就是工具链)
    
    注意: "/work/system" 请自行替换为你的实际目录
    

2.2 设置环境变量使用某个工具链:

a. 要使用arm-linux-gcc 4.3.2, 执行如下命令:
    export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/arm/4.3.2/bin
   然后就可以执行 arm-linux-gcc -v 观看到版本号了

这里用来编译文件系统和应用程序

b. 要使用arm-linux-gnueabi-gcc 4.9.4, 执行如下命令:
    export  PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/rpdzkj/w_device_tree/tool/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin
  然后就可以执行 arm-linux-gnueabi-gcc -v 观看到版本号了

这个用来编译uboot、kernel和驱动模块
   

3. u-boot的编译:

a. 首先设置环境变量使用要使用arm-linux-gnueabi-gcc 4.3.2
b. 
把源文件u-boot-1.1.6.tar.bz2、补丁文件u-boot-1.1.6_device_tree_for_jz2440.patch放在同一个目录,
执行如下命令:
tar xjf u-boot-1.1.6.tar.bz2                                   // 解压
cd u-boot-1.1.6                  
patch -p1 < ../u-boot-1.1.6_device_tree_for_jz2440.patch       // 打补丁
make 100ask24x0_config                                         // 配置
make                                                           // 编译, 可以得到u-boot.bin

勘误说明:

2018.12.26:
    之前的补丁文件中, 
#define CFG_ENV_OFFSET      0x80000    
     它的本意是指定params分区的首地址, 但是写成了0x80000, 这是个错误的值。
     这会导致在u-boot中执行save命令后破坏kernel分区中的数据, 使得板上的uImage无法启动。

     修改过的补丁文件中, 这个宏为:
#define CFG_ENV_OFFSET      0x60000     

3. kernel的编译:

a. 首先设置环境变量使用要使用arm-linux-gnueabi-gcc 4.3.2
b. 
把源文件linux-4.19-rc3.tar.gz、补丁文件linux-4.19-rc3_device_tree_for_jz2440.patch放在同一个目录,
执行如下命令:
tar xzf linux-4.19-rc3.tar.gz                                   // 解压
cd linux-4.19-rc3                  
patch -p1 < ../linux-4.19-rc3_device_tree_for_jz2440.patch      // 打补丁
cp config_ok .config                                            // 配置
make uImage                                                     // 编译, 可以得到arch/arm/boot/uImage
make dtbs                                                       // 编译, 可以得到arch/arm/boot/dts/jz2440.dtb

注意: 
a. 如果提示"mkimage not found", 先编译u-boot, 把tools/mkimage复制到/bin目录
b. 如果提示"openssl/bio.h: No such file or directory"
   先确保你的ubuntu可以上网, 然后执行如下命令:
   sudo apt-get update
   sudo apt-get install libssl-dev
   

4. 制作root filesystem : 

   可以直接使用映象文件: fs_mini_mdev_new.yaffs2   
   
   如果想自己制作,请参考视频: 
   从www.100ask.net下载页面打开百度网盘,
   打开如下目录:
        100ask分享的所有文件
            009_UBOOT移植_LINUX移植_驱动移植(免费)
                毕业班第3课_移植3.4.2内核
                    毕业班第3课第2节_移植3.4.2内核之修改分区及制作根文件系统.WMV

 

5. 烧写

a. 使用EOP烧写u-boot.bin到JZ2440的nor flash或nand flash
b. 启动u-boot, 在串口工具中输入相应菜单命令, 使用dnw_100ask.exe发送对应文件
   
       菜单                            要发送的文件
[k] Download Linux kernel uImage          uImage
[t] Download device tree file(.dtb)       jz2440.dtb
[y] Download root_yaffs image             fs_mini_mdev_new.yaffs2

   烧写完毕即可重启进入板上LINUX系统。

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值