咸阳市城市建设管理局网站,重庆网站制作工作室,四川成都网站建设公司,太原域名注册推荐视频#xff1a;
正点原子【第四期】手把手教你学 Linux之驱动开发篇 小智-学长嵌入式LinuxAndroid底层开发入门教程
能力矩阵
基础能力矩阵
熟悉c/c、熟悉数据结构 熟悉linux系统#xff0c;Shell脚本#xff0c;Makefile/cmake/mk 文件IO、多线程、竞争、并发…推荐视频
正点原子【第四期】手把手教你学 Linux之驱动开发篇 小智-学长嵌入式LinuxAndroid底层开发入门教程
能力矩阵
基础能力矩阵
熟悉c/c、熟悉数据结构 熟悉linux系统Shell脚本Makefile/cmake/mk 文件IO、多线程、竞争、并发、阻塞、同步、中断、网络 熟悉调试工具gdb、gdbserver、tcpdump
行业能力矩阵
平台相关海思/君正/安霸MTK/NXP/高通/全志/瑞芯微/展讯等平台 系统相关bootloader、kernel、文件系统定制、移植、开发与适配 Android相关HAL、Services、Framework 驱动相关驱动设备模型、GPIO、I2C、SPL、UART、WIFl、LCD、USB 物联网相关TCP/IP、UDP、COAP、MQTT、HTTP 流媒体相关RTMP、RTSP、Iive555 视频编码H264、H265、MJPEG 音频编码PCM、AAC、G711 流媒体框架FFMEPG、GStreamer、WebRTC
开发流程
编译系统跑起来编译从原厂拿来的SDK看看有没有报错做好一些必要的ignore管理按照需求将项目外设逐个调通遥控WIFI蓝牙LED灯光等等封装Android接口让应用能用上驱动这个一般是移植的芯片厂家的驱动适配好就行应用实现根据客户的需求定制各种功能
Linux驱动开发思维
1、Liuⅸ下驱动开发直接操作寄存器不现实。 2、根据Linuⅸ下的各种驱动框架进行开发。一定要满足框架也就是Linux下各种驱动框架的掌握。 3、驱动最终表现就是/dev/xxx文件。打开、关闭、读写。 4、现在新的内核支持设备树这个一个dts文件此文件描述了板子的设备信息。
linux驱动开发分类
linux驱动分为三大类 1、字符设备驱动最多的。 2、块设备驱动存储 3、网络设备驱动 一个设备不说是一定只属于某一个类型。比如USB WIFI,SDIO WIFI,属于网络设备驱动因为他又有USB和SDIO,因此也属于字符设备驱动。
应用程序和驱动交互原理
用户空间用户态和内核空间内核态 Linux操作系统内核和驱动程序运行在内核空间、应用程序运行在用户空间。 应用程序想要访问内核资源怎么办有三种方法系统调用、异常中断和陷入。我们一般都是系统调用的方式。 Linux 驱动属于内核的一部分因此驱动运行于内核空间。
当我们在用户空间想要实现对内核的操作比如使用 open 函数打开/dev/led 这个驱动因为用户空间不能直接对内核进行操作因此必须使用一个叫做“系统调用”的方法来实现从用户空间“陷入”到内核空间这样才能实现对底层驱动的操作。
驱动的加载与卸载
Linux 驱动有两种运行方式。
将驱动编译进 Linux 内核中这样当 Linux 内核启动的时候就会自动运行驱动程序。将驱动编译成模块(Linux 下模块扩展名为.ko)在Linux 内核启动以后使用“insmod”命令加载驱动模块。 驱动编译完成以后扩展名为.ko有两种命令可以加载驱动模块insmod和modprobe。insmod 命令不能解决模块的依赖关系modprobe 会分析模块的依赖关系然后会将所有的依赖模块都加载到内核中。驱动模块的卸载使用命令“rmmod”即可 简单看懂驱动代码 /* 驱动入口函数 */ static int __init xxx_init(void) { /* 入口函数具体内容 */ return 0; } /* 驱动出口函数 */ static void __exit xxx_exit(void) { /* 出口函数具体内容 */ } /* 将上面两个函数指定为驱动的入口和出口函数 */ module_init(xxx_init); module_exit(xxx_exit);设备号的分配
静态分配设备号 注册字符设备的时候需要给设备指定一个设备号这个设备号可以是驱动开发者静态的指定一个设备号比如选择 200 这个主设备号。有一些常用的设备号已经被 Linux 内核开发者给分配掉了具体分配的内容可以查看文档 Documentation/devices.txt。 使用“cat /proc/devices”命令即可查看当前系统中所有已经使用了的设备号。动态分配设备号 静态分配设备号需要我们检查当前系统中所有被使用了的设备号然后挑选一个没有使用的。而且静态分配设备号很容易带来冲突问题Linux 社区推荐使用动态分配设备号在注册字符设备之前先申请一个设备号系统会自动给你一个没有被使用的设备号这样就避免了冲突。 设备号的申请函数如下 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) 注销字符设备之后要释放掉设备号设备号释放函数如下 void unregister_chrdev_region(dev_t from, unsigned count)
内核打印
在驱动中可以使用printk 来输出信息而不用printf。因为在 Linux 内核中没有 printf 这个函数。printk 相当于printf 的孪生兄妹printf运行在用户态printk 运行在内核态。在内核中想要向控制台输出或显示一些内容必须使用printk 这个函数。不同之处在于printk 可以根据日志级别对消息进行分类一共有 8 个消息级别这 8 个消息级别定义在文件 include/linux/kern_levels.h 里面定义如下
#define KERN_SOH \001
#define KERN_EMERG KERN_SOH 0 /* 紧急事件一般是内核崩溃 */
#define KERN_ALERT KERN_SOH 1 /* 必须立即采取行动 */
#define KERN_CRIT KERN_SOH 2 /* 临界条件比如严重的软件或硬件错误*/
#define KERN_ERR KERN_SOH 3 /* 错误状态一般设备驱动程序中使用 KERN_ERR 报告硬件错误 */
#define KERN_WARNING KERN_SOH 4 /* 警告信息不会对系统造成严重影响 */
#define KERN_NOTICE KERN_SOH 5 /* 有必要进行提示的一些信息 */
#define KERN_INFO KERN_SOH 6 /* 提示性的信息 */
#define KERN_DEBUG KERN_SOH 7 /* 调试信息 */一共定义了 8 个级别其中 0 的优先级最高7 的优先级最低。如果要设置消息级别参考如下示例
printk(KERN_EMERG gsmi: Log Shutdown Reason\n);上述代码就是设置“gsmi: Log Shutdown Reason\n”这行消息的级别为 KERN_EMERG。在具体的消息前面加上 KERN_EMERG 就可以将这条消息的级别设置为 KERN_EMERG。 如果使用 printk 的 时 候 不 显 式 的 设 置 消 息 级 别 那 么 printk 将 会 采 用 默 认 级 别MESSAGE_LOGLEVEL_DEFAULTMESSAGE_LOGLEVEL_DEFAULT 默认为 4 在控制台修改内核打印级别
echo 7 echo 7 /proc/sys/kernel/printk在uboot下改变内核打印级别
env set loglevel7
env save编译驱动程序和测试 APP
编译驱动程序
当我们编写完一个驱动程序之后会生成.c文件比如chrdevbase.c 这个文件。我们需要将其编译为.ko 模块.
创建Makefile 文件然后在其中输入如下内容 KERNELDIR : /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH : $(shell pwd) obj-m : chrdevbase.o build: kernel_modules kernel_modules:
$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) modules clean:
$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) clean 第 1 行KERNELDIR 表示开发板所使用的 Linux 内核源码目录使用绝对路径大家根据自己的实际情况填写即可。 第 2 行CURRENT_PATH 表示当前路径直接通过运行“pwd”命令来获取当前所处路径。 第 3 行obj-m 表示将 chrdevbase.c 这个文件编译为 chrdevbase.ko 模块。 第 8 行具体的编译命令后面的 modules 表示编译模块-C 表示将当前的工作目录切换到指定目录中也就是 KERNERLDIR 目录。M 表示模块源码目录“make modules”命令中加入 Mdir 以后程序会自动到指定的 dir 目录中读取模块的源码并将其编译为.ko 文件。 2. Makefile 编写好以后输入“make”命令编译驱动模块 3. 编译成功以后就会生成一个叫做 chrdevbaes.ko 的文件此文件就是 chrdevbase 设备的驱动模块。至此chrdevbase 设备的驱动就编译成功。
编译测试 APP
测试 APP 比较简单只有一个文件因此就不需要编写 Makefile 了直接输入命令编译。 因为测试 APP 是要在 ARM 开发板上运行的所以需要使用 arm-linux-gnueabihf-gcc 来编译输入如下命令
arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp编译完成以后会生成一个叫做 chrdevbaseApp 的可执行程序输入如下命令查看chrdevbaseAPP 这个程序的文件信息
file chrdevbaseApp运行测试 拷贝文件 加载 chrdevbase.ko 驱动文件
insmod chrdevbase.ko 或者
modprobe chrdevbase.ko如果使用 modprobe 加载驱动的话可能会出现如下的提示
modprobe 提示无法打开“modules.dep”这个文件因此驱动挂载失败了。我们不用手动创建 modules.dep 这个文件直接输入 depmod 命令即可自动生成modules.dep有些根文件系统可能没有 depmod 这个命令如果没有这个命令就只能重新配置busybox使能此命令然后重新编译 busybox。输入“depmod”命令以后会自动生成 modules.alias、modules.symbols 和 modules.dep 这三个文件
然后重新使用modprobe 加载 chrdevbase.ko 输入如下命令查看当前系统中有没有 chrdevbase 这个设备
cat /proc/devices创建设备节点文件
驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件应用程序就是通过操作这个设备节点文件来完成对具体设备的操作。输入如下命令创建/dev/chrdevbase 这个设备节点文件
mknod /dev/chrdevbase c 200 0其中“mknod”是创建节点命令“/dev/chrdevbase”是要创建的节点文件“c”表示这是个字符设备“200”是设备的主设备号“0”是设备的次设备号。创建完成以后就会存在/dev/chrdevbase 这个文件可以使用“ls /dev/chrdevbase -l”命令查看 如果 chrdevbaseAPP 想要读写 chrdevbase 设备直接对/dev/chrdevbase 进行读写操作即可。
设备操作测试
./chrdevbaseApp /dev/chrdevbase 1./chrdevbaseApp /dev/chrdevbase 2卸载驱动模块
如果不再使用某个设备的话可以将其驱动卸载掉比如输入如下命令卸载掉 chrdevbase 这个设备
rmmod chrdevbase.ko