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驱动移植的更多相关文章

  1. I.MX6 SHT20 Linux 驱动移植

    /*********************************************************************** * I.MX6 SHT20 Linux 驱动移植 * ...

  2. linux驱动移植的重要数据结构

    转载:http://www.embeddedlinux.org.cn/html/jishuzixun/201304/14-2538.html 对于嵌入式 Linux 系统来说,有各种体系结构的处理器和 ...

  3. linux驱动移植问题点

    1.I2C地址是否和其它IC冲突.通过改地址解决 ——通常,以下三种情况的log表现相同:1.ic没连接到主板:2.i2c地址错误:3.该器件I2C地址与同组其它器件冲突 2.I2C通信是否受到其它s ...

  4. linux网卡驱动移植

    这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可 ...

  5. 【转】 linux内核移植和网卡驱动(二)

    原文网址:http://blog.chinaunix.net/uid-29589379-id-4708911.html 一,内核移植步骤: 1, 修改顶层目录下的Makefile ARCH       ...

  6. 【转】 linux内核移植和驱动添加(三)

    原文网址:http://blog.chinaunix.net/uid-29589379-id-4708909.html 原文地址:linux内核移植和驱动添加(三) 作者:genehang 四,LED ...

  7. Linux开源模块迁移概述暨交叉编译跨平台移植总结--从《嵌入式Linux驱动模板简洁和工程实践》

    本文摘录<嵌入式Linux驱动模板简洁和工程实践>一本书"开发和调试技术". Linux强大的是,有那么多的开源项目可以使用.通常非常需要可以通过寻找相关的源模块被定义 ...

  8. 【Linux驱动】TQ2440 DM9000E网卡驱动移植(Linux-2.6.30.4)

    花了一天的时间研究了一下Linux-2.6.30.4版本号内核下关于TQ2440 DM9000E的网卡驱动移植.总结一下自己的收获. 事实上.在Linux-2.6.30.4版本号内核下有关于网卡驱动, ...

  9. Linux Charger IC 驱动移植总结

    Linux Charger IC 驱动移植总结 文章目录 Linux Charger IC 驱动移植总结 1 设备树的基本知识 设备树的概念 设备树的基本结构 compatible属性 举个栗子 2 ...

  10. Linux网卡驱动移植--Dm9000网卡驱动分析

    1. Linux网络体系结构由以下5部分组成 ① 系统调用接口: 位于Linux网络子系统的顶部,为应用程序提供访问内核网络子系统的方法,主要指socket系统调用. ② 协议无关接口: 实现一组基于 ...

随机推荐

  1. 一文搞懂mysql索引底层逻辑,干货满满!

    一.什么是索引 在mysql中,索引是一种特殊的数据库结构,由数据表中的一列或多列组合而成,可以用来快速查询数据表中有某一特定值的记录.通过索引,查询数据时不用读完记录的所有信息,而只是查询索引列即可 ...

  2. Hybrid app本地开发如何调用JSBridge

    前天同事问我公司内部的小程序怎么对接的,我回忆了一下,简单记录了一下前端同学需要注意的点. 背后还有小程序架构.网络策略等等.当时恰逢小程序架构调整,(老架构的时候我就发现了有一个问题点可以优化,但是 ...

  3. LFS(Linux From Scratch)构建过程全记录(三):下载所需的软件包

    写在前面 本文将记录构建LFS的过程中,下载软件包的全过程 准备下载的路径 注意请确保$LFS已经设置完毕 我们需要创建一个文件夹,地址为$LFS/sources,用于保存对应的源码 输入的指令如下: ...

  4. WinForm完美实现Cefsharp-v49控件C#与JS交互,并且可加载运行flash

    https://blog.csdn.net/zhao331863874/article/details/117328415

  5. 安装Alertmanager,nginx配置二级路径代理访问

    安装配置 Alertmanager wget https://github.com/prometheus/alertmanager/releases/download/v0.20.0/alertman ...

  6. 18个一线工作中常用的Shell脚本

    1.检测两台服务器指定目录下的文件一致性 #!/bin/bash ###################################### 检测两台服务器指定目录下的文件一致性 ######### ...

  7. 痞子衡嵌入式:一个关于Segger J-Flash在Micron Flash固定区域下载校验失败的故事(SR寄存器BP[x:0]位)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家讲的是一个关于Segger J-Flash在Micron Flash固定区域下载校验失败的故事. 痞子衡最近在支持一个 i.MXRT1170 欧 ...

  8. 分布式存储系统之Ceph集群访问接口启用

    前文我们使用ceph-deploy工具简单拉起了ceph底层存储集群RADOS,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16724473.html:今天我 ...

  9. 洛谷P1714 切蛋糕(单调队列)

    先放代码...... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e5+10,M=0x3f3f3f3f; ...

  10. Java中的多线程的创建方式

    首先理清几个基本概念: 程序:为完成特定任务,用某种语言编写的一组指令的集合.即一段静态的代码(还没运行起来) 进程:是程序的一次执行过程,也就是说程序运行起来了,加载到了内存中,并占用了cpu的资源 ...