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系统调用. ② 协议无关接口: 实现一组基于 ...
随机推荐
- day02-代码实现01
多用户即时通讯系统02 4.编码实现01 4.1功能实现-用户登录 4.1.1功能说明 因为还没有学习数据库,我们人为规定 用户名/id = 100,密码为 123456 就可以登录,其他用户不能登录 ...
- MySQL 在 Kubernetes IPVS 模式下引发的 TCP 超时问题
文章转载自:https://mp.weixin.qq.com/s/XQ2SlCYxvXPY0rRRO-CURA
- Traefik开启监控,日志,追踪需要的参数
监控 官方文档地址:https://doc.traefik.io/traefik/observability/metrics/overview/ 可以使用多种监控软件,比如Datadog,Influx ...
- 21. Fluentd输出插件:rewrite_tag_filter用法详解
我们在做日志处理时,往往会从多个源服务器收集日志,然后在一个(或一组)中心服务器做日志聚合分析. 源服务器上的日志可能属于同一应用类型,也可能属于不同应用类型.我们可能需要在聚合服务器上对这些不同类型 ...
- 12. Fluentd部署:多Workers进程模式
介绍如何使用Fluentd的多worker模式处理高访问量的日志事件.此模式会运行多个worker进程以最大利用多核CPU. 原理 默认情况下,一个Fluentd实例会运行一个监控进程和一个工作进程. ...
- 打印 Logger 日志时,需不需要再封装一下工具类?
在开发过程中,打印日志是必不可少的,因为日志关乎于应用的问题排查.应用监控等.现在打印日志一般都是使用 slf4j,因为使用日志门面,有助于打印方式统一,即使后面更换日志框架,也非常方便.在 < ...
- 详解JS中 call 方法的实现
摘要:本文将全面的,详细解析call方法的实现原理 本文分享自华为云社区<关于 JavaScript 中 call 方法的实现,附带详细解析!>,作者:CoderBin. 本文将全面的,详 ...
- 洛谷P3690 (动态树模板)
一位大佬写的代码.(加上我自己的一些习惯性写法) 存个模板. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N= ...
- HTML+CSS基础知识(2)选择器的使用、盒子模型的讲解、列表的使用
文章目录 1.CSS基础知识 2.css样式 2.1.代码: 2.2 测试结果 3.CSS的语法 3.1 代码 4.块元素和行内元素 4.1 代码 4.2 测试结果 5.常用的选择器 5.1 代码块 ...
- 齐博X1忘记管理员密码了怎么办?如何强制进后台?
当你忘记密码,或者是某些原因导致进不了后台的话,这个时候你修改一下根目录的admin.php文件,把文件第二行 //define('SUPER_ADMIN',true); 前面的 // 双斜杠删除,再 ...