您的位置:首页 > 其它

【读书笔记】深度探索 HAL与驱动开发

2016-10-08 12:20 225 查看
深度探索 HAL与驱动开发

1.1 Android系统架构

Android的系统架构分为4层。

第1层:Linux内核

主要包括Linux的驱动程序以及内存管理、进程管理、电源管理等程序。Android使用Linux2.6作为其内核,不同Android版本的驱动可能并不通用。

主要讲开发第1层的驱动程序,以及再不同Linux版本、硬件平台移植驱动程序。

第2层:C/C++代码库
主要包括使用C/C++编写的代码库(Linux下的.so文件)。

第3层:Android SDK API
Java API层,用Java编写的各种Library。参考《Android开发权威指南》

第4层:应用程序
相当于Android的UI,所有的Android应用程序都属于这一层,主要依靠第3层中的Android SDK API来完成各种功能。


1.2 Android系统移植的主要工作

Android移植可分为两部分:应用移植和系统移植。

本书的Android移植是指Android操作系统的移植,包括Linux驱动、HAL程序库的移植。

Android系统移植是指让Android操作系统在某个特定硬件平台上运行,使操作系统在某个特定硬件平台上运行的一个首要条件就是该操作系统支持硬件平台的CPU架构。要想Android在不同硬件平台上正常运行,只支持CPU架构还不行,必须要让Android可以识别平台上的各种硬件。这些工作主要由Linux内核完成,主角就是Linux驱动。因此系统移植除了移植CPU架构,最重要的就是移植Linux驱动。

HAL(Hardware Abstraction Layer),硬件抽象层,位于第2层,即普通的Linux程序库(.so文件),Android SDK通过HAL直接访问Linux驱动。Android并不像其他的Linux系统一样由应用程序直接访问驱动,而是中间通过HAL隔了一层。

Android移植的主要工作:
一、移植Linux驱动
二、移植HAL

Android和Linux内核版本说明:
Android移植在很大程度上是Linux内核的移植。Linux内核移植主要就是移植驱动程序。不同Linux版本的驱动程序不能通用。Android版本和Linux版本不同,无论哪个Android版本,其Linux内核版本都是Linux2.6或Linux3.0(或更高),只是小版本号不同。所以就算同一个Android版本,Linux内核也可能不同。移植Linux驱动时,主要应考虑Linux内核的版本,就算Android版本不同,只要Linux内核版本相同,Linux驱动就可以相互替换,有时需要考虑HAL是否与Linux驱动兼容。


1.3 查看Linux内核版本

Linux内核主要维护3个版本:Linux2.4、Linux2.6(使用最广泛)、Linux3.x。

可以在Android系统中的“设备”—->“关于手机”中查看当前Android系统采用的Linux内核版本。

查看Linux系统的内核版本,两种方法:
1、Linux终端执行
#uname -a
2、Linux终端执行
#cat /proc/version


注意:

/proc不是普通文件系统,是内核的映像,该目录中的文件是存放在系统内存中的。

1.5 如何学习Linux驱动开发

学习Linux驱动开发条件:

Linux操作系统,用来开发和测试Linux驱动:如ubuntu Linux10.04以上版本。

GNU C,是对标准C的扩展,是Linux/UNIX下最常用的C语言编译环境。

需要一些与驱动相关的硬件知识。

基于ARM11的开发板,如S3C6410

练习实践

1.6 Linux设备驱动

如果应用程序直接访问硬件,就会造成与硬件耦合度过高的情况。

驱动是直接和硬件交互的一类程序,负责对硬件进行抽象。

计算机系统的硬件主要由CPU、存储器和外设组成。驱动针对的对象是存储器和外设(包括CPU内部集成的存储器和外设),而不是针对CPU核。

Linux将存储器和外设分为3大类:

字符设备(Character devices)

块设备(Block devices)

网络设备(Network devices)

字符设备指那些必须以串行顺序依次进行访问的设备,如:触摸屏、磁带驱动器、鼠标、键盘等。不经过系统的快速缓冲。
块设备可以用任意顺序进行访问,以块为单位进行操作,如:硬盘、软驱等。经过系统的快速缓冲。
字符设备和块设备都是用文件系统(Linux通过文件系统访问驱动)的操作接口open、close、read、write等函数进行访问。


1.7 见识驱动:LED

任何的Linux驱动都有一个装载函数(装载驱动时调用,通过module_init()宏指定)和一个卸载函数(卸载驱动时调用,通过module_exit()宏指定)。


1.8 小结

Linux驱动只和Linux内核有关,与用户使用的Linux系统无关。只要使用了同样的Linux内核,驱动就可以通用。只有组成内核版本号的五部分完全相同,才能说明两个Linux系统内核相同。

学习Android驱动开发,实际上就是学习Linux驱动开发。Android增加了一个HAL,是Android特有的。HAL不是必需的,最好使用HAL程序库。

第2章 搭建Android开发环境

搭建Android底层开发环境:

Android应用程序开发环境

Android NDK开发环境

交叉编译环境

2.1 Android底层开发需要哪些工具

开发、测试和调试Linux驱动、HAL程序库需要的工具:

JDK6或以上版本

Eclipse3.4或以上版本

ADT(用于开发Android应用程序)

CDT(用于开发Android NDK程序)

Android SDK

Android NDK

交叉编译环境

Linux内核源代码

Android源代码

minicom:用于调试开发板的串口工具

2.2 安装JDK

JDK下载地址:

http://www.oracle.com/technetwork/java/javase/downloads/index.html

mkdir -p developer/jdk8/

tar -xvf …

vim /etc/profile

末行添加:

export PATH=.:/developer/jdk/jdk8/bin:$PATH

source /etc/profile

echo $PATH

2.3 搭建Android应用程序开发环境

2.3.1 安装Android SDK

下载SDK:

http://developer.android.com/sdk/index.html

8729e6f2f1fa58f04df9f8d1caac2f5be9dfc549

725bb360f0f7d04eaccff5a2d57abdd49061326d

3 Git使用入门

3.1 安装Git

ubuntu 10系统中安装Git:

#sudo apt-get install git

#sudo apt-get install git-doc git-svn git-email git-gui gitk

ubuntu 低版本系统中安装Git:

#sudo apt-get install git-core

#sudo apt-get install git-doc git-svn git-email git-gui gitk

Redhat、Fedora、CentO S系统中安装Git:

#yum install git

#yum install git-doc git-svn git-email git-gui gitk

如果Linux系统没有为root用户设置密码,用sudo passwd root命令设置root密码。


3.2 查看Git文档

Linux下使用man命令查看帮助,如:

#man git-checkout

安装git-doc后会安装git的文本格式和HTML格式的文档,所有文档都存在/usr/share/doc/git-doc/目录下。

以文本形式查看指定的文档,使用如下命令:

#git help

#git help git-checkout

#git help -w git-checkout 查看HTML格式的文档。

3.3 源代码的提交与获取

3.3.1 创建版本库:git init

Git的版本库分为本地版本库和远程版本库。

访问本地版本库不需要任何的权限,不需要联网。要修改Git源代码托管服务器中的源代码,必须使用git clone命令在本地建立一个与远程版本库一样的本地版本库,必须要联网。

建立开源项目步骤:

在本地创建一个空的版本库,建立一个开源项目的工作目录(/home/demo/helloworld-git/):

#mkdir -p /home/demo/helloworld-git

#cd /home/demo/helloworld-git

#git init

#ls -al 可以看到.git文件夹

空的版本库创建成功, /home/demo/helloworld-git/目录下有一个隐藏的.git目录,即本地版本库。.git目录下有一些子目录和文件。

3.3.2 将文件提交到本地版本库(.git/目录)

在/home/demo/helloworld-git/目录下创建helloworld.c文件:

#cd /home/demo/helloworld-git

#echo “Hello world!” > helloworld.c

将helloworld.c文件添加到本地版本库的索引中,并提交到版本库:

#git add helloworld.c

#git commit -m ‘helloworld-master’

其中,-m命令行参数是本次提交的备注,必修指定该信息。

#git log 显示日志信息

如果提交到本地版本库中的文件被误删或者误改,可以恢复到最近一次提交的状态:

#git checkout helloworld.c

3.3.3 创建本地分支:git branch

在创建本地分支之前可以先了解下当前版本库包含哪些本地分支:

#git branch

#git branch -a

建立一个新的分支:

#git branch new-branch

查看分支时,分支前的星号(*)表示当前工作目录在哪个分支下。

将修改后的文件提交到工作目录当前所在的本地分支:
#git commit
删除刚建立的分支(在分支中所做的一切修改都被删除):
#git branch -D new-branch


3.3.4 切换本地分支:git checkout

将当前本地分支切换到new-branch上:

#git checkout new-branch

将修改的helloworld.c文件提交到new-branch分支:

#echo ‘New world!’ > helloworld.c

#git add helloworld.c

#git commit -m helloworld-new-branch

3.3.5 在GitHub上创建开源项目

使用GitHub来托管创建的文件helloworld.c:

先在https://github.com/signup/free页面注册一个免费用户,“Create an account”按钮会成功创建一个用户。

进入GitHub首页,单击页面右下角的“New repository”按钮创建一个新的项目。项目名必须在GitHub上没有使用过。

3.3.6 上传源代码到GitHub:git push

GitHub上传代码时需要SSH校验,需要在~/.ssh/目录下生成两个纯文本文件,一个密钥文件(id_rsa)和一个公钥文件(id_rsa.pub):

#ssh-keygen -t rsa -C “chiyuan.ma@myemail.com”

将~/.ssh/id_rsa.pub文件中的所有内容复制到剪切板,在GitHub的账户设置页面左侧选择“SSH Public Keys”项,单击“Add another public key”链接,将剪贴板的内容粘贴到Key输入框中。在Title输入框中输入任意标题,单击“Add key”按钮添加一个public key。

设置完public key后,检测公钥、密钥以及设置是否正确:
#ssh -T git@github.com        不要修改git@github.com
有一些Linux系统(如Ubuntu Linux),就算成功完成上面的操作也无法通过测试,需要向代理身份验证添加RSA身份:
#ssh-add

上传文件之前需要设置上传者的名字和email:
#git config --global user.name "Chiyuan.Ma"
#git config --global user.email "chiyuan.ma@myemail.com"

上传工作目录中的文件:
设置helloworld工程在GitHub上的URI:
#git remote add origin git@github.com:androidguy/helloworld.git
origin是远程代码库名,可以任意起名。
将本地版本库中的文件上传到GitHub:
#git push -u origin master
查看所有的分支(本地分支和远程分支):
#git branch -a


3.3.7 从GitHub下载源代码:git clone

下载整个工程:

#git clone git@github.com:androidguy/helloworld.git

下载好的helloworld目录中也包含一个.git本地版本库目录。

只获取某一分支的最新内容:

#git pull origin master

第二篇 Android底层开发入门

第6章 第一个Linux驱动程序:统计单词个数

6.1 Linux驱动到底是什么

Linux系统将每一个驱动都映射成一个文件。这些文件称为设备文件或驱动文件,都保存在/dev/目录。Linux驱动的事件,也就是回调(callback)函数。编写Linux驱动最重要的一步就是编写回调函数,否则与设备文件交互的数据将无法得到处理。
应用软件、设备文件、驱动程序、硬件之间的关系:


6.2 编写Linux驱动程序的步骤

第1步:建立Linux驱动骨架(装载和卸载Linux驱动)
Linux内核在使用驱动时首先需要装载驱动,装载过程中需要进行一些初始化工作,如:建立设备文件、分配内存地址空间等。当Linux系统退出时需要卸载Linux驱动,卸载过程中需要释放由Linux驱动占用的资源,如:删除设备、释放内存地址空间等。Linux驱动程序用module_init()和module_exit()宏分别处理函数的装载和卸载。即包含module_init()和module_exit()宏的C程序文件作为Linux驱动的骨架。

第2步:注册和注销设备文件
Linux驱动需要有一个设备文件,建立设备文件在处理Linux初始化工作的函数中完成,用misc_register()函数创建设备文件。删除设备文件在处理Linuz退出工作的函数中完成,用misc_deregister()函数移除设备文件。

第3步:指定与驱动相关的信息
驱动程序是自描述的,可以通过modinfo命令获取驱动程序的信息。这些信息通过MODULE_AUTHOR、MODULE_LICENSE、MODULE_ALIAS、MODULE_DESCRIPTION等宏指定驱动相关信息。

第4步:指定回调函数
Linux驱动有一个操作集,包含read、write等回调函数,一个驱动程序不一定要指定操作集中的所有回调函数。

第5步:编写业务逻辑
Linux驱动的核心部分。业务逻辑与驱动的功能相关,可能由多个函数、多个文件、多个Linux驱动模块组成。

第6步:编写Makefile文件
定义Linux驱动代码的编译规则。

第7步:编译Linux驱动程序
Linux驱动程序可以直接编译进内核(obj-y:),也可以作为模块单独编译(obj-m:)成*.ko文件。obj-y和obj-m都需要使用“:=”赋值。

第8步:安装和卸载Linux驱动
如果将Linux驱动编译进内核,驱动程序会自动装载。如果Linux驱动程序以模块单独存在,需要使用insmod或modprobe命令装载,使用rmmod或modprobe -d命令卸载。

如果Linux驱动编译进内核,目标文件*.o会被连接进build-in.o文件,该文件是连接同一类程序的.o文件生成的中间目标文件,可以在/kernel/drivers/char/目录找到,该目标文件包含了所有可连接进Linux内核的字符驱动。
如果Linux驱动依赖其他程序,需要修改Makefile文件:
obj-m := word_count.o
word_count-y := process.o data.o
其中依赖文件使用module-y或module-objs指定,module表示模块名。

Linux系统将内存分为了用户空间和内核空间,两个空间的程序不能直接访问。printf()函数运行在用户空间,printk()函数运行在内核空间。Linux驱动程序不能直接访问printf()函数。
如果用户空间的程序要访问内核空间,需要做一个可以访问内核空间的驱动程序,然后用户空间的程序通过设备文件可以与驱动程序进行交互。
/kernel/include/目录的各个子目录中包含了大量的C语言头文件,这些头文件中定义的函数、宏等就是运行在用户空间的程序的替代函数。
用户空间            内核空间
malloc()                kmalloc()
atoi()                    simple_strtol()
itoa()                    snprintf()

编译驱动程序:
#make -C /kernel/ M=/driver_code
由于编译Linux驱动需要使用Linux内核的头文件,在Ubuntu Linux上编译驱动程序,需要使用-C命令行参数指定Linux内核头文件目录(*/kernel/),如果以模块方式编译Linux驱动程序,需要使用M指定驱动程序所在的目录。

安装Linux驱动:
#insmod **.ko
查看**是否成功安装:
#lsmod | grep **
卸载Linux驱动:
#rmmod **
查看由Linux驱动输出的日志信息:
#dmesg | grep ** | tail -n 2
dmesg命令实际是从/var/log/messages(低版本ubuntu)或/var/log/syslog(高版本ubuntu)文件中读取的日志信息,


6.3.3 指定与驱动相关的信息

模块作者:MODULE_AUTHOR宏指定

模块描述:MODULE_DESCRIPTION宏指定

模块别名:MODULE_ALIAS宏指定

开源协议:MODULE_LICENSE宏指定,如:GPL协议

查看**.ko的信息:
#modinfo **.ko
vermagic表示当前Lunux驱动模块在哪个Linux内核版本下编译。


6.3.4 注册和注销设备文件

设备文件一般在初始化Linux驱动时用misc_register()函数建立,在卸载Linux驱动时用misc_deregister()函数删除,创建好的设备文件位于/dev/目录下。
extern int misc_register(struct miscdevice *misc);
extern int misc_deregister(struct miscdevice *misc);
设备文件还需要一个结构体(miscdevice)来描述与其相关的信息,其中一个重要成员变量file_operations fops,用于描述设备文件可触发事件的函数指针。


需要注意:

设备文件由主设备号和次设备号描述。而使用misc_register函数只能设置次设备号,主设备号统一设为10,主设备号为10的设备是Linux系统中拥有共同特性的简单子辐设备,称为misc设备。次设备号需要指定,或者动态生成(需要指定MISC_DYNAMIC_MINOR常量)。

使用register_chrdev_region和alloc_chrdev_region函数同时指定主设备号和次设备号的方式注册和注销设备文件。

miscdevice.name 设备文件的名称,设备注册成功后,可以在/dev/目录下查到。

file_operations.owner 如果为module结构体,表示file_operations可以被应用在由module指定的驱动模块中;如果为THIS_MODULE,表示file_operations只应用于当前驱动模块。

设备文件注册成功,misc_register()函数返回非0整数,失败返回0.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  学习笔记