HaveFunWithEmbeddedSystem/Chapter8_SOC_与_Linux/8.2_Linux_驱动开发概述.md

5.2 KiB
Raw Permalink Blame History

8.2 Linux 驱动开发概述

嵌入式系统的分类

SOC通常内存较大使用外部磁盘大容量 Flash 或 EMMC有 MMU区分用户态和内核态运行 Linux/Android/WinCE 等大型系统,应用程序与系统可分开编译,系统可动态安装程序,一般使用远程 GDB 和日志输出方式进行调试。开发分为 BSP、驱动、中间件、应用/UI 等。比如 Intel Atom 系列、ARM Cortex A 系列。

MCU通常内存和 Flash 较小,无 MMU不区分用户态和内核态无系统或运行小型实时系统应用程序与系统一起编译形成镜像进行烧录通常采用 JTAG 等硬件调试器进行调试。开发不分家或分为系统、驱动、应用开发。比如 ARM Cortex M 系列、Atmel AVR 系列、TI MSP430 系列。

认识 Linux 系统

Linux 可运行于多种平台比如个人电脑、Power PC 以及 ARM Cortex A 系列 SOC 上。

Linux 分为内核态和用户态,内核态为受保护的代码,用户态程序执行异常不会导致系统漰溃,是因为用户态只能执行用户权限指令,特权指令只能在内核态执行 (这是由硬件设计决定的),且由于内核态的内存受到硬件保护,因此用户程序的溢出不会导致内核崩溃。内核态和用户态分离的目的就是为了保证系统的稳定,避免丰富的应用程序导致系统死机,而这种情况在 DOS 时期是很常见的。

由于驱动程序往往需要进行特权操作,比如直接操作物理内存,硬件寄存器,使用中断资源等,因此 Linux 系统的硬件驱动程序需要在内核态执行。Linux 的核心代码很小,在源代码中占大多数的为驱动程序或驱动程序框架。

为了适应不同类型设备的特征Linux 内核驱动划分为多个子系统比如SPI 子系统、存储子系统、GPU 子系统等。每个子系统都为该类型设备而进行优化设计。同时,许多公司在子系统框架下还会按照自己的产品线构建下一级子系统。

嵌入式 Linux

嵌入式 Linux = SOC + Linux 开发

Linux 内核源代码目录结构

  • arch包含和硬件体系结构相关的代码每种平台占一个相应的目录如 i386、ARM、PowerPC、MIPS 等。
  • arch/<arch>/configs各种 defconfig 文件。
  • block块设备驱动程序 I/O 调度。
  • Documentation内核各部分的通用解释和注释。
  • drivers设备驱动程序每个不同的驱动占用一个子目录如 char、block、net、mtd、i2c 等。
  • include头文件与系统相关的头文件被放置在 include/linux 子目录下。
  • kernel内核的最核心部分包括进程调度、定时器等而和平台相关的一部分代码放在 arch/*/kernel 目录下。
  • lib库文件代码。
  • net网络相关代码实现了各种常见的网络协议。
  • scripts包含用于配置内核的脚本文件。
  • soundALSA、OSS 音频设备的驱动核心代码和常用设备驱动。

Linux 内核驱动开发的特点

  • 由于驱动运行在内核态,任何错误都有可能导致内核崩溃,因此软件自量的要求很高。
  • 开发时需要对驱动有所定位,需要清楚驱动在哪个子系统下,属于总线驱动还是设备驱动等。
  • 内核除为驱动划分了各个子系统外,还将驱动进行了分类,比如:字符设备,块设备,网络设备,杂项设备等。
  • 与用户态一样,内核中提供了很多同步异步,以及通讯机制,学习 Linux 内核驱动开发时应注意各 API 的学习。
  • Linux 的内核版本更新很快不同版本间架构API 等会有差异,应注意区分。
  • 内核驱动可以与内核代码编译成一体,称为 Build In也可以编译成独立模块。
  • 学习时应注意区分驱动程序的入口,加载时机等。
  • 内核源码中会使用大量的高级 C 语言宏操作,有些代码比较绕,通常先知道其用途,再深入学习原理。
  • Linux 内核源码使用 Linux 代码风格进行开发,代码风格应共同遵守。
  • 缺少有效的调试手段,通常是打印输出日志,动态调试和设置断点等需要使用 SOC 专用硬件调试器来调试,授权费用很高。

hellodrv —— 一个简单的 Linux 内核模块

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)
{
    printk(KERN_ALERT "hello driver enter\n");
    return 0;
}

static void hello_exit(void)
{
    printk(KERN_ALERT "hello driver exit\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("rick.chan");
MODULE_DESCRIPTION("a simple hello world module");
MODULE_ALIAS("a simplest module");

其由 C 程序的 include、函数、变量以及诸多 MODULE_* 宏构成,这个程序里没有用户态程序的标准入口 main() 函数。程序的入口由 module_init() 定义,出口由 module_exit() 定义。

Makefile 文件如下:

obj-m:= \
    hellodrv.o

hellodrv-objs:= \
    hello_drv.o

EXTRA_CFLAGS += \
    -I$(PWD)

all:
    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean