linux驱动移植
1.1 开发前准备
1.1.1 Linux 驱动(面向对象)
1).Linux 驱动框架
思想:写驱动的时候,只提供操作硬件设备的函数接口
文件存放磁盘: open ,read ,write ,close
ARM exynos4412 origen(母板)
裸机驱动:(硬件操作 + 功能需求)
AD/I2C/UART/PWM(Timer + OUT)/LED/KEY (阅读原理图,芯片手册,编码控制硬件)
2).Linux 硬件子系统
3).动手编写驱动(学习)
实际工作的时候,更多调试
4).学习方法
[1]内核提供的函数接口(熟悉),参照别人的写法,来编写自己
[2]对linux系统的思想认识(大牛灌输)
[3]动手(一点一点加)
[4]思考
1.1.2 Linux 底层课程
1).系统移植(bootloader , Linux kernel , device tree , fs)
2).驱动编写
1.1.3 Source Insight 环境搭建
Linux 内核文件添加,“add Tree” 是添加包含该文件夹路径下的全部文件。
同步文件,类似之前linux ubuntu系统下的tags功能,方便跟踪函数定义
同步过程可能需要几分钟。。。。。。
Options ->document options 设置字体大小
代码显示风格设置
1.2 Linux 驱动模块编程框架
编写linux驱动模块的基本步骤:
<1>头文件
include <linux/init.h>
#include <linux/module.h>
<2>许可权限GPL
MODULE_LICENSE("GPL");
<3>模块入口函数
int xxx_init(void)
{
....
return 0;
}
<4>模块出口函数
void xxx_exit(void)
{
....
}
<5>模块入口
module_init(xxx_init);
<6>模块出口
module_exit(xxx_exit);
1.3 LED驱动——模块化编程
1.3.1 模块化编程基本思路
1).阅读原理图
2).查看datasheet
3).编写模块
4).编译模块
思想:
Linux 内核源码的编译系统可以编译我们编写的模块代码。
第一种(产品发布):
将自己编写的代码,拷贝到Linux内核源码树下,然后配置编译,编译进内核。
第二种(驱动调试):
自己编写Makefile,然后使用Linux内核的编译系统,编译自己的模块代码。
问1:Linux内核的编译系统在哪里?
答:
<1>Linux内核源码下的Makefile
注意:你的Linux内核源码必须已经根据自己所开发的平台进行了配置
[1]修改了Makefile,指定了开发工具链(cross compile )
[2]已经使用Linux内核默认配置文件进行了配置
<2>ubuntu系统自带的Linux内核编译系统(pc机,x86)
/lib/modules/3.13.0-32-generic/build/Makefile
问2:如何在自己编写的Makefile中使用Linux内核的编译系统?
答:make -C linux内核编译系统的路径 M=需要编译的模块代码路径 modules
ifeq ($(KERNELRELEASE),)
KERNEL_BUILD = /lib/modules/$(shell uname -r)/build
MODULE_PATH := $(shell pwd)
module:
$(MAKE) -C $(KERNEL_BUILD) M=$(MODULE_PATH) modules
clean:
$(MAKE) -C $(KERNEL_BUILD) M=$(MODULE_PATH) clean
else
obj-m = led-driver.o
endif
install:
#拷贝led-driver.ko 到共享目录
cp led-driver.ko /home/ubuntu/workdir/fs4412/fs/rootfs
#用ARM gcc 编译器test.c
arm-none-linux-gnueabi-gcc test.c -o test
#拷贝可执行文件test到共享目录
cp test /home/ubuntu/workdir/fs4412/fs/rootfs
#make install 自动执行以上三个步骤
1.3.2 Linux 常用驱动模块操作shell命令
查看设备中已经注册的设备号
sudo insmod led-driver.ko 安装模块
cat /proc/devices 查看设备中已经注册的设备号
sudo rmmod led-driver.ko 卸载模块
dmesg 查看内核空间printk打印的信息
modinfo led-driver.ko 查看模块包含的信息
lsmod 查看系统中的模块
make
make clean 清除之前编译的可执行文件及配置文件。
dmesg | tail
cd /sys/class 查看已经安装的驱动路径
cd /sys/class/led/led cat uevent 查看驱动配置信息
ubuntu@farsight:/sys/class/led/led$ cat uevent
MAJOR=250
MINOR=0
DEVNAME=led
1.3.3 问题汇总——复习课
1)应用层的open( ),close( )如何调用到底层对应的open(),release( )?
函数指针把自定义的led_open,led_release函数首地址赋值给应用层的open,release函数
1.4 LED驱动——字符设备驱动注册
1.4.1 应用层的进程如何访问底层的驱动程序
字符设备或块设备,我们可以通过设备文件来找到底层驱动程序
-----------------------------------------------------------------
驱动的标识:设备号
12bit(主设备号) + 20bit(次设备号) = 32bit
主设备号:标识一类设备
次设备号:为了区分同类型设备的不同设备(个体)
------------------------------------------------------------------
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
//主要通过MKDEV(MAJOR, MINOR) 宏定义来计算设备号
问1:Linux内核有那么多驱动程序,如何才能确定自己需要访问的驱动程序?
答:通过设备文件中包含的设备号信息
问2:Linux内核中,如何描述文件?
答:<1>struct inode 描述文件属性信息(文件类型,权限,大小,修改时间,设备号[设备文件])
<2>struct file 描述一个打开的文件(打开的方式,文件偏移量,...)
[注意:只要打开一次文件,就会分配一次]
问3:应用层访问底层字符设备驱动的过程?
答:open---->设备文件
struct inode:设备号
--------->struct cdev:记录操作硬件设备的函数接口
寻找成功之后:
struct inode 结构体记录struct cdev这个结构体首地址
struct file 结构体记录struct file_operations这个结构体首地址
1.4.2 编写字符驱动基本步骤
问4:写字符驱动,需要做什么?
答:<1>struct cdev:Linux 针对字符设备的通用描述
struct led_device
{
struct cdev cdev;//通用的字符设备描述
....
};
需要给自己设计的结构体分配空间(内核申请空间使用kmalloc函数)
kmalloc(sizeof(pdev), GFP_KERNEL);
<2>提供硬件设备的操作函数接口
struct file_operations 结构体做填充
需要将这个结构体的首地址记录在struct cdev结构体中
<3>申请一个空闲的设备号
<4>使用设备号,将struct cdev这个结构体添加到系统中去
-------------------------------------------------------------------
mknod 设备文件名 设备文件类型 主设备号 次设备号
mknod /dev/led c 250 0
-------------------------------------------------------------------
mknod命令用于创建Linux中的字符设备文件和块设备文件
1.4.3 alloc_chrdev_region()动态获取设备号
1.4.4 class_create() , device_create()自动创建设备文件结点
1.4.5 内核空间与用户空间的数据拷贝函数
copy_from_user() 从用户空间拷贝数据
copy_to_user() 拷贝数据到用户空间
1.4.6 led字符设备驱动在ubuntu下加载bug解决
1.4.6.1 卸载了模块,但是查看设备号信息依然存在,重新加载报错误信息(文件已存在),需要重启虚拟机才消失?
这里当之前已经使用
s****udo insmod led-driver.ko 安装模块
rmmod卸载后再次insmod安装的时候,会提示1 个文件已经存在;调试代码发现如下指针类型匹配的问题:
1.4.6.2 多次使用insmod和rmmod后,cat /proc/devices发现有多个led的主设备号
这里当多次使用insmod和rmmod的时候,使用cat /proc/devices 查看设备号发现这里有多个led的主设备号,也即设备号删除失败。
注:为了方便调试,建议可以在每进行一个删除操作后执行一个printk()打印错误代码的操作(确认删除函数的返回值类型)。便于定位各项操作是否正确执行。
void led_exit(void) 里面有函数没有被正确执行
1.4.7 Led字符设备底层接口实现
Led 端口数据写值;
配置led端口输出模式
1.4.8 如何测试自己的led字符设备驱动是否添加成功
1.4.8.1 编写应用层Led 灯闪烁程序
1.4.8.2 驱动程序在开发板上加载
ubuntu@farsight:~/workdir/fs4412/kernel/linux-3.14$ pwd
/home/ubuntu/workdir/fs4412/kernel/linux-3.14
make
然后把模块文件拷贝到共享路径下
ubuntu@farsight:~/workdir/fs4412/fs/rootfs$ pwd
/home/ubuntu/workdir/fs4412/fs/rootfs
cp led-driver.ko /home/ubuntu/workdir/fs4412/fs/rootfs
ubuntu@farsight:/mnt/hgfs/share/learn_driver/led/led_version6$ arm-none-linux-gnueabi-gcc test.c -o test
ubuntu@farsight:/mnt/hgfs/share/learn_driver/led/led_version6$ cp test /home/ubuntu/workdir/fs4412/fs/rootfs
偷懒的做法,先make ;然后make install
install:
#拷贝led-driver.ko 到共享目录
cp led-driver.ko /home/ubuntu/workdir/fs4412/fs/rootfs
#用ARM gcc 编译器test.c
arm-none-linux-gnueabi-gcc test.c -o test
#拷贝可执行文件test到共享目录
cp test /home/ubuntu/workdir/fs4412/fs/rootfs
#make install 自动执行以上三个步骤
注:照抄的时候上面的文件名要根据自己的文件名修改过来。
1.4.8.3 led字符驱动在fs4412开发板加载过程中bug解决
SecureCRT 有打印led on 和led off信息,但是开发板上的led没有闪烁,这时候有一个基本的判断,之前的version在电脑上的ubuntu测试添加设备驱动成功,****所以设备驱动函数应该是没有问题的,led灯没有闪烁,问题出在针对底层硬件的操作,也即LED 的IO端口的输出模式寄存器配置以及输出电平的寄存器配置。
1.4.9 问题汇总——复习课
1.5 led字符设备驱动函数接口的改进ioctl()
I****octl()函数
1.6 基于platform子系统的字符驱动
M****akefile 里面文件名不匹配
调用函数写错,platform_device_register();
要注意区分device和driver
led-device.ko 驱动模块编写的有问题导致加载错误。
移除驱动的时候报错,这时需要新建目录
mkdir /lib/modules/3.14.0
这样就可以成功移除驱动了。
因为level可以是1,也可以是0,当为0时,如果前面一步没有做清零操作,则这一步|操作就没有起到作用(0与之前的状态相与还是之前的状态,无改变)。
这里注意~0 ——0取反不是1,
!0 ——0取非才是1.
1.7 如何添加多个led硬件设备驱动?
ls /sys/bus/platform/devices/
led_probe()函数多次执行,class_create()重复创建类fs4412-led.
L****ed2设备名字重名,
dev_name(&pdev->dev)****;
register_led_chrdev()传参数的时候需要传struct platform_device pdev****;
platform ——>led_version3 存在的卸载bug
测试老师的代码,使用rmmod led-driver 可以正常卸载
1.8 设备树文件
1.8.1 设备树文件的基本格式
ubuntu@farsight:~/workdir/fs4412/kernel/linux-3.14/arch/arm/boot/dts$
vi exynos4412-fs4412.dts ( X )
上面修改****exynos4412-fs4412.dts 后make dtbs 没有任何输出信息。
vi exynos4412-origen.dts ( √ )
注:这里要修改的是exynos4412-origen.dts,因为之前linux系统移植的时候Makefile 里面修改的是exynos4412-origen.dtb。
在linux-3.14路径下make dtbs 编译生成.dtb设备树文件
ubuntu@farsight:~/workdir/fs4412/kernel/linux-3.14$ make dtbs
DTC arch/arm/boot/dts/exynos4412-origen.dtb
拷贝生成的.dtb设备树文件到tftpboot 目录:
ubuntu@farsight:~/workdir/fs4412/kernel/linux-3.14/arch/arm/boot/dts$
cp exynos4412-origen.dtb /home/ubuntu/workdir/fs4412/tftpboot/
ubuntu@farsight:~/workdir/fs4412/tftpboot$ chmod 777 exynos4412-origen.dtb
修改class_create()函数参数
[root@farsight ]# ls -lh /dev/led2-gpio.2
crw-rw---- 1 0 0 253, 0 Jan 1 00:00 /dev/led2-gpio.2
[root@farsight ]# ./test /dev/led2-gpio.2
为了使pled->trigger_level 为1,则这里要对flags做取反操作。
1.9 按键中断
1.9.1 linux系统中断注册
1)、注册中断函数接口devm_request_irq( )
ubuntu@farsight:~/workdir/fs4412/kernel/linux-3.14/Documentation/devicetree/bindings/interrupt-controller$ vi interrupts.txt
1.9.2 Linux系统中断子系统框架
1.9.3 中断共享 IRQF_SHARED
多个驱动注册同一个中断号
1)、每一个devm_request_irq注册函数中需要在中断标志上加上 IRQF_SHARED****;
2)、要保证每个注册函数的dev_id 要唯一。(因为linux系统在注销一个中断的时候,是通过中断号(irq)和dev_id这两个参数来决定需要注销的中断的)。
cat /proc/interrupts 查看中断信息
这里之前interrupt-names 名字没有匹配(少了‘s’),不是linux系统通用属性就需要驱动工程师自己去解析。
1.10 中断上下文与进程上下文
1.10.1 Linux内核实现中断下半部的三种方式:软中断,tasklet,workqueue
1.10.2 tasklet实现基本步骤
1)初始化一个tasklet 结构体
2)编写tasklet处理函数(中断下半部处理函数)
3)在中断上半部处理函数结束处调度tasklet——tasklet_schedule()
//tasklet_schedule(struct tasklet_struct * t)
tasklet_schedule(&pkey->tasklet);
1.10.3 workqueue(工作队列)实现基本步骤
1)初始化一个work 结构体
struct work_device{
int irq;
struct work_struct work ;
};
struct work_device *pkey;
INIT_WORK(&pkey->work, key_work_handler);
2)编写work处理函数(中断下半部处理函数)
static void key_work_handler(struct work_struct *work)
{
/*******************************************
container_of - cast a member of a structure out to the containing structure
@ptr: the pointer to the member.
@type: the type of the container struct this is embedded in.
@member: the name of the member within the struct.
*******************************************/
struct work_device pkey = container_of****(work, struct work_device, work);
printk("key work handler pkey->irq : %d\n",pkey->irq);
return;
}
3)在中断上半部处理函数结束处调度work—— schedule****_work ()
schedule_work(&pkey->work);//调度工作队列
1.10.4 下半部机制的使用总结
1.11 ADC驱动移植
<1>阻塞
等待队列
<2>时钟信号的使用
1.12 I2C驱动移植
1.12.1 linux驱动中I2C驱动架构
1.12.2 I2C的读写时序
注意:主机接收器在接收到最后一个数据后,
1.12.3 I2C驱动的分层思想
1.12.4 I2C从设备(MPU6050)驱动的实现方法一
I2C控制器驱动,由Samsung提供的i2c-s3c2410.c;
i2c-dev.c实现了从设备通用驱动,则工程师只需要编写I2C从设备的的驱动即可(也即MPU6050的I2C驱动)。
cd sys/bus/i2c/drivers/
在设备树中添加I2C从设备信息后(参考i2c-s3c2410.txt 的写法)
ubuntu@farsight:~/workdir/fs4412/kernel/linux-3.14/Documentation/devicetree/bindings/i2c$
vi i2c-s3c2410.txt
linux驱动移植的更多相关文章
- I.MX6 SHT20 Linux 驱动移植
/*********************************************************************** * I.MX6 SHT20 Linux 驱动移植 * ...
- linux驱动移植的重要数据结构
转载:http://www.embeddedlinux.org.cn/html/jishuzixun/201304/14-2538.html 对于嵌入式 Linux 系统来说,有各种体系结构的处理器和 ...
- linux驱动移植问题点
1.I2C地址是否和其它IC冲突.通过改地址解决 ——通常,以下三种情况的log表现相同:1.ic没连接到主板:2.i2c地址错误:3.该器件I2C地址与同组其它器件冲突 2.I2C通信是否受到其它s ...
- linux网卡驱动移植
这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可 ...
- 【转】 linux内核移植和网卡驱动(二)
原文网址:http://blog.chinaunix.net/uid-29589379-id-4708911.html 一,内核移植步骤: 1, 修改顶层目录下的Makefile ARCH ...
- 【转】 linux内核移植和驱动添加(三)
原文网址:http://blog.chinaunix.net/uid-29589379-id-4708909.html 原文地址:linux内核移植和驱动添加(三) 作者:genehang 四,LED ...
- Linux开源模块迁移概述暨交叉编译跨平台移植总结--从《嵌入式Linux驱动模板简洁和工程实践》
本文摘录<嵌入式Linux驱动模板简洁和工程实践>一本书"开发和调试技术". Linux强大的是,有那么多的开源项目可以使用.通常非常需要可以通过寻找相关的源模块被定义 ...
- 【Linux驱动】TQ2440 DM9000E网卡驱动移植(Linux-2.6.30.4)
花了一天的时间研究了一下Linux-2.6.30.4版本号内核下关于TQ2440 DM9000E的网卡驱动移植.总结一下自己的收获. 事实上.在Linux-2.6.30.4版本号内核下有关于网卡驱动, ...
- Linux Charger IC 驱动移植总结
Linux Charger IC 驱动移植总结 文章目录 Linux Charger IC 驱动移植总结 1 设备树的基本知识 设备树的概念 设备树的基本结构 compatible属性 举个栗子 2 ...
- Linux网卡驱动移植--Dm9000网卡驱动分析
1. Linux网络体系结构由以下5部分组成 ① 系统调用接口: 位于Linux网络子系统的顶部,为应用程序提供访问内核网络子系统的方法,主要指socket系统调用. ② 协议无关接口: 实现一组基于 ...
随机推荐
- 一文搞懂mysql索引底层逻辑,干货满满!
一.什么是索引 在mysql中,索引是一种特殊的数据库结构,由数据表中的一列或多列组合而成,可以用来快速查询数据表中有某一特定值的记录.通过索引,查询数据时不用读完记录的所有信息,而只是查询索引列即可 ...
- Hybrid app本地开发如何调用JSBridge
前天同事问我公司内部的小程序怎么对接的,我回忆了一下,简单记录了一下前端同学需要注意的点. 背后还有小程序架构.网络策略等等.当时恰逢小程序架构调整,(老架构的时候我就发现了有一个问题点可以优化,但是 ...
- LFS(Linux From Scratch)构建过程全记录(三):下载所需的软件包
写在前面 本文将记录构建LFS的过程中,下载软件包的全过程 准备下载的路径 注意请确保$LFS已经设置完毕 我们需要创建一个文件夹,地址为$LFS/sources,用于保存对应的源码 输入的指令如下: ...
- WinForm完美实现Cefsharp-v49控件C#与JS交互,并且可加载运行flash
https://blog.csdn.net/zhao331863874/article/details/117328415
- 安装Alertmanager,nginx配置二级路径代理访问
安装配置 Alertmanager wget https://github.com/prometheus/alertmanager/releases/download/v0.20.0/alertman ...
- 18个一线工作中常用的Shell脚本
1.检测两台服务器指定目录下的文件一致性 #!/bin/bash ###################################### 检测两台服务器指定目录下的文件一致性 ######### ...
- 痞子衡嵌入式:一个关于Segger J-Flash在Micron Flash固定区域下载校验失败的故事(SR寄存器BP[x:0]位)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家讲的是一个关于Segger J-Flash在Micron Flash固定区域下载校验失败的故事. 痞子衡最近在支持一个 i.MXRT1170 欧 ...
- 分布式存储系统之Ceph集群访问接口启用
前文我们使用ceph-deploy工具简单拉起了ceph底层存储集群RADOS,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16724473.html:今天我 ...
- 洛谷P1714 切蛋糕(单调队列)
先放代码...... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e5+10,M=0x3f3f3f3f; ...
- Java中的多线程的创建方式
首先理清几个基本概念: 程序:为完成特定任务,用某种语言编写的一组指令的集合.即一段静态的代码(还没运行起来) 进程:是程序的一次执行过程,也就是说程序运行起来了,加载到了内存中,并占用了cpu的资源 ...